Fix API and admin for location2025

This commit is contained in:
2025-08-30 04:12:55 +09:00
parent 596b7313dd
commit cb399f14bf
3 changed files with 139 additions and 14 deletions

View File

@ -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'

View File

@ -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に基づくインタラクションタイプを返す"""

View File

@ -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