Django ManyToMany through model/table indexing

For ManyToMany relationships Django will automatically create a trough model/table.

For example for:

class Magazine(models.Model):
    pass


class Article(models.Model):
    magazines = models.ManyToManyField('Magazine', related_name='articles')

there will be a Article.through manager and model. The associated table will have two separate indices for each of the columns. Two questions:

  1. How do I create a new index on both columns? I could run a raw SQL query to do it, but I wonder if there is a way to do something similar to how index_together, which is easy to maintain and track.
  2. Is there a reason why Django doesn't automatically add an index to both columns? In my test, I manually created the index and saw Postgres hit it a lot and gain performance.

If you want to make changes to the intermediate table used by the ManyToManyField, you can use the through argument on the field. This lets you manually define the intermediate model, to which you can add a multi-column index. Note that index_together has been deprecated in favor of indexes.

Here's a simple example of what it would look like to add a multi-column index to a manually defined intermediate table.

class Magazine(models.Model):
    pass


class Article(models.Model):
    magazines = models.ManyToManyField('Magazine', related_name='articles', through='MagazineArticle')


class MagazineArticle(models.Model):
    magazine = models.ForeignKey('Magazine')
    article = models.ForeignKey('Article')

    class Meta:
        indexes = [
            models.Index(fields=["magazine", "article"])
        [

I can't speak to why Django doesn't automatically create this index, but at least there's a simple way to add it!

Documentation:

ManyToManyField through argument

Indexes Meta option

To create a composite index on both columns:

You define your own through model

class Magazine(models.Model):
        pass

class Article(models.Model):
        magazines = models.ManyToManyField('Magazine', 
                                             through='MagazineArticle',
                                             related_name='articles')
            
        
class MagazineArticle(models.Model):
        article = models.ForeignKey(Article, on_delete=models.CASCADE)
        magazine = models.ForeignKey(Magazine, on_delete=models.CASCADE)
            
        class Meta:
            indexes = [
                    models.Index(fields=['article', 'magazine'])
                ]
        
        
Вернуться на верх