【问题标题】:DJANGO > 2.2 - Unique together on foreign keys A, B OR foreign keys A, B, CDJANGO > 2.2 - 外键 A、B 或外键 A、B、C 上的唯一性
【发布时间】:2019-09-02 09:27:06
【问题描述】:

在 Django 2.2 中,您可以摆脱 unique_together 以支持带有 UniqueConstraint 类的 constraints 列表,因为它有一个 condition 参数。

MyObject 有三个外键,一个是可选的。

class MyObject(ModelBase):
    parent=... # mandatory
    source=... # mandatory
    subsource=... # optional

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["parent", "source", "subsource"],    
                name="unique_subsource"
            ),
            models.UniqueConstraint(
                fields=["parent", "source"],
                condition=models.Q(subsource=None),
                name="unique_source",
            ),
        ]

想象一下,我尝试依次创建以下对象和预期的验证:

parent   source   subsource   valid?
1.       1.       1.          yes
1.       1.       2.          yes
1.       1.       -           yes
1.       1.       -           no
1.       1.       3.          yes
1.       2.       -           yes
...

于是我写了两个测试:

def test_unique1(self):
    """ unique parent/source/subsource """
    parent = ParentFactory()
    source = SourceFactory()
    subsource = SubsourceFactory()
    MyObjectFactory(parent=parent, source=source, subsource=subsource)
    myobj = MyObjectFactory.build(parent=parent, source=source, subsource=subsource)
    self.should_raise_validation_error(myobj)

def test_unique2(self):
    """ unique parent/source """
    parent = ParentFactory()
    source = SourceFactory()
    subsource = SubsourceFactory()
    MyObjectFactory(parent=parent, source=source)
    myobj = MyObjectFactory.build(parent=parent, source=source)
    self.should_raise_validation_error(myobj)

但后者不会引发任何验证错误:

.....F.
======================================================================
FAIL: test_unique2
unique parent/source/subsource
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/sebastiennicolaidis/dev/python/scientcap/macrovars/tests/models/test_dsflink.py", line 50, in test_unique2
    self.should_raise_validation_error(dsflink)
  File "/Users/sebastiennicolaidis/dev/python/scientcap/shared/tests/base_model_test.py", line 9, in should_raise_validation_error
    instance.full_clean()
AssertionError: ValidationError not raised

----------------------------------------------------------------------
Ran 7 tests in 0.141s

[UPDATE 2] sqlmigrate 的输出:

BEGIN;
--
-- Change Meta options on dataset
--
--
-- Change Meta options on dsforecast
--
--
-- Alter unique_together for dsflink (0 constraint(s))
--
ALTER TABLE "macrovars_dsflink" DROP CONSTRAINT "macrovars_dsflink_parent_id_source_id_73901780_uniq";
--
-- Create constraint unique_subsource on model dsflink
--
CREATE UNIQUE INDEX "unique_subsource" ON "macrovars_dsflink" ("parent_id", "source_id", "subsource_id") WHERE "subsource_id" IS NOT NULL;
--
-- Create constraint unique_source on model dsflink
--
CREATE UNIQUE INDEX "unique_source" ON "macrovars_dsflink" ("parent_id", "source_id") WHERE "subsource_id" IS NULL;
COMMIT;

【问题讨论】:

  • 您能看一下添加这些约束的迁移生成的sqlmigrate 吗?
  • 已添加但需要翻译一下dsflink => my_obj,而app_namemacrovars
  • 乍一看,好像从来没有添加过第二个唯一约束?

标签: django python-3.x postgresql django-models


【解决方案1】:

我认为解决方案是同时创建带有条件的约束。

class MyObject(ModelBase):
    parent=... # mandatory
    source=... # mandatory
    subsource=... # optional

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["parent", "source", "subsource"],    
                condition=models.Q(subsource__isnull=False),  # <- filter subsource is null
                name="unique_subsource"
            ),
            models.UniqueConstraint(
                fields=["parent", "source"],
                condition=models.Q(subsource__isnull=True),
                name="unique_source",
            ),
        ]

【讨论】:

  • 现在是两个验证没有引发(我更新了 sql 迁移输出),但我怀疑 condition 删除了 Django 级别的验证。
  • 约束的验证段落。
  • 嗯,这很奇怪,理想情况下它应该选择两个约束之一
  • @Sebabouche 我在本地检查过,它按预期工作。
猜你喜欢
  • 1970-01-01
  • 2011-01-31
  • 1970-01-01
  • 2011-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-10
  • 2011-12-23
相关资源
最近更新 更多