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

设置default的Model ForeignKey字段触发非空约束问题排查

问题:Django模型默认值不生效导致非空约束错误

我已创建名为“noname”的Maker实例,期望创建Item时若未通过表单设置maker字段,系统自动分配该默认实例。但实际操作时,未设置maker的Item创建请求触发错误:
psycopg2.errors.NotNullViolation: null value in column "maker_id" of relation "stock_management_item" violates not-null constraint

我可以在视图/表单层面处理这个问题,但想搞清楚为什么模型中定义的default值不生效。

模型代码

class Maker(models.Model):
    name = models.CharField()

def get_default_maker():
    default_maker, created = Maker.objects.get_or_create(name="noname")
    return default_maker.pk


class Item(models.Model):
    maker = models.ForeignKey(
        Maker,
        verbose_name="Maker",
        on_delete=models.SET(get_default_maker),
        default=get_default_maker
        )

补充代码(UPDATE 1)

视图代码

def add_stock_item(request):
    form = forms.ItemForm()
    if request.method == "POST":
        form = forms.ItemForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect(reverse("items_list"))
    else:   
        return render(
            request,
            "stock_management/item_create_form.html",
            context={"form": form}
        )

class ItemUpdateView(UpdateView):

    model = models.Item
    form_class = forms.ItemForm
    template_name = "stock_management/item_update_form.html"

    def get_success_url(self) -> str:
        return reverse("items_list")

表单代码

class ItemForm(forms.ModelForm):

    maker = forms.ModelChoiceField(
        label='Maker',
        queryset=models.Maker.objects.all(),
        widget=Select2Widget(attrs={"style": "width: 100%"}),
        required=False
        )
    
    class Meta:
        model = models.Item
        fields = ("id", "maker", "name", "item_type")

该表单同时用于创建和更新视图,两种场景下未设置maker字段都会触发相同错误。


原因分析

问题核心在表单设置:你将maker字段的required设为False,当用户未选择maker时,表单会向模型传递None值,而非触发模型的默认值逻辑。Django的ModelForm在字段被显式赋值(哪怕是None)时,会跳过模型层面的默认值处理,直接将None写入数据库,而你的maker字段在数据库中是NOT NULL约束,因此触发报错。

解决方案

方案1:在表单clean方法中补全默认值

当maker字段为空时,手动指定默认实例:

class ItemForm(forms.ModelForm):
    maker = forms.ModelChoiceField(
        label='Maker',
        queryset=models.Maker.objects.all(),
        widget=Select2Widget(attrs={"style": "width: 100%"}),
        required=False
    )
    
    def clean_maker(self):
        maker = self.cleaned_data.get('maker')
        if not maker:
            return Maker.objects.get(name="noname")
        return maker
    
    class Meta:
        model = models.Item
        fields = ("id", "maker", "name", "item_type")

方案2:在表单初始化时设置默认选中值

仅在创建新Item时,默认选中“noname”实例:

class ItemForm(forms.ModelForm):
    maker = forms.ModelChoiceField(
        label='Maker',
        queryset=models.Maker.objects.all(),
        widget=Select2Widget(attrs={"style": "width: 100%"}),
        required=False
    )
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 仅新建对象时设置初始值
        if not self.instance.pk:
            default_maker = Maker.objects.get(name="noname")
            self.fields['maker'].initial = default_maker.pk
    
    class Meta:
        model = models.Item
        fields = ("id", "maker", "name", "item_type")

另外注意:你的模型中Maker类定义缺少class关键字,正确写法应为class Maker(models.Model):,这可能是输入笔误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 04:42:47