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

Django API创建MenuItem触发IntegrityError外键约束问题

IntegrityError: NOT NULL constraint failed on LittleLemonAPI_menuitem.category_id when creating MenuItem via API

问题重现

使用Insomnia发送POST请求创建MenuItem时触发数据库约束错误:

IntegrityError at /api/menu-items NOT NULL constraint failed: LittleLemonAPI_menuitem.category_id

但通过Django admin后台创建相同数据(title=Spring Rolls, price=5.99, featured=true, category=1)完全正常,且已确认ID=1的Category已通过API创建并存在。

已尝试删除迁移文件、重置数据库,均未解决问题。

问题根源

问题出在MenuItemSerializer的两处配置冲突:

  1. depth=1干扰反序列化逻辑:该参数会让DRF将外键category序列化为嵌套的Category对象,但在创建数据时,DRF无法将你传入的数字ID正确解析为外键关联,导致category_id字段未被赋值,触发NOT NULL约束。
  2. 自定义验证返回值不匹配:你的validate_category方法返回的是Category的ID,但depth=1让序列化器期望接收完整的Category对象结构,两者不匹配进一步导致外键字段为空。

修复方案

基础修复(快速解决)

直接移除序列化器中的depth=1参数,让DRF默认处理外键的ID输入:

class MenuItemSerializer(serializers.ModelSerializer):
    def validate_category(self, value):
        try:
            # 返回Category对象而非ID,符合DRF外键处理逻辑
            return Category.objects.get(pk=value)
        except Category.DoesNotExist:
            raise serializers.ValidationError('Invalid category ID.')

    class Meta:
        model = MenuItem
        fields = ['id', 'title', 'price', 'featured', 'category']
        # 移除 depth = 1 配置

进阶优化(保留GET请求的嵌套数据)

如果需要在GET请求返回MenuItem时同时展示Category的详细信息,创建/更新时仍用ID输入,可拆分序列化器:

  1. 定义用于详情展示的序列化器:
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ['id', 'name', 'slug']  # 根据你的Category模型调整字段
class MenuItemDetailSerializer(serializers.ModelSerializer):
    category = CategorySerializer(read_only=True)

    class Meta:
        model = MenuItem
        fields = ['id', 'title', 'price', 'featured', 'category']
  1. 修改视图类,根据请求方法切换序列化器:
class MenuItemListView(generics.ListCreateAPIView):
    queryset = MenuItem.objects.all()

    def get_serializer_class(self):
        # GET请求用带嵌套的序列化器,其他操作用基础序列化器
        if self.request.method == 'GET':
            return MenuItemDetailSerializer
        return MenuItemSerializer

    def get_permissions(self):
        permission_classes = [IsAuthenticated]
        if self.request.method != 'GET':
            # 补全权限类,原代码存在语法不完整问题
            permission_classes = [IsAuthenticated, IsManager | IsAdmin]
        return [permission() for permission in permission_classes]

额外检查

  • 确认请求JSON中的category字段确实是数字1,无拼写或格式错误:
{
    "title": "Spring Rolls",
    "price": "5.99",
    "featured": true,
    "category": 1
}
  • 补全视图类中权限代码的缺失部分,避免隐性权限问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 13:46:15