Fix API and admin for location2025
This commit is contained in:
35
rog/admin.py
35
rog/admin.py
@ -1039,16 +1039,21 @@ class WaypointAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(Location2025)
|
@admin.register(Location2025)
|
||||||
class Location2025Admin(LeafletGeoAdmin):
|
class Location2025Admin(LeafletGeoAdmin):
|
||||||
"""Location2025の管理画面"""
|
"""Location2025の管理画面(全フィールド対応)"""
|
||||||
list_display = [
|
list_display = [
|
||||||
'cp_number', 'cp_name', 'event', 'total_point', 'is_active',
|
'cp_number', 'cp_name', 'event', 'sub_loc_id', 'subcategory',
|
||||||
|
'total_point', 'has_photos', 'has_videos', 'is_active',
|
||||||
'csv_upload_date', 'created_at'
|
'csv_upload_date', 'created_at'
|
||||||
]
|
]
|
||||||
list_filter = [
|
list_filter = [
|
||||||
'event', 'is_active', 'shop_closed', 'shop_shutdown',
|
'event', 'is_active', 'shop_closed', 'shop_shutdown',
|
||||||
|
'subcategory', 'hidden_location',
|
||||||
'csv_upload_date', 'created_at'
|
'csv_upload_date', 'created_at'
|
||||||
]
|
]
|
||||||
search_fields = ['cp_name', 'address', 'description']
|
search_fields = [
|
||||||
|
'cp_name', 'address', 'description', 'remark', 'tags',
|
||||||
|
'sub_loc_id', 'subcategory', 'evaluation_value'
|
||||||
|
]
|
||||||
readonly_fields = [
|
readonly_fields = [
|
||||||
'csv_source_file', 'csv_upload_date', 'csv_upload_user',
|
'csv_source_file', 'csv_upload_date', 'csv_upload_user',
|
||||||
'created_at', 'updated_at', 'created_by', 'updated_by'
|
'created_at', 'updated_at', 'created_by', 'updated_by'
|
||||||
@ -1056,7 +1061,7 @@ class Location2025Admin(LeafletGeoAdmin):
|
|||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('基本情報', {
|
('基本情報', {
|
||||||
'fields': ('cp_number', 'event', 'cp_name', 'is_active', 'sort_order')
|
'fields': ('cp_number', 'event', 'cp_name', 'sub_loc_id', 'subcategory', 'is_active', 'sort_order')
|
||||||
}),
|
}),
|
||||||
('位置情報', {
|
('位置情報', {
|
||||||
'fields': ('latitude', 'longitude', 'location', 'address')
|
'fields': ('latitude', 'longitude', 'location', 'address')
|
||||||
@ -1071,7 +1076,15 @@ class Location2025Admin(LeafletGeoAdmin):
|
|||||||
'fields': ('shop_closed', 'shop_shutdown', 'opening_hours')
|
'fields': ('shop_closed', 'shop_shutdown', 'opening_hours')
|
||||||
}),
|
}),
|
||||||
('詳細情報', {
|
('詳細情報', {
|
||||||
'fields': ('phone', 'website', 'description')
|
'fields': ('phone', 'website', 'description', 'remark')
|
||||||
|
}),
|
||||||
|
('メディア・タグ情報', {
|
||||||
|
'fields': ('photos', 'videos', 'tags', 'evaluation_value'),
|
||||||
|
'classes': ('wide',)
|
||||||
|
}),
|
||||||
|
('高度設定', {
|
||||||
|
'fields': ('hidden_location',),
|
||||||
|
'classes': ('collapse',)
|
||||||
}),
|
}),
|
||||||
('CSV情報', {
|
('CSV情報', {
|
||||||
'fields': ('csv_source_file', 'csv_upload_date', 'csv_upload_user'),
|
'fields': ('csv_source_file', 'csv_upload_date', 'csv_upload_user'),
|
||||||
@ -1083,6 +1096,18 @@ class Location2025Admin(LeafletGeoAdmin):
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def has_photos(self, obj):
|
||||||
|
"""写真データ有無の表示"""
|
||||||
|
return bool(obj.photos and obj.photos.strip())
|
||||||
|
has_photos.boolean = True
|
||||||
|
has_photos.short_description = '写真'
|
||||||
|
|
||||||
|
def has_videos(self, obj):
|
||||||
|
"""動画データ有無の表示"""
|
||||||
|
return bool(obj.videos and obj.videos.strip())
|
||||||
|
has_videos.boolean = True
|
||||||
|
has_videos.short_description = '動画'
|
||||||
|
|
||||||
# CSV一括アップロード機能
|
# CSV一括アップロード機能
|
||||||
change_list_template = 'admin/location2025/change_list.html'
|
change_list_template = 'admin/location2025/change_list.html'
|
||||||
|
|
||||||
|
|||||||
@ -36,17 +36,85 @@ class LocationCatSerializer(serializers.ModelSerializer):
|
|||||||
fields=['category',]
|
fields=['category',]
|
||||||
|
|
||||||
|
|
||||||
class LocationSerializer(GeoFeatureModelSerializer):
|
class LocationSerializer(serializers.ModelSerializer):
|
||||||
# evaluation_valueに基づくインタラクション情報を追加
|
# evaluation_valueに基づくインタラクション情報を追加
|
||||||
interaction_type = serializers.SerializerMethodField()
|
interaction_type = serializers.SerializerMethodField()
|
||||||
requires_photo = serializers.SerializerMethodField()
|
requires_photo = serializers.SerializerMethodField()
|
||||||
requires_qr_code = serializers.SerializerMethodField()
|
requires_qr_code = serializers.SerializerMethodField()
|
||||||
interaction_instructions = serializers.SerializerMethodField()
|
interaction_instructions = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
# 追加フィールドのカスタムシリアライズ
|
||||||
|
has_photos = serializers.SerializerMethodField()
|
||||||
|
has_videos = serializers.SerializerMethodField()
|
||||||
|
photos_list = serializers.SerializerMethodField()
|
||||||
|
videos_list = serializers.SerializerMethodField()
|
||||||
|
tags_list = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
# 位置情報の緯度経度
|
||||||
|
latitude_float = serializers.SerializerMethodField()
|
||||||
|
longitude_float = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model=Location2025
|
model=Location2025
|
||||||
geo_field='geom'
|
fields=[
|
||||||
fields="__all__"
|
# 基本フィールド
|
||||||
|
'id', 'cp_number', 'event', 'cp_name', 'sub_loc_id', 'subcategory',
|
||||||
|
'latitude', 'longitude', 'location', 'address',
|
||||||
|
'cp_point', 'photo_point', 'buy_point',
|
||||||
|
'checkin_radius', 'auto_checkin',
|
||||||
|
'shop_closed', 'shop_shutdown', 'opening_hours',
|
||||||
|
'phone', 'website', 'description', 'remark',
|
||||||
|
# 追加フィールド
|
||||||
|
'photos', 'videos', 'tags', 'evaluation_value', 'hidden_location',
|
||||||
|
# 管理フィールド
|
||||||
|
'is_active', 'sort_order', 'csv_source_file', 'csv_upload_date',
|
||||||
|
'created_at', 'updated_at', 'created_by', 'updated_by',
|
||||||
|
# カスタムフィールド
|
||||||
|
'interaction_type', 'requires_photo', 'requires_qr_code', 'interaction_instructions',
|
||||||
|
'has_photos', 'has_videos', 'photos_list', 'videos_list', 'tags_list',
|
||||||
|
'latitude_float', 'longitude_float'
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_latitude_float(self, obj):
|
||||||
|
"""位置情報から緯度を取得"""
|
||||||
|
if obj.location:
|
||||||
|
return obj.location.y
|
||||||
|
return obj.latitude
|
||||||
|
|
||||||
|
def get_longitude_float(self, obj):
|
||||||
|
"""位置情報から経度を取得"""
|
||||||
|
if obj.location:
|
||||||
|
return obj.location.x
|
||||||
|
return obj.longitude
|
||||||
|
|
||||||
|
def get_has_photos(self, obj):
|
||||||
|
"""写真データの有無を返す"""
|
||||||
|
return bool(obj.photos and obj.photos.strip())
|
||||||
|
|
||||||
|
def get_has_videos(self, obj):
|
||||||
|
"""動画データの有無を返す"""
|
||||||
|
return bool(obj.videos and obj.videos.strip())
|
||||||
|
|
||||||
|
def get_photos_list(self, obj):
|
||||||
|
"""写真ファイル名をリストで返す"""
|
||||||
|
if not obj.photos or not obj.photos.strip():
|
||||||
|
return []
|
||||||
|
# カンマ区切りで分割してリストとして返す
|
||||||
|
return [photo.strip() for photo in obj.photos.split(',') if photo.strip()]
|
||||||
|
|
||||||
|
def get_videos_list(self, obj):
|
||||||
|
"""動画ファイル名をリストで返す"""
|
||||||
|
if not obj.videos or not obj.videos.strip():
|
||||||
|
return []
|
||||||
|
# カンマ区切りで分割してリストとして返す
|
||||||
|
return [video.strip() for video in obj.videos.split(',') if video.strip()]
|
||||||
|
|
||||||
|
def get_tags_list(self, obj):
|
||||||
|
"""タグをリストで返す"""
|
||||||
|
if not obj.tags or not obj.tags.strip():
|
||||||
|
return []
|
||||||
|
# カンマ区切りで分割してリストとして返す
|
||||||
|
return [tag.strip() for tag in obj.tags.split(',') if tag.strip()]
|
||||||
|
|
||||||
def get_interaction_type(self, obj):
|
def get_interaction_type(self, obj):
|
||||||
"""evaluation_valueに基づくインタラクションタイプを返す"""
|
"""evaluation_valueに基づくインタラクションタイプを返す"""
|
||||||
|
|||||||
44
rog/views.py
44
rog/views.py
@ -24,7 +24,7 @@ import uuid
|
|||||||
from rest_framework.exceptions import ValidationError as DRFValidationError
|
from rest_framework.exceptions import ValidationError as DRFValidationError
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import F,Sum
|
from django.db.models import F,Sum,Q
|
||||||
from rest_framework import viewsets, permissions, status
|
from rest_framework import viewsets, permissions, status
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
@ -239,17 +239,49 @@ class NewEvent2Admin(admin.ModelAdmin):
|
|||||||
class LocationViewSet(viewsets.ModelViewSet):
|
class LocationViewSet(viewsets.ModelViewSet):
|
||||||
queryset=Location2025.objects.all()
|
queryset=Location2025.objects.all()
|
||||||
serializer_class=LocationSerializer
|
serializer_class=LocationSerializer
|
||||||
filter_fields=["prefecture", "location_name"]
|
filter_fields=[
|
||||||
|
"event", "cp_name", "subcategory", "sub_loc_id",
|
||||||
|
"is_active", "hidden_location", "evaluation_value"
|
||||||
|
]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = Location2025.objects.all()
|
queryset = Location2025.objects.all()
|
||||||
|
|
||||||
# リクエストパラメータの確認
|
# リクエストパラメータの確認
|
||||||
group_filter = self.request.query_params.get('group__contains')
|
event_filter = self.request.query_params.get('event')
|
||||||
|
subcategory_filter = self.request.query_params.get('subcategory')
|
||||||
|
has_photos_filter = self.request.query_params.get('has_photos')
|
||||||
|
has_videos_filter = self.request.query_params.get('has_videos')
|
||||||
|
tags_filter = self.request.query_params.get('tags__contains')
|
||||||
|
evaluation_filter = self.request.query_params.get('evaluation_value')
|
||||||
|
hidden_filter = self.request.query_params.get('hidden_location')
|
||||||
|
|
||||||
if group_filter:
|
if event_filter:
|
||||||
# フィルタの適用
|
queryset = queryset.filter(event_id=event_filter)
|
||||||
queryset = queryset.filter(group__contains=group_filter)
|
|
||||||
|
if subcategory_filter:
|
||||||
|
queryset = queryset.filter(subcategory__icontains=subcategory_filter)
|
||||||
|
|
||||||
|
if has_photos_filter == 'true':
|
||||||
|
queryset = queryset.exclude(photos__isnull=True).exclude(photos='')
|
||||||
|
elif has_photos_filter == 'false':
|
||||||
|
queryset = queryset.filter(Q(photos__isnull=True) | Q(photos=''))
|
||||||
|
|
||||||
|
if has_videos_filter == 'true':
|
||||||
|
queryset = queryset.exclude(videos__isnull=True).exclude(videos='')
|
||||||
|
elif has_videos_filter == 'false':
|
||||||
|
queryset = queryset.filter(Q(videos__isnull=True) | Q(videos=''))
|
||||||
|
|
||||||
|
if tags_filter:
|
||||||
|
queryset = queryset.filter(tags__icontains=tags_filter)
|
||||||
|
|
||||||
|
if evaluation_filter:
|
||||||
|
queryset = queryset.filter(evaluation_value=evaluation_filter)
|
||||||
|
|
||||||
|
if hidden_filter == 'true':
|
||||||
|
queryset = queryset.filter(hidden_location=True)
|
||||||
|
elif hidden_filter == 'false':
|
||||||
|
queryset = queryset.filter(hidden_location=False)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user