【问题标题】:Creating Partial Indexes with Django 1.7使用 Django 1.7 创建部分索引
【发布时间】:2014-12-12 04:18:17
【问题描述】:

Django 1.7 的 documentation 提到 RunSQL 类可用于在表上创建部分索引。我有一张桌子,我希望 titleblogcategory 的组合是独一无二的。但是,如果没有提供类别,标题和博客的组合仍然应该是唯一的。

class Post(models.Model):
    title = models.CharField(max_length=200)
    blog = models.ForeignKey(Blog)
    category = models.ForeignKey(Category, null=True, blank=True)

我可以使用部分索引来实现这个约束(如下所示的 SQL)。如果我使用 Django 1.7 迁移,我应该在哪里添加此代码?

CREATE UNIQUE INDEX idx1 
  ON Post (title, blog_id, category_id) 
  WHERE category_id IS NOT NULL;

CREATE UNIQUE INDEX idx2 
  ON Post (title, blog_id)
  WHERE category_id IS NULL;

【问题讨论】:

    标签: django django-models django-orm django-1.7 django-migrations


    【解决方案1】:

    Django 2.2 及更高版本

    从 2.2 版起,Django 在支持 declarative partial unique indexes 的数据库(PostgreSQL 和 SQLite)上支持它们。所以你可以这样做:

    from django.db.models import Model, Q, UniqueConstraint
    
    class Post(Model):
        ...
        class Meta:
            constraints = [
                UniqueConstraint(
                    fields=["title", "blog", "category"],
                    name="idx1",
                    condition=Q(category__isnull=False)),
                UniqueConstraint(
                    fields=["title", "blog"], 
                    name="idx2",                    
                    condition=Q(category__isnull=True)),
            ]
    

    Django 2.1 及更早版本

    在旧版本中,您需要通过迁移来执行此操作。首先创建一个新的空迁移文件:

    python manage.py makemigrations --empty yourappname
    

    然后,为每个索引添加一个适当的RunSQL 行:

    operations = [
        migrations.RunSQL("CREATE UNIQUE INDEX..."),
        migrations.RunSQL("CREATE UNIQUE INDEX..."),
    ]
    

    最后,运行migrate

    【讨论】:

    • 谢谢。我在文档中找不到这个。何时以及如何使用 sqlreverse_sql 参数?
    • @user4150760:sql 参数是创建索引的字符串,正如您在上面给出的。如果您希望能够回滚迁移,请同时提供 reverse_sql 参数。在这种情况下,这将是等效的 DROP INDEX ... 命令。 state_operations 参数不适用于此处。
    【解决方案2】:

    您可以像这样提供unique_together

    class Post(models.Model):
        title = models.CharField(max_length=200)
        blog = models.ForeignKey(Blog)
        category = models.ForeignKey(Category, null=True, blank=True)
    
    class Meta:
        unique_together = ("title", "blog", "category")
    

    类别的 NULL 将按照您的意愿工作,如果未设置,则标题/博客必须是唯一的。

    https://docs.djangoproject.com/en/1.8/ref/models/options/#unique-together

    【讨论】:

    • 您链接的文档既不支持也不反驳您所说的。某些(全部?)数据库从不等同于空值(即,一列空值满足对其的唯一约束),因此如果标题、博客或类别中的任何一个为空,则可以简单地满足 unique_together 子句。
    • unique_together 不用于条件索引,仅用于多键索引
    猜你喜欢
    • 1970-01-01
    • 2017-05-25
    • 2021-08-26
    • 2015-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-27
    • 1970-01-01
    相关资源
    最近更新 更多