Comment créer un index dans Django sans temps d'arrêt – Real Python

By | avril 10, 2019

python pour débutant

La gestion des migrations de bases de données est un défi de taille pour tout projet logiciel. Heureusement, à compter de la version 1.7, Django est livré avec un framework de migration intégré. Le cadre est très puissant et utile pour gérer le changement dans les bases de données. Mais la flexibilité fournie par le cadre a nécessité certains compromis. Pour comprendre les limites des migrations Django, vous allez aborder un problème bien connu: créer un index dans Django sans temps d'arrêt.

Dans ce tutoriel, vous apprendrez:

  • Quand et comment Django génère de nouvelles migrations
  • Comment inspecter les commandes générées par Django pour exécuter les migrations
  • Comment modifier en toute sécurité les migrations pour répondre à vos besoins

Ce didacticiel de niveau intermédiaire est destiné aux lecteurs déjà familiarisés avec les migrations Django. Pour une introduction à ce sujet, consultez Django Migrations: A Primer.

Le problème de la création d'un index dans les migrations Django

L'ajout d'un index est un changement courant qui devient généralement nécessaire lorsque les données stockées par votre application augmentent. Les index sont utilisés pour accélérer les requêtes et rendre votre application rapide et réactive.

Dans la plupart des bases de données, l'ajout d'un index nécessite un verrou exclusif sur la table. Un verrou exclusif empêche les opérations de modification de données (DML) telles que METTRE À JOUR, INSÉRER, et EFFACER, alors que l’index est créé.

Les verrous sont obtenus implicitement par la base de données lors de l'exécution de certaines opérations. Par exemple, lorsqu'un utilisateur se connecte à votre application, Django met à jour le Dernière connexion domaine dans le auth_user table. Pour effectuer la mise à jour, la base de données devra d'abord obtenir un verrou sur la ligne. Si la ligne est actuellement verrouillée par une autre connexion, vous pouvez obtenir une exception de base de données.

Le verrouillage d’une table peut poser problème s’il est nécessaire de garder le système disponible pendant les migrations. Plus la table est grande, plus la création de l'index peut prendre longtemps. Plus la création de l'index est longue, plus le système est indisponible ou ne répond pas aux utilisateurs.

Certains fournisseurs de base de données offrent un moyen de créer un index sans verrouiller la table. Par exemple, pour créer un index dans PostgreSQL sans verrouiller une table, vous pouvez utiliser le De façon concurrente mot-clé:

CRÉER INDICE De façon concurrente ix SUR table (colonne)

Dans Oracle, il existe un En ligne option pour autoriser les opérations DML sur la table pendant la création de l'index:

CRÉER INDICE ix SUR table (colonne) En ligne;

Lors de la génération de migrations, Django n'utilisera pas ces mots-clés spéciaux. L'exécution de la migration en l'état fera en sorte que la base de données acquiert un verrou exclusif sur la table et empêche les opérations DML pendant la création de l'index.

La création simultanée d'un index présente certaines réserves. Il est important de comprendre à l’avance les problèmes spécifiques à votre base de données. Par exemple, dans PostgreSQL, un avertissement est que la création simultanée d'un index prend plus de temps car elle nécessite une analyse de table supplémentaire.

Dans ce tutoriel, vous utiliserez les migrations Django pour créer un index sur une table volumineuse, sans provoquer de temps mort.

Installer

Vous allez utiliser un maquillage Vente modèle dans une application appelée app. En situation réelle, des modèles tels que Vente Les tables principales de la base de données sont généralement très grandes et stockent beaucoup de données:

# models.py

de django.db importation des modèles

classe Vente(des modèles.Modèle):
    sold_at = des modèles.DateTimeField(
        auto_now_add=Vrai,
    )
    chargé_amount = des modèles.PositiveIntegerField()

Pour créer la table, générez la migration initiale et appliquez-la:

$ python manage.py makemigrations
Migrations pour 'app':
        app / migrations / 0001_initial.py
                - Créer un modèle de vente

$ python gérer migrer
Opérations à effectuer:
        Appliquer toutes les migrations: app
Migrations en cours:
        Application app.0001_initial ... OK

Après un certain temps, la table des ventes devient très grande et les utilisateurs commencent à se plaindre de la lenteur. Tout en surveillant la base de données, vous avez constaté que de nombreuses requêtes utilisent la sold_at colonne. Pour accélérer les choses, vous décidez que vous avez besoin d'un index sur la colonne.

Pour ajouter un index sur sold_at, vous apportez les modifications suivantes au modèle:

# models.py

de django.db importation des modèles

classe Vente(des modèles.Modèle):
    sold_at = des modèles.DateTimeField(
        auto_now_add=Vrai,
        db_index=Vrai,
    )
    chargé_amount = des modèles.PositiveIntegerField()

Si vous exécutez cette migration telle quelle, Django créera l'index sur la table et sera verrouillé jusqu'à ce que l'index soit terminé. La création d'un index sur une très grande table peut prendre un certain temps et vous souhaitez éviter les temps d'arrêt.

Sur un environnement de développement local avec un petit jeu de données et très peu de connexions, cette migration peut sembler instantanée. Toutefois, sur les grands ensembles de données comportant de nombreuses connexions simultanées, l'obtention d'un verrou et la création de l'index peuvent prendre un certain temps.

Dans les étapes suivantes, vous allez modifier les migrations créées par Django pour créer l'index sans provoquer de temps mort.

Fausse migration

La première approche consiste à créer l'index manuellement. Vous allez générer la migration, mais vous ne laisserez pas Django l'appliquer. Au lieu de cela, vous allez exécuter le code SQL manuellement dans la base de données, puis faire penser à Django que la migration est terminée.

Tout d'abord, générez la migration:

$ python manage.py makemigrations --name add_index_fake
Migrations pour 'app':
        app / migrations / 0002_add_index_fake.py
                - Alter field vendu_at en vente

Utilisez le sqlmigrate commande pour afficher le SQL que Django utilisera pour exécuter cette migration:

$ application python manage.py sqlmigrate 0002

COMMENCER;
-
- Alter field vendu_at en vente
-
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMETTRE;

Vous voulez créer l'index sans verrouiller la table, vous devez donc modifier la commande. Ajouter le De façon concurrente mot-clé et exécuter dans la base de données:

app = # CRÉER INDICE De façon concurrente "app_sale_sold_at_b9438ae4"
SUR "app_sale" ("sold_at")

CRÉER UN INDEX

Notez que vous avez exécuté la commande sans le COMMENCER et COMMETTRE les pièces. Si vous omettez ces mots-clés, les commandes seront exécutées sans transaction de base de données. Nous discuterons des transactions de base de données plus tard dans l'article.

Après avoir exécuté la commande, si vous essayez d'appliquer des migrations, vous obtiendrez le message d'erreur suivant:

$ python manage.py migrer

Opérations à effectuer:
        Appliquer toutes les migrations: app
Migrations en cours:
        Application app.0002_add_index_fake ... Traceback (dernier appel passé):
        Fichier "venv / lib / python3.7 / site-packages / django / db / backends / utils.py", ligne 85, dans _execute
                retourne self.cursor.execute (sql, params)

psycopg2.ProgrammingError: la relation "app_sale_sold_at_b9438ae4" existe déjà

Django se plaint que l’index existe déjà, il ne peut donc pas procéder à la migration. Vous venez de créer l'index directement dans la base de données, vous devez donc maintenant faire croire à Django que la migration a déjà été appliquée.

Comment simuler une migration

Django fournit un moyen intégré de marquer les migrations comme exécutées, sans les exécuter réellement. Pour utiliser cette option, définissez le --faux drapeau lors de l'application de la migration:

$ python manage.py migrer --fake
Opérations à effectuer:
        Appliquer toutes les migrations: app
Migrations en cours:
        Application de app.0002_add_index_fake ... FAKED

Django n'a pas généré d'erreur cette fois-ci. En fait, Django n’a pas vraiment appliqué de migration. Il vient de marquer comme exécuté (ou SIMULÉ).

Voici quelques problèmes à prendre en compte lorsque vous simulez des migrations:

  • La commande manuelle doit être équivalente au SQL généré par Django: Vous devez vous assurer que la commande que vous exécutez est équivalente au SQL généré par Django. Utilisation sqlmigrate pour produire la commande SQL. Si les commandes ne correspondent pas, vous risquez de vous retrouver avec des incohérences entre la base de données et l'état des modèles.

  • D'autres migrations non appliquées seront également faussées: Lorsque vous avez plusieurs migrations non appliquées, elles sont toutes simulées. Avant d’appliquer des migrations, il est important de s’assurer que seules les migrations que vous voulez simuler ne sont pas appliquées. Sinon, vous pourriez vous retrouver avec des incohérences. Une autre option consiste à spécifier la migration exacte à simuler.

  • Un accès direct à la base de données est requis: Vous devez exécuter la commande SQL dans la base de données. Ce n'est pas toujours une option. En outre, l’exécution de commandes directement dans une base de données de production est dangereuse et doit être évitée autant que possible.

  • Les processus de déploiement automatisés peuvent nécessiter des ajustements: Si vous avez automatisé le processus de déploiement (à l'aide de CI, de CD ou d'autres outils d'automatisation), vous devrez peut-être modifier le processus pour créer de fausses migrations. Ce n'est pas toujours souhaitable.

Nettoyer

Avant de passer à la section suivante, vous devez ramener la base de données à son état immédiatement après la migration initiale. Pour ce faire, revenez à la migration initiale:

$ python manage.py migrer 0001
Opérations à effectuer:
        Migration spécifique à la cible: 0001_initial, à partir de l'application
Migrations en cours:
        Le rendu des états du modèle ... DONE
        Désappliquer app.0002_add_index_fake ... OK

Django n’ayant pas appliqué les modifications apportées lors de la deuxième migration, il est désormais sûr de supprimer également le fichier:

$ rm app / migrations / 0002_add_index_fake.py

Pour vous assurer que tout a bien été, inspectez les migrations:

$ Application python manage.py showmigrations
app
 [X]    0001_initial

La migration initiale a été appliquée et il n'y a pas de migrations non appliquées.

Exécuter le SQL brut dans les migrations

Dans la section précédente, vous avez exécuté SQL directement dans la base de données et simulé la migration. Cela fait le travail, mais il y a une meilleure solution.

Django offre un moyen d'exécuter du SQL brut dans les migrations à l'aide de RunSQL. Essayons de l’utiliser au lieu d’exécuter la commande directement dans la base de données.

Commencez par générer une nouvelle migration vide:

$ application python manage.py makemigrations --empty --name add_index_runsql
Migrations pour 'app':
        app / migrations / 0002_add_index_runsql.py

Ensuite, éditez le fichier de migration et ajoutez un RunSQL opération:

# migrations / 0002_add_index_runsql.py

de django.db importation migrations, des modèles

classe Migration(migrations.Migration):
    atomique = Faux

    les dépendances = [[[[
        ('app', '0001_initial'),
    ]

    opérations = [[[[
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4"'
            'ON "app_sale" ("sold_at");',
        ),
    ]

Lorsque vous exécutez la migration, vous obtiendrez le résultat suivant:

$ python manage.py migrer
Opérations à effectuer:
        Appliquer toutes les migrations: app
Migrations en cours:
        Application app.0002_add_index_runsql ... OK

Cela a l'air bien, mais il y a un problème. Essayons de générer à nouveau des migrations:

$ python manage.py makemigrations --name leftover_migration
Migrations pour 'app':
        app / migrations / 0003_leftover_migration.py
                - Alter field vendu_at en vente

Django a généré à nouveau la même migration. Pourquoi a-t-il fait cela?

Nettoyer

Avant de pouvoir répondre à cette question, vous devez nettoyer et annuler les modifications apportées à la base de données. Commencez par supprimer la dernière migration. Elle n’a pas été appliquée. Vous pouvez donc supprimer en toute sécurité:

$ rm app / migrations / 0003_leftover_migration.py

Ensuite, listez les migrations pour le app app:

$ Application python manage.py showmigrations
app
 [X]    0001_initial
 [X]    0002_add_index_runsql

La troisième migration a disparu, mais la seconde est appliquée. Vous souhaitez revenir à l'état juste après la migration initiale. Essayez de revenir à la migration initiale comme vous l'avez fait dans la section précédente:

$ application de migration python manage.py 0001
Opérations à effectuer:
        Migration spécifique à la cible: 0001_initial, à partir de l'application
Migrations en cours:
        Le rendu des états du modèle ... DONE
        Désapplication de app.0002_add_index_runsql ... Traceback (dernier appel passé):

NotImplementedError: Vous ne pouvez pas inverser cette opération

Django est incapable d'inverser la migration.

Opération de migration inverse

Pour inverser une migration, Django exécute une action opposée pour chaque opération. Dans ce cas, l'inverse de l'ajout d'un index consiste à le supprimer. Comme vous l'avez déjà vu, lorsqu'une migration est réversible, vous pouvez la désappliquer. Tout comme vous pouvez utiliser check-out dans Git, vous pouvez inverser une migration si vous exécutez émigrer à une migration antérieure.

De nombreuses opérations de migration intégrées définissent déjà une action inverse. Par exemple, l'action inverse pour ajouter un champ consiste à supprimer la colonne correspondante. L'action inverse pour créer un modèle consiste à supprimer la table correspondante.

Certaines opérations de migration ne sont pas réversibles. Par exemple, il n'y a pas d'action inverse pour supprimer un champ ou un modèle, car une fois la migration appliquée, les données ont disparu.

Dans la section précédente, vous avez utilisé le RunSQL opération. Lorsque vous avez essayé d'inverser la migration, vous avez rencontré une erreur. Selon l'erreur, l'une des opérations de la migration ne peut pas être annulée. Django ne peut pas inverser le SQL brut par défaut. Django n'ayant aucune connaissance de ce qui a été exécuté par l'opération, il ne peut pas générer automatiquement une action opposée.

Comment rendre une migration réversible

Pour qu'une migration soit réversible, toutes les opérations qu’elle comporte doivent être réversibles. Il n’est pas possible d’inverser une partie de la migration, aussi une seule opération non réversible rendra-t-elle la migration dans son ensemble.

Faire un RunSQL opération réversible, vous devez fournir le code SQL à exécuter lorsque l'opération est inversée. Le reverse SQL est fourni dans le reverse_sql argument.

L'action opposée à l'ajout d'un index consiste à le supprimer. Pour rendre votre migration réversible, fournissez le reverse_sql supprimer l'index:

# migrations / 0002_add_index_runsql.py

de django.db importation migrations, des modèles

classe Migration(migrations.Migration):
    atomique = Faux

    les dépendances = [[[[
        ('app', '0001_initial'),
    ]

    opérations = [[[[
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4"'
            'ON "app_sale" ("sold_at");',

            reverse_sql='DROP INDEX "app_sale_sold_at_b9438ae4";',
        ),
    ]

Maintenant, essayez d'inverser la migration:

$ Application python manage.py showmigrations
app
 [X]    0001_initial
 [X]    0002_add_index_runsql

$ application de migration python manage.py 0001
Opérations à effectuer:
        Migration spécifique à la cible: 0001_initial, à partir de l'application
Migrations en cours:
        Le rendu des états du modèle ... DONE
    Désappliquer app.0002_add_index_runsql ... OK

$ Application python manage.py showmigrations
app
 [X]    0001_initial
 [ ]    0002_add_index_runsql

La deuxième migration a été inversée et l’index a été abandonné par Django. Maintenant, vous pouvez supprimer le fichier de migration en toute sécurité:

$ rm app / migrations / 0002_add_index_runsql.py

C’est toujours une bonne idée de fournir reverse_sql. Dans les cas où l'inversion d'une opération SQL brute ne nécessite aucune action, vous pouvez marquer l'opération comme réversible à l'aide de la sentinelle spéciale. migrations.RunSQL.noop:

migrations.RunSQL(
    sql='...',  # Votre SQL suivant ici
    reverse_sql=migrations.RunSQL.noop,
),

Comprendre l'état du modèle et l'état de la base de données

Lors de votre précédente tentative de création manuelle de l'index à l'aide de RunSQL, Django a généré la même migration encore et encore, même si l’index a été créé dans la base de données. Pour comprendre pourquoi Django a fait cela, vous devez d’abord comprendre comment Django décide de générer de nouvelles migrations.

Quand Django génère une nouvelle migration

Au cours du processus de génération et d’application des migrations, Django synchronise l’état de la base de données avec l’état des modèles. Par exemple, lorsque vous ajoutez un champ à un modèle, Django ajoute une colonne à la table. Lorsque vous supprimez un champ du modèle, Django supprime la colonne de la table.

Pour synchroniser les modèles avec la base de données, Django conserve un état qui représente les modèles. Pour synchroniser la base de données avec les modèles, Django génère des opérations de migration. Les opérations de migration se traduisent par un code SQL spécifique au fournisseur pouvant être exécuté dans la base de données. Lorsque toutes les opérations de migration sont exécutées, la base de données et les modèles doivent être cohérents.

Pour obtenir l'état de la base de données, Django regroupe les opérations de toutes les migrations antérieures. Lorsque l'état agrégé des migrations n'est pas cohérent avec l'état des modèles, Django génère une nouvelle migration.

Dans l'exemple précédent, vous avez créé l'index à l'aide de SQL brut. Django ne savait pas que vous aviez créé l'index car vous n'aviez pas utilisé d'opération de migration familière.

Lorsque Django a agrégé toutes les migrations et les a comparées avec l'état des modèles, il a constaté qu'un index était manquant. C'est pourquoi, même après avoir créé l'index manuellement, Django pensait toujours qu'il manquait et générait une nouvelle migration pour celui-ci.

Comment séparer la base de données et l'état dans les migrations

Étant donné que Django ne peut pas créer l'index comme vous le souhaitez, vous souhaitez fournir votre propre code SQL tout en laissant savoir à Django que vous l'avez créé.

En d'autres termes, vous devez exécuter quelque chose dans la base de données et fournir à Django l'opération de migration pour synchroniser son état interne. Pour ce faire, Django nous fournit une opération de migration spéciale appelée SeparateDatabaseAndState. Cette opération n'est pas bien connue et devrait être réservée à des cas spéciaux tels que celui-ci.

Il est beaucoup plus facile d’éditer les migrations que de les écrire de toutes pièces. Commencez donc par générer une migration de la manière habituelle:

$ python manage.py makemigrations --name add_index_separate_database_and_state

Migrations pour 'app':
        app / migrations / 0002_add_index_separate_database_and_state.py
                - Alter field vendu_at en vente

Voici le contenu de la migration générée par Django, comme auparavant:

# migrations / 0002_add_index_separate_database_and_state.py

de django.db importation migrations, des modèles

classe Migration(migrations.Migration):

    les dépendances = [[[[
        ('app', '0001_initial'),
    ]

    opérations = [[[[
        migrations.AlterField(
            nom du modèle='vente',
            prénom='sold_at',
            champ=des modèles.DateTimeField(
                auto_now_add=Vrai,
                db_index=Vrai,
            ),
        ),
    ]

Django a généré un AlterField opération sur le terrain sold_at. L'opération va créer un index et mettre à jour l'état. Nous voulons conserver cette opération mais fournir une commande différente à exécuter dans la base de données.

Encore une fois, pour obtenir la commande, utilisez le SQL généré par Django:

$ application python manage.py sqlmigrate 0002
COMMENCER;
-
- Alter field vendu_at en vente
-
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMETTRE;

Ajouter le De façon concurrente mot-clé à l'endroit approprié:

CRÉER INDICE De façon concurrente "app_sale_sold_at_b9438ae4"
SUR "app_sale" ("sold_at")

Ensuite, éditez le fichier de migration et utilisez SeparateDatabaseAndState pour fournir votre commande SQL modifiée pour l'exécution:

# migrations / 0002_add_index_separate_database_and_state.py

de django.db importation migrations, des modèles

classe Migration(migrations.Migration):

    les dépendances = [[[[
        ('app', '0001_initial'),
    ]

    opérations = [[[[

        migrations.SeparateDatabaseAndState(

            state_operations=[[[[
                migrations.AlterField(
                    nom du modèle='vente',
                    prénom='sold_at',
                    champ=des modèles.DateTimeField(
                        auto_now_add=Vrai,
                        db_index=Vrai,
                    ),
                ),
            ],

            opérations_base_de_données=[[[[
                migrations.RunSQL(sql="" "
                                                                                CRÉER INDEX SIMULTANEMENT "app_sale_sold_at_b9438ae4"
                                                                                ON "app_sale" ("sold_at");
                                                                "" ", reverse_sql="" "
                                                                                DROP INDEX "app_sale_sold_at_b9438ae4";
                                                                "" "),
            ],
        ),

    ],

L'opération de migration SeparateDatabaseAndState accepte 2 listes d'opérations:

  1. state_operations sont des opérations à appliquer sur l'état du modèle interne. Ils n'affectent pas la base de données.
  2. opérations_base_de_données sont des opérations à appliquer à la base de données.

Vous avez conservé l'opération d'origine générée par Django dans state_operations. Lors de l'utilisation SeparateDatabaseAndState, c’est ce que vous voudrez habituellement faire. Notez que le db_index = True l'argument est fourni au champ. Cette opération de migration permettra à Django de savoir qu’il existe un index sur le terrain.

Vous avez utilisé le SQL généré par Django et ajouté le De façon concurrente mot-clé. Vous avez utilisé l'action spéciale RunSQL exécuter du SQL brut dans la migration.

Si vous essayez d'exécuter la migration, vous obtiendrez le résultat suivant:

$ application de migration python manage.py
Opérations à effectuer:
        Appliquer toutes les migrations: app
Migrations en cours:
        Application app.0002_add_index_separate_database_and_state ... Traceback (l'appel le plus récent en dernier):
        Fichier "/venv/lib/python3.7/site-packages/django/db/backends/utils.py", ligne 83, dans _execute
                retourne self.cursor.execute (sql)
psycopg2.InternalError: CREATE INDEX CONCURRENTLY ne peut pas s'exécuter dans un bloc de transaction

Migrations Non Atomiques

En SQL, CRÉER, LAISSEZ TOMBER, MODIFIER, et TRONQUER les opérations sont appelées Langage de définition de données (DDL). Dans les bases de données prenant en charge la DDL transactionnelle, telle que PostgreSQL, Django exécute les migrations dans une transaction de base de données par défaut. Cependant, conformément à l'erreur ci-dessus, PostgreSQL ne peut pas créer un index simultanément dans un bloc de transaction.

Pour pouvoir créer un index simultanément dans une migration, vous devez indiquer à Django de ne pas exécuter la migration dans une transaction de base de données. Pour ce faire, vous marquez la migration comme non atomique en définissant atomique à Faux:

# migrations / 0002_add_index_separate_database_and_state.py

de django.db importation migrations, des modèles

classe Migration(migrations.Migration):
    atomique = Faux

    les dépendances = [[[[
        ('app', '0001_initial'),
    ]

    opérations = [[[[

        migrations.SeparateDatabaseAndState(

            state_operations=[[[[
                migrations.AlterField(
                    nom du modèle='vente',
                    prénom='sold_at',
                    champ=des modèles.DateTimeField(
                        auto_now_add=Vrai,
                        db_index=Vrai,
                    ),
                ),
            ],

            opérations_base_de_données=[[[[
                migrations.RunSQL(sql="" "
                                                                                CRÉER INDEX SIMULTANEMENT "app_sale_sold_at_b9438ae4"
                                                                                ON "app_sale" ("sold_at");
                                                                "" ",
                reverse_sql="" "
                                                                                DROP INDEX "app_sale_sold_at_b9438ae4";
                                                                "" "),
            ],
        ),

    ],

Après avoir marqué la migration comme non atomique, vous pouvez exécuter la migration:

$ application de migration python manage.py
Opérations à effectuer:
        Appliquer toutes les migrations: app
Migrations en cours:
        Appliquer app.0002_add_index_separate_database_and_state ... OK

Vous venez d'exécuter la migration sans causer de temps mort.

Voici quelques points à prendre en compte lorsque vous utilisez SeparateDatabaseAndState:

  • Les opérations de base de données doivent être équivalentes aux opérations d'état: Les incohérences entre la base de données et l'état du modèle peuvent causer beaucoup de problèmes. Un bon point de départ est de garder les opérations générées par Django dans state_operations et éditer la sortie de sqlmigrate à utiliser dans opérations_base_de_données.

  • Les migrations non atomiques ne peuvent pas revenir en arrière en cas d'erreur: En cas d’erreur lors de la migration, vous ne pourrez pas revenir en arrière. Vous devrez soit annuler la migration, soit la terminer manuellement. C’est une bonne idée de limiter au minimum les opérations exécutées dans une migration non atomique. Si vous avez des opérations supplémentaires dans la migration, déplacez-les vers une nouvelle migration.

  • La migration peut être spécifique au fournisseur: Le code SQL généré par Django est spécifique au backend de base de données utilisé dans le projet. Cela peut fonctionner avec d'autres bases de données, mais cela n'est pas garanti. Si vous devez prendre en charge plusieurs moteurs de base de données, vous devez apporter quelques modifications à cette approche.

Conclusion

Vous avez commencé ce tutoriel avec une grande table et un problème. Vous vouliez rendre votre application plus rapide pour vos utilisateurs, et cela sans leur causer de temps mort.

À la fin du didacticiel, vous avez réussi à générer et à modifier en toute sécurité une migration Django pour atteindre cet objectif. Vous avez abordé différents problèmes en cours de route et vous avez réussi à les résoudre à l'aide des outils intégrés fournis par le cadre de migration.

Dans ce tutoriel, vous avez appris ce qui suit:

  • Comment les migrations Django fonctionnent en interne en utilisant l'état du modèle et de la base de données, et lorsque de nouvelles migrations sont générées
  • Comment exécuter un SQL personnalisé dans les migrations à l'aide du RunSQL action
  • Que sont les migrations réversibles et comment en tirer parti? RunSQL action réversible
  • Que sont les migrations atomiques et comment modifier le comportement par défaut en fonction de vos besoins?
  • Comment exécuter en toute sécurité des migrations complexes dans Django

La séparation entre l’état du modèle et celui de la base de données est un concept important. Une fois que vous l'aurez compris et utilisé, vous pourrez surmonter de nombreuses limitations des opérations de migration intégrées. Certains cas d'utilisation qui viennent à l'esprit incluent l'ajout d'un index déjà créé dans la base de données et la fourniture d'arguments spécifiques au fournisseur pour les commandes DDL.