Django:unique_together是否意味着与ForeignKey一样的db_index=True?

32
一个模型中的字段 foo = models.ForeignKey(Foo) 会自动为该列添加数据库索引,以加快查找速度。这很好,但是 Django 的文档没有说明模型元数据中的 unique_together 字段是否也接受同样的处理。我恰好有一个模型,其中一个列在 unique_together 中列出,需要索引以进行快速查找。我知道在字段定义中添加重复的 db_index=True 不会有任何问题,但我很好奇。
5个回答

38

如果有人在这里想知道他们是否需要在unique_together之外再添加一个index_together才能获得索引的性能优势,那么对于Postgres来说,答案是不需要,它们在功能上相同


2
非常好的补充,汤姆。谢谢你。 - orokusaki
感谢提供额外的信息!我尝试修复损坏的链接,但无法找到有效的链接。您能否请修复链接或将其删除? - vishes_shell

14

如果 unique_together 添加索引,那么它将是一个多列索引。

如果您想单独对其中一列创建索引,我认为您需要在字段定义中指定db_index=True


1
我可以从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) - Hassek

8
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/ 以下是开源项目中的一个示例模型:
class 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']),
    ]

1
嗨,感谢您提供这个新答案。新的约束 API 真是太棒了。我在您的回答上方添加了一些内容,以直接回答原始问题,因此我可以将其作为新的正确答案,因为现在它更加相关。 - orokusaki

7
Django 1.5及以上版本中,您可以使用{Model}.Meta.index_together类属性。如果您有两个名为foobar的字段,则应添加:
class Meta(object):
    index_together = unique_together = [
        ['foo', 'bar']
    ]

如果你只有一组唯一字段,你可以使用一个一维 iterable 对象来定义 unique_together。然而,文档 没有说明同样适用于 index_together
下面这种写法也是可以的:
class Meta(object):
    unique_together = 'foo', 'bar'
    index_together = [
        ['foo', 'bar']
    ]

然而,文档不支持此操作:

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

2
index_together与2个字段不同于相同的2个字段的db_index=Trueindex_together允许您将多个索引合并为1个,以便在数据库扫描单个索引时可以比较诸如WHERE a = 5 AND b = 10之类的子句。 - orokusaki

1
根据文档,它只会在数据库级别强制执行唯一性。我认为通常使字段唯一并不意味着它具有索引。虽然您也可以在数据库级别上检查索引是否存在。但是所有指示都表明它不存在。

我的问题中的“意味着”部分涉及Django特定的行为。在字段定义中,models.ForeignKey默认设置了db_index=True。我认为,一个unique=True字段也会创建一个索引(数据库级别),这就是唯一性检查快速运行的方式(如果我错了,请纠正我)。 - orokusaki
我认为这取决于你的数据库引擎如何处理唯一约束条件。如果我没记错,大多数数据库会设置索引。但这和Django无关。 - Torsten Engelbrecht
实际上,models.ForeignKey 部分与 Django 有关,而不是 unique=True。我提到 unique=True 只是作为一个旁注,因为你的回答中涉及了 使字段唯一并不会... 的部分。 - orokusaki
2
unique_together 在数据库级别上也会被执行,而不是在 Django 中。假设您的数据库引擎自动支持唯一列上的索引,则将设置该索引。如果不支持,则不会设置。这就是答案。此外,浏览 Django 代码并没有表明他们会在 unique_together 上设置数据库索引。 - Torsten Engelbrecht

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接