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).
已确认前端提交了所有必填字段,相关提交截图:

相关代码
模型层代码
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
相关产品推荐
相关产品推荐

