Stefan Kögl
Stefan Kögl

Reputation: 4743

Django: How to migrate to class-based indexes

Django 1.11 introduced Class-based model indexes. What was previously defined as

class A(models.Model):
    class Meta:
        index_together = [
            ('foo', 'bar'),
        ]

Can now be defined as

class A(models.Model):
    class Meta:
        indexes = [
            models.Index(fields=['foo', 'bar']),
        ]

When I change to the new syntax for a model an run python manage.py makemigrations, it will create migrations like

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.AlterIndexTogether(
            name='a',
            index_together=set([]),
        ),
        migrations.AddIndex(
            model_name='a',
            index=models.Index(fields=['foo', 'bar'], name='app_a_f12345_idx'),
        ),
    ]

This migration will remove and re-create my indexes, which is something I'd like to avoid.

What is the recommended way for switching from the old to the new syntax? I could not find anything in the documentation.

Upvotes: 4

Views: 946

Answers (2)

Akaisteph7
Akaisteph7

Reputation: 6525

Instead of --fake, I wanted to use something that will automatically get applied to various databases for this specific migration only. So I ended up using a combination of AddIndex, RemoveIndex, RenameIndex, and SeparateDatabaseAndState as Django was treating this one index I had modified upstream as a new index.

# Generated by Django 4.2.18

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ("app_name", "00XX_auto_XXXX"),
    ]

    operations = [
        # Handle this operation a bit differently since old migrations had to be updated to get rid of `index_together` references
        migrations.SeparateDatabaseAndState(
            # What actually happens
            database_operations=[
                migrations.RenameIndex(
                    model_name="model_name",
                    new_name="model_xxx_idx",
                    old_fields=("field1", "field2"),
                ),
            ],
            # What we want Django to believe happened
            state_operations=[
                migrations.RemoveIndex(
                    model_name="model_name",
                    name="model_xxx_idx_old",
                ),
                migrations.AddIndex(
                    model_name="model_name",
                    index=models.Index(
                        fields=["field1", "field2"], name="model_xxx_idx"
                    ),
                ),
            ],
        ),
    ]

Upvotes: 0

Phil Krylov
Phil Krylov

Reputation: 530

You can edit the generated migration so that the index name matches your original index_together index name, then run manage.py migrate with --fake option.

Upvotes: 1

Related Questions