Files
ShooterHub/apps/photos/serializers.py
2026-04-02 11:24:30 +02:00

94 lines
3.0 KiB
Python

from rest_framework import serializers
# Cross-app import (photos → tools). String FK in the model avoids DB circular
# import; here we need the class directly for the serializer queryset.
from apps.tools.models import ShotGroup
from .models import GroupPhoto, GroupPhotoAnalysis, Photo, PointOfImpact
class PhotoMetaSerializer(serializers.ModelSerializer):
"""Photo metadata only — the binary `data` field is never exposed in JSON."""
class Meta:
model = Photo
fields = ['id', 'content_type', 'size', 'width', 'height',
'uploaded_by', 'uploaded_at', 'description']
read_only_fields = ['id', 'size', 'width', 'height', 'uploaded_by', 'uploaded_at']
class GroupPhotoAnalysisSerializer(serializers.ModelSerializer):
class Meta:
model = GroupPhotoAnalysis
fields = [
'group_size_mm', 'group_size_moa',
'elevation_offset_mm', 'elevation_offset_moa',
'windage_offset_mm', 'windage_offset_moa',
'mean_radius_mm', 'mean_radius_moa',
'notes',
]
def validate(self, attrs):
instance = GroupPhotoAnalysis(**attrs)
instance.clean()
return attrs
class PointOfImpactSerializer(serializers.ModelSerializer):
# Write: accept Shot PK; Read: compact inline summary
shot_detail = serializers.SerializerMethodField()
class Meta:
model = PointOfImpact
fields = [
'id', 'order',
'shot', 'shot_detail',
'x_px', 'y_px',
'x_mm', 'y_mm', 'radius_mm',
'notes',
]
def get_shot_detail(self, obj):
if not obj.shot_id:
return None
return {
'id': obj.shot.pk,
'shot_number': obj.shot.shot_number,
'velocity_fps': str(obj.shot.velocity_fps),
}
class GroupPhotoSerializer(serializers.ModelSerializer):
photo = PhotoMetaSerializer(read_only=True)
photo_id = serializers.PrimaryKeyRelatedField(
source='photo',
queryset=Photo.objects.all(),
write_only=True,
)
# shot_group is optional — photos can exist independently of any group
shot_group = serializers.PrimaryKeyRelatedField(
queryset=ShotGroup.objects.all(),
required=False, allow_null=True,
)
shot_group_detail = serializers.SerializerMethodField()
analysis = GroupPhotoAnalysisSerializer(read_only=True)
points_of_impact = PointOfImpactSerializer(many=True, read_only=True)
class Meta:
model = GroupPhoto
fields = [
'id', 'photo_id', 'photo',
'shot_group', 'shot_group_detail', 'caption', 'order',
'is_public', 'analysis', 'points_of_impact',
]
def get_shot_group_detail(self, obj):
if not obj.shot_group_id:
return None
sg = obj.shot_group
return {
'id': sg.pk,
'label': sg.label,
'distance_m': str(sg.distance_m) if sg.distance_m else None,
}