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:
- 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. - 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:
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'])
]