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
相关产品推荐
相关产品推荐

