簡體   English   中英

Django獨特的約束失敗?

[英]Django unique together constraint failure?

使用Django 1.5.1。 Python 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之后,為什么我仍然可以輸入三個相同值的行?

我現在只是猜測它可能與foreign_key字段的默認值None有關,因為在unique_together之前,當我在slug上有unique = True時,一切正常。 因此,如果是這種情況,我應該具有哪個默認值表示空值,還保持唯一約束?

在Postgresql中, NULL不等於任何其他NULL 因此,您創建的行不同(從Postgres的角度來看)。

更新

你有幾種方法來處理它:

  • 禁止外鍵的Null值並使用一些默認值
  • 覆蓋模型的save方法以檢查是否存在此類行
  • 改變SQL標准:)

為模型添加一個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")

注意,默認save方法不會調用clean (或full_clean )。

注意:如果您將此代碼放在save方法中,更新表單(如在管理員中)將不起作用:由於ValidationError異常,您將遇到跟蹤錯誤。

只需在slug字段上手動創建二級索引,但僅限於foreign_key_id NULL值:

CREATE INDEX table_name_unique_null_foreign_key
  ON table_name (slug) WHERE foreign_key_id is NULL

請注意,Django不支持此功能,因此如果沒有自定義表單/模型驗證,您將獲得純IntegrityError / 500。

使用空列創建唯一約束的可能重復

正如業余愛好者所說,“在Postgresql中NULL不等於任何其他NULL。因此你創建的行不一樣(從Postgres的角度來看)。”

解決此挑戰的另一種可能方法是在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對象,則需要將相同的驗證邏輯添加到您的管理表單中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM