带有可为空的外键的 Django unique_together [英] Django unique_together with nullable ForeignKey
问题描述
我使用 Sqlite 在我的开发机器上使用 Django 1.8.4,我有这些模型:
I'm using Django 1.8.4 in my dev machine using Sqlite and I have these models:
class ModelA(Model):
field_a = CharField(verbose_name='a', max_length=20)
field_b = CharField(verbose_name='b', max_length=20)
class Meta:
unique_together = ('field_a', 'field_b',)
class ModelB(Model):
field_c = CharField(verbose_name='c', max_length=20)
field_d = ForeignKey(ModelA, verbose_name='d', null=True, blank=True)
class Meta:
unique_together = ('field_c', 'field_d',)
我已经运行了正确的迁移并在 Django Admin 中注册了它们.所以,使用管理员我已经完成了这个测试:
I've run proper migration and registered them in the Django Admin. So, using the Admin I've done this tests:
- 我能够创建 ModelA 记录,但 Django 禁止我创建重复记录 - 正如预期的那样!
- 当 field_b 不为空时,我无法创建相同的 ModelB 记录
- 但是,当使用 field_d 为空时,我可以创建相同的 ModelB 记录
我的问题是:如何将 unique_together 应用于可为空的外键?
My question is: How do I apply unique_together for nullable ForeignKey?
我为这个问题找到的最新答案是 5 年......我确实认为 Django 已经进化,问题可能不一样.
The most recent answer I found for this problem has 5 year... I do think Django have evolved and the issue may not be the same.
推荐答案
UPDATE:我之前版本的答案是实用的,但设计很糟糕,这个版本考虑了一些评论和其他答案.
UPDATE: previous version of my answer was functional but had bad design, this one takes in account some of the comments and other answers.
在 SQL 中 NULL 不等于 NULL.这意味着如果您有两个对象,其中 field_d == None 和 field_c == "somestring"
它们不相等,因此您可以创建两者.
In SQL NULL does not equal NULL. This means if you have two objects where field_d == None and field_c == "somestring"
they are not equal, so you can create both.
您可以覆盖 Model.clean
以添加您的检查:
You can override Model.clean
to add your check:
class ModelB(Model):
#...
def validate_unique(self, exclude=None):
if ModelB.objects.exclude(id=self.id).filter(field_c=self.field_c,
field_d__isnull=True).exists():
raise ValidationError("Duplicate ModelB")
super(ModelB, self).validate_unique(exclude)
如果在表单之外使用,您必须调用 full_clean
或 validate_unique
.
If used outside of forms you have to call full_clean
or validate_unique
.
不过要注意处理竞争条件.
Take care to handle the race condition though.
这篇关于带有可为空的外键的 Django unique_together的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!