使用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时,一切正常。那么如果是这种情况,我应该使用哪个默认值来表示空值,还要保持唯一约束?
答案 0 :(得分:20)
在Postgresql NULL
中不等于任何其他NULL
。因此,您创建的行不同(从Postgres的角度来看)。
<强>更新强>
您有几种方法可以解决它:
Null
值并使用一些默认值save
方法以检查是否存在此行答案 1 :(得分: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
异常,您将出现回溯错误。
答案 2 :(得分:0)
只需在ng-change
字段上手动创建辅助索引,但仅限于slug
中的NULL值:
foreign_key_id
请注意,Django不支持此功能,因此如果没有自定义表单/模型验证,您将获得纯IntegrityError / 500.
可能重复答案 3 :(得分:0)
正如业余爱好者所说,&#34;在Postgresql中,NULL不等于任何其他NULL。因此,您创建的行不同(从Postgres&#39;透视)。&#34;
解决此挑战的另一种可能方法是在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)
&#13;
如果您使用基于类的视图,此方法很有用,尤其是当您自动将值分配给要隐藏用户的字段时。
优点:
缺点: - 这不能防止直接在数据库级别创建的重复行。 - 如果您使用Django的管理员后端来创建新的MyModel对象,您需要将相同的验证逻辑添加到您的管理表单中。