带有可为空的外键的 Django unique_together [英] Django unique_together with nullable ForeignKey

查看:32
本文介绍了带有可为空的外键的 Django unique_together的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 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_cleanvalidate_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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆