You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django MealSerializer更新未触发 同日期同类型餐品唯一约束报错解决

问题修复方案

1. 核心问题原因

  • 唯一约束报错、update方法不触发:当前POST接口没有先校验对应menu/date/type/url的Meal是否存在,直接调用序列化器创建逻辑,重复提交就触发联合唯一约束,自然不会走到update流程
  • 一次只能存1条餐品:序列化器的update方法未处理items数组更新,且视图层的update方法缩进错误、误用了MealItemSerializer,同时items的创建/更新逻辑有遗漏

2. 代码修改

① 序列化器(serializers.py)修改

class MealSerializer(serializers.ModelSerializer):
    items = MealItemSerializer(many=True)
    class Meta:
        model = Meal
        fields = ['id', 'date', 'type','url', 'items', 'menu', ]
        validators = []

    def create(self, validated_data):
        item_data = validated_data.pop('items')
        meal = Meal.objects.create(**validated_data)
        # 批量创建items,支持多条数据一次性写入
        MealItem.objects.bulk_create([
            MealItem(meal=meal, **item) for item in item_data
        ])
        return meal
    
    def update(self, instance, validated_data):
        instance.date = validated_data.get('date', instance.date)
        instance.type = validated_data.get('type', instance.type)
        instance.url = validated_data.get('url', instance.url)
        instance.save()
        # 处理items更新:先清空原有items,再批量创建新的,避免数据冗余
        item_data = validated_data.pop('items', None)
        if item_data:
            instance.items.all().delete()
            MealItem.objects.bulk_create([
                MealItem(meal=instance, **item) for item in item_data
            ])
        return instance

② 视图层(views.py)修改

class MealViewSet(LoginRequiredMixin, viewsets.ModelViewSet):
    # 替换ReadOnlyModelViewSet为ModelViewSet,默认支持增删改查操作
    serializer_class = MealSerializer
    pagination_class = MealPagination

    def get_queryset(self):
        if self.request.user.is_authenticated:
            queryset = Menu.objects.all()
            menu = get_object_or_404(
                queryset, pk=self.kwargs['menu_pk'], users=self.request.user)
            return Meal.objects.filter(menu=menu)
        return HttpResponseRedirect("/login")

    def create(self, request, menu_pk):
        # 替换原自定义post方法为标准create方法
        data = request.data
        user = request.user
        if not (user.is_authenticated and user.has_perm("meals.change_menu")):
            return Response({'failure': "用户未登录或无提交权限"})
        
        # 先查询是否存在对应Meal实例,存在则走更新逻辑,不存在走创建逻辑
        try:
            meal = Meal.objects.get(
                menu_id=menu_pk,
                date=data['date'],
                type=data['type'],
                url=data['url']
            )
            # 传入已有实例,序列化器自动调用update方法
            serializer = self.get_serializer(meal, data=data, partial=True)
        except Meal.DoesNotExist:
            # 无匹配实例,走创建逻辑
            serializer = self.get_serializer(data=data)
        
        serializer.is_valid(raise_exception=True)
        serializer.save()
        # 直接返回序列化后的数据,完全匹配你需要的返回格式
        return Response(serializer.data)

    def update(self, request, menu_pk, pk=None):
        user = request.user
        if not (user.is_authenticated and user.has_perm("meals.change_menu")):
            return Response({'failure': "用户未登录或无修改权限"})
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)

3. 效果验证

  • 重复提交相同menu/date/type/url的餐食时,会自动更新原有数据,不会触发唯一约束报错
  • 同一日期下lunch和dinner的type字段不同,查询和更新时会作为独立实例处理,不会互相干扰
  • 提交的items数组支持多条数据,会批量写入数据库,返回格式和要求的结构完全一致

内容的提问来源于stack exchange,提问作者CodeSmitty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 00:54:03