Django 中的 unique_together 不起作用

ran*_*ndy 3 django django-models

unique_together不起作用,它只在第一个字段上设置唯一约束并忽略第二个字段。有没有办法强制执行唯一约束?

class BaseModel(models.Model):

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    deleted = models.DateTimeField(db_index=True, null=True, blank=True)
    last_modified_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True


class Book(BaseModel):
    first_form_number = models.CharField(max_length=8)

    class Meta:
        unique_together = (("first_form_number", "deleted"),)
Run Code Online (Sandbox Code Playgroud)

hyn*_*cer 5

您的模型在创建正确的唯一索引的范围内正常工作:

$ python manage.py sqlmigrate app 0001_initial
...
CREATE UNIQUE INDEX "app_base_slug_version_a455c5b7_uniq" ON "app_base" ("slug", "version");
...
Run Code Online (Sandbox Code Playgroud)

(预计您的应用程序名称是“app”)

我必须大致同意 user3541631 的回答。这通常取决于数据库,但 Django 直接支持的所有四个数据库引擎都相似。他们期望“空值在 UNIQUE 列中是不同的”(请参阅SQLite 与其他数据库引擎中的 NULL 处理

我验证了您的问题是否为空:

class Test(TestCase):
    def test_without_null(self):
        timestamp = datetime.datetime(2017, 8, 25, tzinfo=pytz.UTC)
        book_1 = Book.objects.create(deleted=timestamp, first_form_number='a')
        with self.assertRaises(django.db.utils.IntegrityError):
            Book.objects.create(deleted=timestamp, first_form_number='a')

    def test_with_null(self):
        # this test fails !!! (and a duplicate is created)
        book_1 = Book.objects.create(first_form_number='a')
        with self.assertRaises(django.db.utils.IntegrityError):
            Book.objects.create(first_form_number='a')
Run Code Online (Sandbox Code Playgroud)

如果您愿意手动编写迁移以创建两个特殊的部分唯一索引,则可以为PostgreSQL 提供一个解决方案:

CREATE UNIQUE INDEX book_2col_uni_idx ON app_book (first_form_number, deleted)
WHERE deleted IS NOT NULL;

CREATE UNIQUE INDEX book_1col_uni_idx ON app_book (first_form_number)
WHERE deleted IS NULL;
Run Code Online (Sandbox Code Playgroud)

看: