diff --git a/rog/admin.py b/rog/admin.py index 4cb836c..8641492 100755 --- a/rog/admin.py +++ b/rog/admin.py @@ -1039,16 +1039,21 @@ class WaypointAdmin(admin.ModelAdmin): @admin.register(Location2025) class Location2025Admin(LeafletGeoAdmin): - """Location2025の管理画面""" + """Location2025の管理画面(全フィールド対応)""" 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' ] list_filter = [ 'event', 'is_active', 'shop_closed', 'shop_shutdown', + 'subcategory', 'hidden_location', '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 = [ 'csv_source_file', 'csv_upload_date', 'csv_upload_user', 'created_at', 'updated_at', 'created_by', 'updated_by' @@ -1056,7 +1061,7 @@ class Location2025Admin(LeafletGeoAdmin): 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') @@ -1071,7 +1076,15 @@ class Location2025Admin(LeafletGeoAdmin): '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情報', { '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一括アップロード機能 change_list_template = 'admin/location2025/change_list.html' diff --git a/rog/serializers.py b/rog/serializers.py index 1ac5e4a..a386b07 100755 --- a/rog/serializers.py +++ b/rog/serializers.py @@ -36,17 +36,85 @@ class LocationCatSerializer(serializers.ModelSerializer): fields=['category',] -class LocationSerializer(GeoFeatureModelSerializer): +class LocationSerializer(serializers.ModelSerializer): # evaluation_valueに基づくインタラクション情報を追加 interaction_type = serializers.SerializerMethodField() requires_photo = serializers.SerializerMethodField() requires_qr_code = 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: model=Location2025 - geo_field='geom' - fields="__all__" + fields=[ + # 基本フィールド + '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): """evaluation_valueに基づくインタラクションタイプを返す""" diff --git a/rog/views.py b/rog/views.py index a86e19b..679a631 100755 --- a/rog/views.py +++ b/rog/views.py @@ -24,7 +24,7 @@ import uuid from rest_framework.exceptions import ValidationError as DRFValidationError 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.decorators import action from rest_framework.response import Response @@ -239,17 +239,49 @@ class NewEvent2Admin(admin.ModelAdmin): class LocationViewSet(viewsets.ModelViewSet): queryset=Location2025.objects.all() 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): 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: - # フィルタの適用 - queryset = queryset.filter(group__contains=group_filter) + if event_filter: + queryset = queryset.filter(event_id=event_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