Django:unique_together 是不是以与 ForeignKey 相同的方式暗示 db_index=True?

Posted

技术标签:

【中文标题】Django:unique_together 是不是以与 ForeignKey 相同的方式暗示 db_index=True?【英文标题】:Django: Does unique_together imply db_index=True in the same way that ForeignKey does?Django:unique_together 是否以与 ForeignKey 相同的方式暗示 db_index=True? 【发布时间】:2011-12-11 10:04:32 【问题描述】:

模型上的字段,foo = models.ForeignKey(Foo) 会自动为该列添加数据库索引,以加快查找速度。这很好,但是 Django 的文档没有说明模型元 unique_together 中的字段是否接受相同的处理。我碰巧有一个模型,其中unique_together 中列出的一个字符字段需要一个索引以进行快速查找。我知道在字段定义中添加重复的db_index=True 不会有什么坏处,但我很好奇。

【问题讨论】:

【参考方案1】:

unique_together 不会自动为列表中包含的每个字段添加索引。

新版本的 Django 建议使用索引和约束元选项:

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

https://docs.djangoproject.com/en/3.2/ref/models/options/#index-together

https://docs.djangoproject.com/en/dev/ref/models/indexes/

还有一个来自开源项目的示例模型:

类 GroupResult(models.Model): """任务组结果/状态。"""

group_id = models.CharField(
    max_length=getattr(
        settings,
        "DJANGO_CELERY_RESULTS_TASK_ID_MAX_LENGTH",
        255
    ),
    unique=True,
    verbose_name=_("Group ID"),
    help_text=_("Celery ID for the Group that was run"),
)
date_created = models.DateTimeField(
    auto_now_add=True,
    verbose_name=_("Created DateTime"),
    help_text=_("Datetime field when the group result was created in UTC"),
)
date_done = models.DateTimeField(
    auto_now=True,
    verbose_name=_("Completed DateTime"),
    help_text=_("Datetime field when the group was completed in UTC"),
)
content_type = models.CharField(
    max_length=128,
    verbose_name=_("Result Content Type"),
    help_text=_("Content type of the result data"),
)
content_encoding = models.CharField(
    max_length=64,
    verbose_name=_("Result Encoding"),
    help_text=_("The encoding used to save the task result data"),
)
result = models.TextField(
    null=True, default=None, editable=False,
    verbose_name=_('Result Data'),
    help_text=_('The data returned by the task.  '
                'Use content_encoding and content_type fields to read.'))

def as_dict(self):
    return 
        'group_id': self.group_id,
        'result': self.result,
        'date_done': self.date_done,
    

def __str__(self):
    return f'<Group: self.group_id>'

objects = managers.GroupResultManager()

class Meta:
    """Table information."""

    ordering = ['-date_done']

    verbose_name = _('group result')
    verbose_name_plural = _('group results')

    indexes = [
        models.Index(fields=['date_created']),
        models.Index(fields=['date_done']),
    ]

【讨论】:

您好,感谢您的新回答。新的约束 API 很棒。我在您的答案上方添加了一些内容以直接回答原始问题,这样我就可以将其作为新的正确答案,因为它现在更加相关。【参考方案2】:

如果有人来这里想知道除了unique_together 是否还需要index_together 来获得索引的性能优势,Postgres 的答案是no,他们是functionally the same.

【讨论】:

出色的补充,汤姆。谢谢你。 感谢您提供更多信息!试图修复断开的链接,但找不到有效的链接。请您修复链接或完全删除它吗?【参考方案3】:

Django 1.5 及更高版本 中,您可以使用Model.Meta.index_together 类属性。如果您有两个名为 foobar 的字段,您将添加:

class Meta(object):
    index_together = unique_together = [
        ['foo', 'bar']
    ]

如果您只有一组唯一字段,则可以为 unique_together 使用一维迭代。但是,the documentation 并不表示同样适用于index_together

这样也可以:

class Meta(object):
    unique_together = 'foo', 'bar'
    index_together = [
        ['foo', 'bar']
    ]

但是,文档不支持

class Meta(object):
    unique_together = 'foo', 'bar'
    index_together = 'foo', 'bar'

【讨论】:

index_together 有 2 个字段与 db_index=True 有相同的 2 个字段不同。 index_together 允许您将多个索引合并为 1,以便在数据库扫描单个索引时可以比较像 WHERE a = 5 AND b = 10 这样的子句。【参考方案4】:

如果unique_together 确实添加了索引,它将是一个多列索引。

如果您希望单独索引其中一列,我相信您需要在字段定义中指定db_index=True

【讨论】:

我可以从 postgresql 确认这一点,这里有一个来自 postgresql 控制台的 uniquere_toguether 索引示例:public | registration_something | registration_something_field_5c0b3bdb9d08b87c_uniq | | CREATE UNIQUE INDEX registration_something_field_5c0b3bdb9d08b87c_uniq ON registration_something USING btree (field1, field2)【参考方案5】:

根据the docs,它只会在数据库级别强制执行唯一性。我认为通常使字段唯一并不意味着它具有索引。尽管如果索引存在,您也可以简单地检查数据库级别。尽管没有,但一切都表明了。

【讨论】:

我的问题的“暗示”部分与 Django 特定的行为有关。 models.ForeignKey 在字段定义中默认设置 db_index=True。我也相信unique=True 字段还会创建一个索引(数据库级别),这就是唯一检查如何快速运行(请纠正我,如果我错了)。 我认为这取决于您的数据库引擎如何处理 UNIQUE 约束。如果我是对的,大多数人都会设置一个索引。不过这与 Django 无关。 确实,models.ForeignKey 部分与 Django 有关,而不是 unique=True。我提到了unique=True 作为旁白,因为你的答案中有making a field unique does not... 部分。 unique_together 也在数据库级别而不是在 Django 中强制执行。假设您的数据库引擎确实自动支持唯一列上的索引,则将设置索引。如果不是,那就不是。这就是你的答案。此外,浏览 Django 的代码并不表示他们会在 unique_together 上设置数据库索引。

以上是关于Django:unique_together 是不是以与 ForeignKey 相同的方式暗示 db_index=True?的主要内容,如果未能解决你的问题,请参考以下文章

UniqueConstraint 与 unique_together 之间的区别 - Django 2.2?

如何在 django 中使用 Uniqueconstraints 函数创建 unique_together 索引?

约束 unique_together 可能与 django 类中的唯一字段冲突

Django 数据库模型“unique_together”不起作用?

如何在 django 的核心模型中添加“unique_together”约束?

django rest 框架:内容类型 unique_together 和序列化