pythondjangodjango-modelsdatabase-indexes

Django: Does unique_together imply db_index=True in the same way that ForeignKey does?


A field on a model, foo = models.ForeignKey(Foo) will automatically add a database index for the column, in order to make look-ups faster. That's good and well, but Django's docs don't state whether the fields in a model-meta's unique_together receive the same treatment. I happen to have a model in which one char field which is listed in unique_together requires an index for quick lookups. I know that it won't hurt anything to add a duplicate db_index=True in the field definition, but I'm curious.


Solution

  • unique_together does not automatically add indexes for each field included in the list.

    The new versions of Django suggest using Index & constraint meta options instead:

    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/

    And an example model from an open source project:

    class GroupResult(models.Model): """Task Group result/status."""
    
    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']),
        ]