18

Django 1.5.1 を使用しています。パイソン 2.7.3。

外部キー フィールドとスラッグ フィールドを使用して、一緒に一意の制約を実行したいと考えていました。だから私のモデルのメタでは、私はやった

foreign_key = models.ForeignKey("self", null=True, default=None)
slug = models.SlugField(max_length=40, unique=False)

class Meta:
    unique_together = ("foreign_key", "slug")

Postgres (9.1) でテーブルの説明を確認したところ、データベース テーブルに制約が設定されていました。

-- something like
"table_name_foreign_key_id_slug_key" UNIQUE CONSTRAINT, btree (foreign_key_id, slug)

ただし、None/null のforeign_key と重複した文字列をデータベース テーブルに保存することはできます。

例えば、

入力して保存できました

# model objects with slug="python" three times; all three foreign_key(s) 
# are None/null because that is their default value
MO(slug="python").save()
MO(slug="python").save()
MO(slug="python").save()

では、unique_together を使用した後でも、同じ値の行を 3 つ入力できるのはなぜでしょうか?

unique_together の前に、slug で unique=True を使用したとき、すべてが正常に機能したためです。その場合、null 値を示すだけでなく、一意の制約を維持するデフォルト値は何を設定すればよいでしょうか?

4

4 に答える 4

25

PostgresqlNULLでは、他の と同等ではありませんNULL。したがって、作成する行は (Postgres の観点から) 同じではありません。

アップデート

それに対処するにはいくつかの方法があります。

  • Null外部キーの値を禁止し、デフォルト値を使用します
  • モデルのメソッドをオーバーライドして、saveそのような行が存在しないことを確認します
  • SQL標準を変更してください:)
于 2013-07-07T07:59:38.333 に答える
1

モデルにメソッドを追加してclean、既存の行を編集できるようにします。

def clean(self):
    queryset = MO.objects.exclude(id=self.id).filter(slug=self.slug)
    if self.foreign_key is None:
        if queryset.exists():
            raise ValidationError("A row already exists with this slug and no key")
    else:
        if queryset.filter(foreign_key=self.foreign_key).exists():
            raise ValidationError("This row already exists")

clean(またはfull_clean) はデフォルトのsaveメソッドでは呼び出されないことに注意してください。

注意: このコードをメソッドに入れるとsave、(管理者のように) フォームの更新が機能しなくなります:ValidationError例外によるトレースバック エラーが発生します。

于 2013-10-29T23:19:19.667 に答える
0

趣味が述べたように、「Postgresql NULL では、他の NULL と同じではありません。したがって、作成する行は (Postgres の観点からは) 同じではありません。」

この課題に対処するもう 1 つの方法は、form_valid メソッドのビュー レベルでカスタム検証を追加することです。

views.py で:

def form_valid(self, form): 

  --OTHER VALIDATION AND FIELD VALUE ASSIGNMENT LOGIC--

  if ModelForm.objects.filter(slug=slug,foreign_key=foreign_key:   
    form.add_error('field',
      forms.ValidationError( _("Validation error message that shows up in your form. "), 
      code='duplicate_row', )) 
    return self.form_invalid(form)

このアプローチは、クラス ベースのビューを使用している場合、特にユーザーから隠したいフィールドに値を自動的に割り当てている場合に役立ちます。

長所:

  • データベースにダミーのデフォルト値を作成する必要はありません
  • 更新フォームは引き続き使用できます(Toffの回答を参照)

短所: - これは、データベース レベルで直接作成された重複行から保護されません。- Django の管理バックエンドを使用して新しい MyModel オブジェクトを作成する場合は、これと同じ検証ロジックを管理フォームに追加する必要があります。

于 2016-06-09T23:18:46.020 に答える