一尘不染

在Django中允许为空的唯一字段

django

我有Foo模型,其中有栏杆。bar字段应该是唯一的,但允许为空,这意味着如果bar字段为null,我想允许多个记录,但如果不是,null则值必须是唯一的。

这是我的模型:

class Foo(models.Model):
    name = models.CharField(max_length=40)
    bar = models.CharField(max_length=40, unique=True, blank=True, null=True, default=None)

这是该表的相应SQL:

CREATE TABLE appl_foo
(
    id serial NOT NULL,
     "name" character varying(40) NOT NULL,
    bar character varying(40),
    CONSTRAINT appl_foo_pkey PRIMARY KEY (id),
    CONSTRAINT appl_foo_bar_key UNIQUE (bar)
)   

当使用管理界面在bar为null的情况下创建多个1个foo对象时,它给我一个错误:“此Bar的Foo已经存在。”

但是,当我插入数据库(PostgreSQL)时:

insert into appl_foo ("name", bar) values ('test1', null)
insert into appl_foo ("name", bar) values ('test2', null)

这样做很好,它允许我插入1条以上且bar为null的记录,因此数据库允许我执行我想要的操作,这是Django模型的错误。有任何想法吗?

编辑

就数据库而言,解决方案的可移植性不是问题,我们对Postgres感到满意。我尝试过设置可调用对象的唯一性,这是我的函数针对bar的特定值返回True / False ,它没有给出任何错误,但是接缝就像根本没有任何作用。

到目前为止,我已经从bar属性中删除了唯一的说明符,并在应用程序中处理了bar的唯一性,但是仍在寻找更优雅的解决方案。有什么建议吗?


阅读 1331

收藏
2020-03-27

共1个答案

一尘不染

自从票务#9039被修复以来,Django就唯一性检查的目的没有将NULL视为NULL,请参阅:

http://code.djangoproject.com/ticket/9039

这里的问题是,表单CharField的规范化“空白”值是一个空字符串,而不是无。因此,如果将该字段保留为空白,则会得到一个空字符串,而不是NULL,存储在数据库中。在Django和数据库规则下,空字符串等于用于唯一性检查的空字符串。

你可以通过使用clean_bar方法为Foo提供自己的自定义模型表单,从而将空字符串转换为None,从而强制管理界面为空字符串存储NULL:

class FooForm(forms.ModelForm):
    class Meta:
        model = Foo
    def clean_bar(self):
        return self.cleaned_data['bar'] or None

class FooAdmin(admin.ModelAdmin):
    form = FooForm
2020-03-27