Files
zhangmenu/menu/serializers.py
DeveloperandClaude Opus 4.6 94647e2b4b perf: 性能与资源优化 - 提取内联资源、数据库查询优化、限流与缓存增强
主要优化:
- templates/index.html: 220KB → 28KB,内联CSS/JS提取为静态文件
- menu/serializers.py: 修复 Order.ingredients_summary N+1 查询问题
- menu/services/dish_service.py: 移除多余查询,添加 only() 限制字段
- menu/views.py: 上传接口添加每分钟20次限流
- static/js/api.js: 统一API错误处理
- zhangmenu/settings.py: 添加Whitenoise压缩、Redis缓存支持、环境变量配置
- static/css/app.css, static/js/app.js: 提取的内联资源文件
- 新增 migrations, requirements.txt, .env.example

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 17:18:59 +08:00

81 lines
2.9 KiB
Python

from django.db.models import Count
from rest_framework import serializers
from .models import Dish, DishCategory, Order
class DishCategorySerializer(serializers.ModelSerializer):
"""分类序列化器"""
dish_count = serializers.IntegerField(read_only=True)
class Meta:
model = DishCategory
fields = ['id', 'name', 'icon', 'order', 'dish_count']
class DishSerializer(serializers.ModelSerializer):
"""菜品序列化器"""
category_name = serializers.CharField(source='category.name', read_only=True)
category_id = serializers.IntegerField(write_only=True, required=False, allow_null=True)
class Meta:
model = Dish
fields = ['id', 'name', 'category', 'category_name', 'category_id',
'cover_image', 'description', 'ingredients', 'view_count',
'created_at', 'updated_at']
read_only_fields = ['created_at', 'updated_at', 'view_count']
class OrderSerializer(serializers.ModelSerializer):
"""订单序列化器"""
dishes_detail = DishSerializer(source='dishes', many=True, read_only=True)
ingredients_summary = serializers.SerializerMethodField()
dish_ids = serializers.ListField(
child=serializers.IntegerField(),
write_only=True,
required=False
)
class Meta:
model = Order
fields = ['id', 'name', 'dishes', 'dishes_detail', 'dish_ids',
'status', 'party_date', 'participants',
'created_at', 'completed_at', 'note', 'ingredients_summary', 'images']
read_only_fields = ['created_at', 'completed_at']
extra_kwargs = {
'dishes': {'required': False}
}
def get_ingredients_summary(self, obj):
# Compute in Python using prefetched dishes (avoids N+1)
summary = {}
for dish in obj.dishes.all():
for ing in dish.ingredients:
name = ing.get('name', '').strip()
amount = ing.get('amount', '').strip()
if name:
if name in summary:
if amount:
summary[name] += f" + {amount}"
else:
summary[name] = amount
return [{'name': k, 'amount': v} for k, v in summary.items()]
def create(self, validated_data):
dish_ids = validated_data.pop('dish_ids', [])
order = Order.objects.create(**validated_data)
if dish_ids:
dishes = Dish.objects.filter(id__in=dish_ids)
order.dishes.set(dishes)
return order
def update(self, instance, validated_data):
dish_ids = validated_data.pop('dish_ids', None)
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
if dish_ids is not None:
dishes = Dish.objects.filter(id__in=dish_ids)
instance.dishes.set(dishes)
return instance