lty/qy_lty/card/serializers.py
2026-03-17 13:17:02 +08:00

437 lines
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from rest_framework import serializers
from .models import (
CardTemplate, Card, CardBatch, CardUsageLog,
ClothingAttributes, PropAttributes, SongAttributes,
DanceAttributes, FurnitureAttributes, DecorationAttributes
)
class ClothingAttributesSerializer(serializers.ModelSerializer):
class Meta:
model = ClothingAttributes
fields = [
'style', 'size', 'color', 'season',
'material', 'fit_type', 'care_instructions'
]
class PropAttributesSerializer(serializers.ModelSerializer):
class Meta:
model = PropAttributes
fields = [
'prop_type', 'material', 'size', 'weight',
'durability', 'usage_instructions'
]
class SongAttributesSerializer(serializers.ModelSerializer):
class Meta:
model = SongAttributes
fields = [
'genre', 'duration', 'bpm', 'composer',
'lyricist', 'arrangement', 'audio_file', 'lyrics'
]
class DanceAttributesSerializer(serializers.ModelSerializer):
class Meta:
model = DanceAttributes
fields = [
'style', 'difficulty', 'duration', 'choreographer',
'required_space', 'calories_burn', 'tutorial_video'
]
class FurnitureAttributesSerializer(serializers.ModelSerializer):
class Meta:
model = FurnitureAttributes
fields = [
'furniture_type', 'style', 'material', 'dimensions',
'weight', 'assembly_required', 'max_weight_capacity', 'care_instructions'
]
class DecorationAttributesSerializer(serializers.ModelSerializer):
class Meta:
model = DecorationAttributes
fields = [
'decoration_type', 'style', 'material', 'size',
'placement', 'indoor_outdoor', 'installation_required', 'care_instructions'
]
class CardTemplateSerializer(serializers.ModelSerializer):
category_display = serializers.SerializerMethodField()
rarity_display = serializers.SerializerMethodField()
card_type_display = serializers.SerializerMethodField()
status_display = serializers.SerializerMethodField()
image_url = serializers.SerializerMethodField()
# 新增所有类别专有属性的write_only字段
clothing_attributes = ClothingAttributesSerializer(write_only=True, required=False)
prop_attributes = PropAttributesSerializer(write_only=True, required=False)
song_attributes = SongAttributesSerializer(write_only=True, required=False)
dance_attributes = DanceAttributesSerializer(write_only=True, required=False)
furniture_attributes = FurnitureAttributesSerializer(write_only=True, required=False)
decoration_attributes = DecorationAttributesSerializer(write_only=True, required=False)
class Meta:
model = CardTemplate
fields = [
'id', 'name', 'category', 'category_display', 'description',
'card_type', 'card_type_display', 'rarity', 'rarity_display',
'image', 'image_url', 'model_url', 'model_version',
'status', 'status_display', 'published_at', 'price',
'created_at', 'updated_at',
# 新增
'clothing_attributes', 'prop_attributes', 'song_attributes', 'dance_attributes', 'furniture_attributes', 'decoration_attributes',
]
read_only_fields = ['created_at', 'updated_at', 'published_at', 'image_url']
def create(self, validated_data):
# 把所有 *_attributes 字段pop掉只留主表字段
validated_data.pop('clothing_attributes', None)
validated_data.pop('prop_attributes', None)
validated_data.pop('song_attributes', None)
validated_data.pop('dance_attributes', None)
validated_data.pop('furniture_attributes', None)
validated_data.pop('decoration_attributes', None)
return super().create(validated_data)
def get_category_display(self, obj):
return dict(CardTemplate.CATEGORY_CHOICES).get(obj.category)
def get_rarity_display(self, obj):
return obj.get_rarity_display()
def get_card_type_display(self, obj):
return obj.get_card_type_display()
def get_status_display(self, obj):
return obj.get_status_display()
def get_image_url(self, obj):
if obj.image:
return obj.image.url
return None
def to_representation(self, instance):
representation = super().to_representation(instance)
# 添加类别专有属性
try:
if instance.category == 'clothing' and hasattr(instance, 'clothing_attrs'):
representation['attributes'] = ClothingAttributesSerializer(instance.clothing_attrs).data
elif instance.category == 'prop' and hasattr(instance, 'prop_attrs'):
representation['attributes'] = PropAttributesSerializer(instance.prop_attrs).data
elif instance.category == 'song' and hasattr(instance, 'song_attrs'):
representation['attributes'] = SongAttributesSerializer(instance.song_attrs).data
elif instance.category == 'dance' and hasattr(instance, 'dance_attrs'):
representation['attributes'] = DanceAttributesSerializer(instance.dance_attrs).data
elif instance.category == 'furniture' and hasattr(instance, 'furniture_attrs'):
representation['attributes'] = FurnitureAttributesSerializer(instance.furniture_attrs).data
elif instance.category == 'decoration' and hasattr(instance, 'decoration_attrs'):
representation['attributes'] = DecorationAttributesSerializer(instance.decoration_attrs).data
except Exception:
representation['attributes'] = None
return representation
def update(self, instance, validated_data):
# 取出专属属性数据
clothing_data = validated_data.pop('clothing_attributes', None)
prop_data = validated_data.pop('prop_attributes', None)
song_data = validated_data.pop('song_attributes', None)
dance_data = validated_data.pop('dance_attributes', None)
furniture_data = validated_data.pop('furniture_attributes', None)
decoration_data = validated_data.pop('decoration_attributes', None)
# 更新主表
instance = super().update(instance, validated_data)
# 更新专属属性
if clothing_data is not None and hasattr(instance, 'clothing_attrs'):
for k, v in clothing_data.items():
setattr(instance.clothing_attrs, k, v)
instance.clothing_attrs.save()
if prop_data is not None and hasattr(instance, 'prop_attrs'):
for k, v in prop_data.items():
setattr(instance.prop_attrs, k, v)
instance.prop_attrs.save()
if song_data is not None and hasattr(instance, 'song_attrs'):
for k, v in song_data.items():
setattr(instance.song_attrs, k, v)
instance.song_attrs.save()
if dance_data is not None and hasattr(instance, 'dance_attrs'):
for k, v in dance_data.items():
setattr(instance.dance_attrs, k, v)
instance.dance_attrs.save()
if furniture_data is not None and hasattr(instance, 'furniture_attrs'):
for k, v in furniture_data.items():
setattr(instance.furniture_attrs, k, v)
instance.furniture_attrs.save()
if decoration_data is not None and hasattr(instance, 'decoration_attrs'):
for k, v in decoration_data.items():
setattr(instance.decoration_attrs, k, v)
instance.decoration_attrs.save()
return instance
class CardTemplateDetailSerializer(CardTemplateSerializer):
"""Detailed template serializer with related batches and cards count"""
batches_count = serializers.SerializerMethodField()
active_cards_count = serializers.SerializerMethodField()
class Meta(CardTemplateSerializer.Meta):
fields = CardTemplateSerializer.Meta.fields + ['batches_count', 'active_cards_count']
def get_batches_count(self, obj):
return obj.batches.count()
def get_active_cards_count(self, obj):
# 只计算激活状态的卡片数量
return obj.cards.filter(status='active').count()
def to_representation(self, instance):
representation = super().to_representation(instance)
# 添加类别专有属性
try:
if instance.category == 'clothing' and hasattr(instance, 'clothing_attrs'):
representation['attributes'] = ClothingAttributesSerializer(instance.clothing_attrs).data
elif instance.category == 'prop' and hasattr(instance, 'prop_attrs'):
representation['attributes'] = PropAttributesSerializer(instance.prop_attrs).data
elif instance.category == 'song' and hasattr(instance, 'song_attrs'):
representation['attributes'] = SongAttributesSerializer(instance.song_attrs).data
elif instance.category == 'dance' and hasattr(instance, 'dance_attrs'):
representation['attributes'] = DanceAttributesSerializer(instance.dance_attrs).data
elif instance.category == 'furniture' and hasattr(instance, 'furniture_attrs'):
representation['attributes'] = FurnitureAttributesSerializer(instance.furniture_attrs).data
elif instance.category == 'decoration' and hasattr(instance, 'decoration_attrs'):
representation['attributes'] = DecorationAttributesSerializer(instance.decoration_attrs).data
except Exception as e:
# 处理相关属性不存在的情况
representation['attributes'] = None
return representation
class CardSerializer(serializers.ModelSerializer):
category_display = serializers.SerializerMethodField()
template_name = serializers.SerializerMethodField()
status_display = serializers.SerializerMethodField()
image_url = serializers.SerializerMethodField()
class Meta:
model = Card
fields = [
'id', 'unique_id', 'template', 'template_name', 'name',
'category', 'category_display', 'description', 'batch',
'image', 'image_url', 'price', 'status', 'status_display',
'user', 'used_at', 'manufactured', 'manufactured_at',
'created_at', 'updated_at'
]
read_only_fields = [
'created_at', 'updated_at', 'used_at',
'manufactured_at', 'image_url'
]
def get_category_display(self, obj):
return dict(CardTemplate.CATEGORY_CHOICES).get(obj.category)
def get_template_name(self, obj):
return obj.template.name if obj.template else None
def get_status_display(self, obj):
return obj.get_status_display()
def get_image_url(self, obj):
if obj.image:
return obj.image.url
# 如果卡片没有图片,使用模板的图片
elif obj.template and obj.template.image:
return obj.template.image.url
return None
class CardDetailSerializer(CardSerializer):
"""Detailed card serializer with usage logs"""
usage_logs = serializers.SerializerMethodField()
template_details = serializers.SerializerMethodField()
class Meta(CardSerializer.Meta):
fields = CardSerializer.Meta.fields + ['usage_logs', 'template_details']
def get_usage_logs(self, obj):
# Only return the 10 most recent logs
logs = obj.usage_logs.all().order_by('-created_at')[:10]
return CardUsageLogSerializer(logs, many=True).data
def get_template_details(self, obj):
if obj.template:
return {
'rarity': obj.template.rarity,
'rarity_display': obj.template.get_rarity_display(),
'card_type': obj.template.card_type,
'card_type_display': obj.template.get_card_type_display(),
'model_url': obj.template.model_url,
'model_version': obj.template.model_version,
}
return None
class CardBatchSerializer(serializers.ModelSerializer):
category_display = serializers.SerializerMethodField()
template_name = serializers.SerializerMethodField()
status_display = serializers.SerializerMethodField()
excel_file_url = serializers.SerializerMethodField()
class Meta:
model = CardBatch
fields = [
'id', 'batch_number', 'template', 'template_name',
'category', 'category_display', 'quantity', 'description',
'status', 'status_display', 'exported', 'exported_at',
'excel_file', 'excel_file_url', 'sent_to_production',
'production_date', 'published', 'published_at',
'created_at', 'updated_at',
'start_id', 'end_id'
]
read_only_fields = [
'created_at', 'updated_at', 'exported_at', 'excel_file',
'excel_file_url', 'production_date', 'published_at', 'quantity',
'start_id', 'end_id'
]
def get_category_display(self, obj):
return dict(CardTemplate.CATEGORY_CHOICES).get(obj.category)
def get_template_name(self, obj):
return obj.template.name if obj.template else None
def get_status_display(self, obj):
return obj.get_status_display()
def get_excel_file_url(self, obj):
if obj.excel_file:
return obj.excel_file.url
return None
class CardUsageLogSerializer(serializers.ModelSerializer):
action_display = serializers.SerializerMethodField()
user_display = serializers.SerializerMethodField()
class Meta:
model = CardUsageLog
fields = [
'id', 'card', 'user', 'user_display', 'action', 'action_display',
'details', 'old_status', 'new_status', 'created_at', 'ip_address'
]
read_only_fields = fields
def get_action_display(self, obj):
return obj.get_action_display() if hasattr(obj, 'get_action_display') else obj.action
def get_user_display(self, obj):
if obj.user:
return obj.user.username
return None
class CardScanSerializer(serializers.Serializer):
"""Serializer for scanning a card with NFC"""
unique_id = serializers.CharField(max_length=100)
def validate(self, data):
"""
Check that at least one identifier is provided.
"""
if not any([data.get('unique_id')]):
raise serializers.ValidationError("unique_id must be provided.")
return data
class CardUseSerializer(serializers.Serializer):
"""Serializer for using a card"""
unique_id = serializers.CharField(max_length=100)
class CardBatchGenerateSerializer(serializers.Serializer):
"""Serializer for generating card batches"""
template = serializers.PrimaryKeyRelatedField(queryset=CardTemplate.objects.all())
quantity = serializers.IntegerField(min_value=1, max_value=10000)
description = serializers.CharField(required=False, allow_blank=True)
class CardTemplatePublishSerializer(serializers.Serializer):
"""Serializer for publishing a card template"""
template_id = serializers.IntegerField()
class CardBatchPublishSerializer(serializers.Serializer):
"""Serializer for publishing a card batch"""
batch_id = serializers.IntegerField()
class CardBatchManufactureSerializer(serializers.Serializer):
"""Serializer for marking a card batch as manufactured"""
batch_id = serializers.IntegerField()
# 类别特定的属性序列化器
class CategoryTemplateSerializer(CardTemplateDetailSerializer):
"""Base class for category-specific template serializers"""
def to_representation(self, instance):
representation = super().to_representation(instance)
# Add category-specific attributes if they exist
try:
if instance.category == 'clothing' and hasattr(instance, 'clothing_attrs'):
representation['attributes'] = ClothingAttributesSerializer(instance.clothing_attrs).data
elif instance.category == 'prop' and hasattr(instance, 'prop_attrs'):
representation['attributes'] = PropAttributesSerializer(instance.prop_attrs).data
elif instance.category == 'song' and hasattr(instance, 'song_attrs'):
representation['attributes'] = SongAttributesSerializer(instance.song_attrs).data
elif instance.category == 'dance' and hasattr(instance, 'dance_attrs'):
representation['attributes'] = DanceAttributesSerializer(instance.dance_attrs).data
elif instance.category == 'furniture' and hasattr(instance, 'furniture_attrs'):
representation['attributes'] = FurnitureAttributesSerializer(instance.furniture_attrs).data
elif instance.category == 'decoration' and hasattr(instance, 'decoration_attrs'):
representation['attributes'] = DecorationAttributesSerializer(instance.decoration_attrs).data
except Exception as e:
# Handle the case where related attributes don't exist
representation['attributes'] = None
return representation
# 带有专有属性的卡牌详情序列化器
class CategoryCardDetailSerializer(CardDetailSerializer):
"""Card detail serializer with category-specific attributes"""
def to_representation(self, instance):
representation = super().to_representation(instance)
# If card has a template, get its specific attributes
if instance.template:
template = instance.template
try:
# Add category-specific attributes if they exist
if template.category == 'clothing' and hasattr(template, 'clothing_attrs'):
representation['attributes'] = ClothingAttributesSerializer(template.clothing_attrs).data
elif template.category == 'prop' and hasattr(template, 'prop_attrs'):
representation['attributes'] = PropAttributesSerializer(template.prop_attrs).data
elif template.category == 'song' and hasattr(template, 'song_attrs'):
representation['attributes'] = SongAttributesSerializer(template.song_attrs).data
elif template.category == 'dance' and hasattr(template, 'dance_attrs'):
representation['attributes'] = DanceAttributesSerializer(template.dance_attrs).data
elif template.category == 'furniture' and hasattr(template, 'furniture_attrs'):
representation['attributes'] = FurnitureAttributesSerializer(template.furniture_attrs).data
elif template.category == 'decoration' and hasattr(template, 'decoration_attrs'):
representation['attributes'] = DecorationAttributesSerializer(template.decoration_attrs).data
except Exception as e:
# Handle the case where related attributes don't exist
representation['attributes'] = None
return representation