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

Django插入产品数据报categories_id非空约束违反错误排查

Django DRF 外键字段写入触发非空约束报错问题

问题描述

使用serializers实现包含外键字段的CRUD操作时,插入数据失败,抛出如下错误:

django.db.utils.IntegrityError: null value in column "categories_id" of relation "polls_products" violates not-null constraint
DETAIL: Failing row contains (42, aw, 12, 4, good, 9, null, 3, 3, 1, t).

已确认前端提交了所有必填字段,相关提交截图:
提交截图1
提交截图2

相关代码

模型层代码

class Products(models.Model):
    categories = models.ForeignKey(Categories,on_delete=models.CASCADE)
    sub_categories = models.ForeignKey(SUBCategories,on_delete=models.CASCADE)
    color = models.ForeignKey(Colors,on_delete=models.CASCADE)
    size = models.ForeignKey(Size,on_delete=models.CASCADE)
    # image = models.ImageField(upload_to = 'media/',width_field=None,height_field=None,null=True)
    title = models.CharField(max_length=50)
    price = models.CharField(max_length=10)
    sku_number = models.CharField(max_length=10)
    product_details = models.CharField(max_length=300)
    quantity = models.IntegerField(default=0)
    isactive = models.BooleanField(default=True)

Serializer代码

class POLLSerializer(serializers.ModelSerializer):
    class Meta:
        model = Products
        fields = "__all__"

class pollSerializer(serializers.ModelSerializer):
    categories = serializers.StringRelatedField()
    class Meta:
        model = Products
        fields = "__all__"

视图层插入逻辑代码

def insert(request):
    data = {}
    if request.method == "POST":
        print('POST',id)
        data['categories'] = request.POST.get('categories')
        data['sub_categories'] = request.POST.get('sub_categories')
        data['color'] = request.POST.get('color')
        data['size'] = request.POST.get('size')
        data['title'] = request.POST.get('title')
        data['price'] = request.POST.get('price')
        data['sku_number'] = request.POST.get('sku_number')
        data['product_details'] = request.POST.get('product_details')
        data['quantity'] = request.POST.get('quantity')

        form = pollSerializer(data=data)
        print(form)
        if form.is_valid():
            print('form after valid:',form)
            print("error of form:",form.errors)
            form.save()
            
            messages.success(request, "Record Updated Successfully...!:)")
            return redirect("polls:show")
        else:
            print('form not valid')
            print(form.errors)
    if request.method == "GET":
        print('POST',id)
        category_dict = Categories.objects.filter(isactive=True)
        category = CategoriesSerializer(category_dict, many=True)
        sub_category_dict = SUBCategories.objects.filter(isactive=True)
        sub_category = SUBCategoriesSerializer(sub_category_dict,many=True)
        color_dict = Colors.objects.filter(isactive=True)
        color = ColorsSerializer(color_dict,many=True)
        size_dict = Size.objects.filter(isactive=True)
        size = SizeSerializer(size_dict,many=True)
        hm = {"context": category.data,"sub_context":sub_category.data,"color_context":color.data,"size_context":size.data}
        return render(request, "polls/product_insert.html", hm)

错误原因

核心问题出在自定义的pollSerializer配置上,共2个关键问题:

  • categories字段被指定为serializers.StringRelatedField(),该字段默认是只读属性,仅用于序列化输出时展示外键的字符串值,完全不参与反序列化(写入操作)的字段接收、校验和赋值。所以即使前端传了categories的id,该字段也不会接收值,保存到数据库时categories_id为null,直接触发非空约束报错。
  • 其余三个外键字段sub_categories、color、size没有做额外覆盖,ModelSerializer默认使用PrimaryKeyRelatedField处理,本身支持传入主键id完成写入,但被单独修改的categories字段破坏了写入逻辑。

修复方案

根据业务需求选择对应改法即可:

方案1:仅需通过外键ID完成CRUD(最简单)

删除自定义的pollSerializer,直接使用原生生成的POLLSerializer,默认外键字段就支持传入主键id写入,完全满足常规CRUD需求。

class POLLSerializer(serializers.ModelSerializer):
    class Meta:
        model = Products
        fields = "__all__"

视图中实例化serializer时,把pollSerializer替换为POLLSerializer即可。

方案2:需要序列化输出时返回外键名称

如果需要接口返回时展示外键的字符串名称而非ID,需要区分读写场景分别配置字段:

class pollSerializer(serializers.ModelSerializer):
    # 写入场景用PrimaryKeyRelatedField接收外键ID
    categories = serializers.PrimaryKeyRelatedField(queryset=Categories.objects.all())
    sub_categories = serializers.PrimaryKeyRelatedField(queryset=SUBCategories.objects.all())
    color = serializers.PrimaryKeyRelatedField(queryset=Colors.objects.all())
    size = serializers.PrimaryKeyRelatedField(queryset=Size.objects.all())
    # 读取场景加单独的只读字段返回外键名称
    categories_name = serializers.StringRelatedField(source='categories', read_only=True)
    class Meta:
        model = Products
        fields = "__all__"

额外注意:视图中POST逻辑下的print('POST',id)打印的是Python内置的id函数,和业务字段无关,这段打印无实际意义可以删除。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 04:24:15