1. ホーム
  2. django

[解決済み] Django で null を許容するユニークなフィールド

2022-04-26 08:12:01

質問

私は、フィールドbarを持つモデルFooを持っています。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のfooオブジェクトを1つ以上作成しようとすると、次のようなエラーが表示されます。

しかし、データベース(PostgreSQL)に挿入すると。

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

これはちゃんと動作しますし、bar が NULL であっても 1 つ以上のレコードを挿入することができます。何かアイデアはありますか?

EDIT

DBに関しては、ソリューションの移植性は問題なく、Postgresで満足しています。 の特定の値に対して True/False を返す私の関数である callable にユニークを設定しようとしました。 バー エラーは出ませんでしたが、全く効果がないように思われます。

ここまでで、ユニーク指定子を外した バー プロパティを処理し バー の一意性を確保する必要がありますが、よりエレガントな解決策を探しています。何かお勧めはありますか?

どのように解決するのですか?

Django は、チケット #9039 が修正されて以来、一意性チェックのために NULL を NULL と等しいと見なさないようになりました、参照。

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

ここでの問題は、フォームの CharField の正規化された "blank" 値が None ではなくて、空の文字列であることです。そのため、フィールドを空白にすると、NULLではなく空文字列がDBに保存されます。 空文字列は、Django とデータベースの両方のルールにおいて、一意性チェックのために空文字列と同じになります。

Foo 用にカスタマイズしたモデルフォームに、空の文字列を None に変換する clean_bar メソッドを用意すれば、管理者インタフェースに空の文字列の 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