Database
 sql >> Base de données >  >> RDS >> Database

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

La gestion des migrations de bases de données est un grand défi dans tout projet logiciel. Heureusement, depuis 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 les changements dans les bases de données. Mais la flexibilité offerte par le cadre nécessitait certains compromis. Pour comprendre les limites des migrations Django, vous allez vous attaquer à un problème bien connu :créer un index dans Django sans temps d'arrêt.

Dans ce didacticiel, vous apprendrez :

  • Comment et quand 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 tutoriel de niveau intermédiaire est conçu pour les lecteurs déjà familiarisés avec les migrations Django. Pour une introduction à ce sujet, consultez Django Migrations :A Primer.

Bonus gratuit : Cliquez ici pour accéder gratuitement à des didacticiels et ressources Django supplémentaires que vous pouvez utiliser pour approfondir vos compétences en développement Web Python.


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

Un changement courant qui devient généralement nécessaire lorsque les données stockées par votre application augmentent consiste à ajouter un index. 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 UPDATE , INSERT , et DELETE , pendant la création de l'index.

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 mettra à jour le last_login champ 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 lorsqu'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 du temps. Plus il faut de temps pour créer l'index, plus le système est indisponible ou ne répond pas aux utilisateurs.

Certains fournisseurs de bases de données proposent 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 CONCURRENTLY mot-clé :

CREATE INDEX CONCURRENTLY ix ON table (column);

Dans Oracle, il y a un ONLINE option pour autoriser les opérations DML sur la table lors de la création de l'index :

CREATE INDEX ix ON table (column) ONLINE;

Lors de la génération des migrations, Django n'utilisera pas ces mots clés spéciaux. L'exécution de la migration telle quelle forcera la base de données à acquérir un verrou exclusif sur la table et empêchera les opérations DML pendant la création de l'index.

La création simultanée d'un index comporte certaines mises en garde. Il est important de comprendre à l'avance les problèmes spécifiques à votre backend de base de données. Par exemple, une mise en garde dans PostgreSQL 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 didacticiel, vous utiliserez les migrations Django pour créer un index sur une grande table, sans provoquer de temps d'arrêt.

Remarque : Pour suivre ce tutoriel, il est recommandé d'utiliser un backend PostgreSQL, Django 2.x et Python 3.

Il est également possible de suivre d'autres backends de base de données. Dans les endroits où les fonctionnalités SQL uniques à PostgreSQL sont utilisées, modifiez le SQL pour qu'il corresponde à votre backend de base de données.



Configuration

Vous allez utiliser une Sale inventée modèle dans une application appelée app . Dans une situation réelle, des modèles tels que Sale sont les tables principales de la base de données, et elles seront généralement très volumineuses et stockeront beaucoup de données :

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
    )
    charged_amount = models.PositiveIntegerField()

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

$ python manage.py makemigrations
Migrations for 'app':
  app/migrations/0001_initial.py
    - Create model Sale

$ python manage migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0001_initial... OK

Au bout d'un moment, le tableau des ventes devient très gros et les utilisateurs commencent à se plaindre de la lenteur. Lors de la surveillance de la base de données, vous avez remarqué que de nombreuses requêtes utilisent le 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 la modification suivante au modèle :

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
    )
    charged_amount = models.PositiveIntegerField()

Si vous exécutez cette migration telle quelle, Django créera l'index sur la table et il 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.

Dans 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. Cependant, sur de grands ensembles de données avec de nombreuses connexions simultanées, l'obtention d'un verrou et la création de l'index peuvent prendre un certain temps.

Dans les prochaines étapes, vous allez modifier les migrations créées par Django pour créer l'index sans provoquer de temps d'arrêt.



Fausse migration

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

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

$ python manage.py makemigrations --name add_index_fake
Migrations for 'app':
  app/migrations/0002_add_index_fake.py
    - Alter field sold_at on sale

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

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

Vous souhaitez créer l'index sans verrouiller la table, vous devez donc modifier la commande. Ajoutez le CONCURRENTLY mot-clé et exécuter dans la base de données :

app=# CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

CREATE INDEX

Notez que vous avez exécuté la commande sans le BEGIN et COMMIT les pièces. L'omission de ces mots clés exécutera les commandes 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 l'erreur suivante :

$ python manage.py migrate

Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake...Traceback (most recent call last):
  File "venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)

psycopg2.ProgrammingError: relation "app_sale_sold_at_b9438ae4" already exists

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 --fake flag lors de l'application de la migration :

$ python manage.py migrate --fake
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake... FAKED

Django n'a pas levé d'erreur cette fois. En fait, Django n'a pas vraiment appliqué de migration. Il l'a juste marqué comme exécuté (ou FAKED ).

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. Utilisez 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 truquées : Lorsque vous avez plusieurs migrations non appliquées, elles seront toutes truquées. Avant d'appliquer des migrations, il est important de s'assurer que seules les migrations que vous souhaitez simuler ne sont pas appliquées. Sinon, vous pourriez vous retrouver avec des incohérences. Une autre option consiste à spécifier la migration exacte que vous souhaitez 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. De plus, l'exécution de commandes directement dans une base de données de production est dangereuse et doit être évitée dans la mesure du 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.

Nettoyage

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

$ python manage.py migrate 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_fake... OK

Django a annulé les modifications apportées lors de la deuxième migration, il est donc désormais possible de supprimer également le fichier en toute sécurité :

$ rm app/migrations/0002_add_index_fake.py

Pour vous assurer que vous avez tout fait correctement, inspectez les migrations :

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

La migration initiale a été appliquée et il n'y a aucune migration non appliquée.



Exécuter Raw SQL 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 existe une meilleure solution.

Django fournit 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.

Tout d'abord, générez une nouvelle migration vide :

$ python manage.py makemigrations app --empty --name add_index_runsql
Migrations for '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

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

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

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

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

$ python manage.py migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_runsql... OK

Cela semble bon, mais il y a un problème. Essayons de générer à nouveau des migrations :

$ python manage.py makemigrations --name leftover_migration
Migrations for 'app':
  app/migrations/0003_leftover_migration.py
    - Alter field sold_at on sale

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

Nettoyage

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

$ rm app/migrations/0003_leftover_migration.py

Ensuite, répertoriez les migrations pour l'app application :

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

La troisième migration a disparu, mais la seconde est appliquée. Vous voulez 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 :

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_runsql...Traceback (most recent call last):

NotImplementedError: You cannot reverse this operation

Django est incapable d'inverser la migration.



Opération de migration inverse

Pour inverser une migration, Django exécute une action inverse pour chaque opération. Dans ce cas, l'inverse de l'ajout d'un index est de 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 checkout dans Git, vous pouvez inverser une migration si vous exécutez migrate à 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 supprimer 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 est incapable d'inverser le SQL brut par défaut. Comme Django n'a 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 contient doivent être réversibles. Il n'est pas possible d'inverser une partie d'une migration, donc une seule opération irréversible rendra toute la migration irréversible.

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

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

# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

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

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

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

Essayez maintenant d'inverser la migration :

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

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
 Unapplying app.0002_add_index_runsql... OK

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

La deuxième migration a été inversée et l'index a été supprimé par Django. Vous pouvez maintenant 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 situations 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='...',  # Your forward SQL here
    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 quand générer de nouvelles migrations.


Lorsque Django génère une nouvelle migration

Lors du processus de génération et d'application des migrations, Django se synchronise entre l'état de la base de données et 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 et la base de données, Django maintient 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 SQL spécifique au fournisseur qui peut ê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 agrège les opérations de toutes les migrations passées. 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'avez pas utilisé une opération de migration familière.

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



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

Étant donné que Django est incapable de créer l'index comme vous le souhaitez, vous souhaitez fournir votre propre SQL tout en faisant 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 est peu connue et doit être réservée à des cas particuliers comme celui-ci.

Il est beaucoup plus facile de modifier des migrations que de les écrire à partir de zéro, alors commencez par générer une migration de la manière habituelle :

$ python manage.py makemigrations --name add_index_separate_database_and_state

Migrations for 'app':
  app/migrations/0002_add_index_separate_database_and_state.py
    - Alter field sold_at on sale

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

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

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

    operations = [
        migrations.AlterField(
            model_name='sale',
            name='sold_at',
            field=models.DateTimeField(
                auto_now_add=True,
                db_index=True,
            ),
        ),
    ]

Django a généré un AlterField opération sur le champ sold_at . L'opération créera un index et mettra à 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 :

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

Ajoutez le CONCURRENTLY mot-clé à l'endroit approprié :

CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

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

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

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

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "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. état_opérations sont des opérations à appliquer sur l'état du modèle interne. Ils n'affectent pas la base de données.
  2. database_operations 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 de SeparateDatabaseAndState , c'est ce que vous voudrez généralement faire. Notez que le db_index=True argument est fourni au champ. Cette opération de migration permettra à Django de savoir qu'il existe un index sur le champ.

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

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

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state...Traceback (most recent call last):
  File "/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 83, in _execute
    return self.cursor.execute(sql)
psycopg2.InternalError: CREATE INDEX CONCURRENTLY cannot run inside a transaction block



Migrations non atomiques

En SQL, CREATE , DROP , ALTER , et TRUNCATE les opérations sont appelées langage de définition de données (DDL). Dans les bases de données prenant en charge le DDL transactionnel, telles que PostgreSQL, Django exécute les migrations à l'intérieur d'une transaction de base de données par défaut. Cependant, selon 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 atomic à False :

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

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

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "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 :

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state... OK

Vous venez d'exécuter la migration sans causer de temps d'arrêt.

Voici quelques problèmes à 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 conserver les opérations générées par Django dans state_operations et modifiez la sortie de sqlmigrate à utiliser dans database_operations .

  • Les migrations non atomiques ne peuvent pas être annulées en cas d'erreur : S'il y a une 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 réduire 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 SQL généré par Django est spécifique à la base de données utilisée dans le projet. Cela peut fonctionner avec d'autres backends de base de données, mais ce n'est pas garanti. Si vous devez prendre en charge plusieurs backends de base de données, vous devez apporter quelques ajustements à cette approche.



Conclusion

Vous avez commencé ce didacticiel avec une grande table et un problème. Vous vouliez rendre votre application plus rapide pour vos utilisateurs, et vous vouliez le faire sans leur causer de temps d'arrêt.

À 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 avez réussi à les surmonter à l'aide d'outils intégrés fournis par le framework de migration.

Dans ce didacticiel, vous avez appris ce qui suit :

  • Comment les migrations Django fonctionnent en interne à l'aide de l'état du modèle et de la base de données, et quand de nouvelles migrations sont générées
  • Comment exécuter du SQL personnalisé dans les migrations à l'aide de RunSQL action
  • Qu'est-ce que les migrations réversibles et comment créer un 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 le modèle et l'état de la base de données est un concept important. Une fois que vous l'avez compris et comment l'utiliser, vous pouvez surmonter de nombreuses limitations des opérations de migration intégrées. Certains cas d'utilisation qui me viennent à l'esprit incluent l'ajout d'un index qui a déjà été créé dans la base de données et la fourniture d'arguments spécifiques au fournisseur pour les commandes DDL.