# Documentation Django 5.2.8

# Migrations

Les migrations sont la manière par laquelle Django propage des modifications que vous apportez à des modèles (ajout d’un champ, suppression d’un modèle, etc.) dans un schéma de base de données. Elles sont conçues pour être quasiment automatiques, mais vous aurez besoin de savoir quand créer les migrations, quand les exécuter, et les problèmes courants que vous pourriez rencontrer.

## Les commandes

Il y a plusieurs commandes utiles pour interagir avec les migrations et manipuler le schéma de base de données avec Django :

- `migrate`, qui est responsable de l’exécution et de l’annulation des migrations.

- `makemigrations`, qui est responsable de la création de nouvelles migrations en fonction des modifications que vous avez apportées aux modèles.

- `sqlmigrate`, qui affiche les instructions SQL correspondant à une migration.

- `showmigrations`, qui affiche la liste des migrations d’un projet ainsi que leur état.

Vous devez imaginer les migrations comme un système de contrôle de versions pour un schéma de base de données. `makemigrations` se charge de regrouper vos changements de modèle dans des fichiers de migration individuels - comme des commits - et `migrate` est chargé d’appliquer les changements à la base de données.

Les fichiers de migration pour chaque application sont stockés dans un répertoire « migrations » à l’intérieur de l’application, et sont conçus pour faire partie du code source et de la distribution de cette application. Les migrations sont créées une fois sur votre machine de développement, puis exécutées telles quelles sur les machines de vos collègues, les machines de tests, et finalement sur les machines de production.

Note

Pour chaque application, il est possible de redéfinir le nom du paquet qui contient les migrations en utilisant le réglage `MIGRATION_MODULES`.

Les migrations se déroulent toujours de la même manière lorsqu’elles sont appliquées sur un même ensemble de données et leurs résultats sont constants et répétables, ce qui signifie que ce que vous voyez dans le développement et les machines de test correspondra exactement à ce qui va se passer en production si les conditions d’exécution sont identiques.

Django crée des migrations pour tout changement dans les modèles ou les champs, même pour des options qui n’affectent pas la base de données, car pour lui la seule manière de reconstruire un champ correctement est d’avoir accès à l’historique de toutes les modifications ; vous pourriez avoir besoin de ces options lors de migrations de données ultérieures (par exemple, si vous avez défini des règles de validation particulières).

## Bases de données prises en charge

Les migrations sont prises en charge sur tous les moteurs de bases de données livrées avec Django, ainsi que les bases de données tierces si elles prennent en charge les modifications de schéma (effectuées par l’intermédiaire de la classe SchemaEditor).

Toutefois, certaines bases de données ont plus de fonctionnalités que d’autres quand il s’agit de migrations de schéma ; un certain nombre de mises en garde sont rapportées ci-dessous.

### PostgreSQL

Parmi ces bases de données, PostgreSQL est celle disposant du plus de capacités en terme de prise en charge de schéma.

### MySQL

MySQL ne crée pas de transactions pour les opérations de modification de schéma, ce qui signifie que si une migration ne parvient pas à s’appliquer, vous devrez défaire manuellement les modifications pour essayer de nouveau (il est impossible de revenir à un point de sauvegarde antérieur).

MySQL 8.0 a introduit des améliorations de performance significatives pour les opérations de structures de données, en les rendant plus efficaces et en réduisant le besoin de reconstructions complètes de tables. Cependant, il ne peut pas garantir une absence complète de verrous ou d’interruptions. Dans les situations où les verrous sont encore nécessaires, la durée de ces opérations sera proportionnelle au nombre de lignes concernées.

Enfin, MySQL a une taille limite relativement petite sur la taille combinée de toutes les colonnes qu’un index recouvre. Cela signifie que des index qui sont possibles sur d’autres bases de données ne pourront pas toujours être créés avec MySQL.

### SQLite

SQLite ne gère nativement que très peu d’opérations de modifications de schéma, Django tente donc de les émuler par :

- La création d’une nouvelle table pour le nouveau schéma

- La copie des données de l’une à l’autre

- La suppression de l’ancienne table

- Le renommage de la nouvelle table avec le nom de l’ancienne table

Ce processus fonctionne généralement bien, mais il peut être lent et comporter des anomalies. Il n’est pas recommandé d’utiliser et de faire migrer SQLite dans un environnement de production, sauf si vous êtes pleinement conscient des risques et des limites ; la prise en charge de Django de cette fonctionnalité est conçue pour permettre aux développeurs d’utiliser SQLite sur leurs machines en local pour développer des projets Django moins complexes sans avoir besoin d’une base de données complète.

## Procédures

Django peut créer les migrations à votre place. Modifiez vos modèles - par exemple en ajoutant un champ ou en supprimant un modèle - puis exécutez `makemigrations`:

    $ python manage.py makemigrations
    Migrations for 'books':
      books/migrations/0003_auto.py:
        ~ Alter field author on book

Les modèles sont analysés et comparés aux versions actuellement contenues dans les fichiers de migration. Puis, un nouveau jeu de migrations est rédigé. Il est chaudement conseillé de lire le résultat produit pour voir ce que `makemigrations` a détecté comme changements. Cette commande n’est pas parfaite, et pour des changements complexes, il se peut qu’elle n’ait pas détecté ce que vous attendiez.

Lorsque les nouveaux fichiers de migration sont créés, on peut les appliquer à la base de données pour s’assurer qu’ils fonctionnent correctement :

    $ python manage.py migrate
    Operations to perform:
      Apply all migrations: books
    Running migrations:
      Rendering model states... DONE
      Applying books.0003_auto... OK

Quand la migration a été appliquée, ajoutez la migration et les modifications des modèles dans votre système de gestion de versions par un seul « commit » ; de cette façon, lorsque d’autres développeurs (ou votre serveur de production) obtiennent le nouveau code, ils reçoivent en même temps les modifications des modèles et la migration qui leur correspond.

Si vous souhaitez attribuer un nom éloquent à une migration au lieu d’un nom généré automatiquement, vous pouvez utilisez l’option `makemigrations --name`:

    $ python manage.py makemigrations --name changed_my_model your_app_label

### Contrôle de versions

Comme les migrations sont stockées dans le système de gestion de versions, il peut arriver de temps à autre qu’un autre développeur enregistre une migration pour la même application en même temps que vous, ce qui aboutit à deux migrations préfixées du même numéro.

Soyez sans crainte, les numéros servent uniquement comme référence pour les développeurs, Django exige seulement que chaque migration soit nommée différemment. Les migrations définissent dans leur fichier les autres migrations dont elles dépendent, y compris les migrations précédentes de la même application, il est donc possible de détecter lorsqu’il y a deux nouvelles migrations pour la même application sans préférence d’ordre.

Lorsque ceci se produit, Django vous pose la question avec plusieurs options. S’il pense que c’est sans risque, il offre de fusionner automatiquement les deux migrations pour vous. Dans le cas contraire, il vous faudra modifier vous-même les migrations, mais ce n’est pas une opération difficile et elle est expliquée plus en détails dans Fichiers de migrations ci-dessous.

## Transactions

Avec les bases de données qui gèrent les transactions pour les instructions de définition de schéma (SQLite et PostgreSQL), toutes les opérations de migration s’exécuteront par défaut dans une transaction unique. Au contraire, si une base de données ne gère pas ces transactions DDL (par ex. MySQL, Oracle), alors toutes les opérations seront exécutées sans transaction.

Il est possible d’empêcher qu’une migration s’exécute dans une transaction en définissant l’attribut `atomic` à `False`. Par exemple

    from django.db import migrations

    class Migration(migrations.Migration):
        atomic = False

It’s also possible to execute parts of the migration inside a transaction using `atomic()` or by passing `atomic=True` to `RunPython`. See Migrations non atomiques for more details.

## Dépendances

Bien que les migrations sont spécifiques à chaque application, les tables et les relations déterminées par les modèles sont trop complexes pour être créées pour une application à la fois. Lorsque vous créez une migration qui nécessite qu’une autre opération soit exécutée, par exemple l’ajout d’une clé `ForeignKey` de l’application `livres` vers l’application `auteurs`, la migration résultante contiendra une dépendance sur une migration dans `auteurs`.

Cela signifie que quand vous exécutez les migrations, la migration de `auteurs` s’exécute en premier et crée la table référencée par la clé étrangère, puis la migration qui crée la colonne `ForeignKey` peut s’exécuter et créer la contrainte. Si cela ne se faisait pas ainsi, la migration essaierait de créer une colonne `ForeignKey` sans que la table référencée n’existe et la base de données signalerait une erreur.

Ce comportement de dépendances affecte la majorité des opérations de migration qui sont limitées à une seule application. La restriction à une seule application (que ce soit pour `makemigrations` ou pour `migrate`) est respectée dans la mesure du possible, mais ce n’est pas une garantie ; toute autre application concernée dans l’optique de la gestion des dépendances sera également impliquée dans l’opération.

Les applications sans migrations ne doivent pas posséder de relations (ForeignKey`, `ManyToManyField`, etc.) vers de applications avec migrations. Cela peut parfois marcher, mais Django ne le garantit aucunement.

### Dépendances interchangeables

django.db.migrations.swappable_dependency(value)

La fonction `swappable_dependency()` est utilisée dans les migrations pour déclarer les dépendances interchangeables sur les migrations de l’application du modèle inséré actuellement, dans la première migration de cette application. Par conséquent, le modèle inséré doit être créé lors de la migration initiale. L’argument `value` est une chaîne `étiquette_app.modèle` indiquant l’étiquette d’une application et le nom du modèle, par exemple `"monapp.MonModele"`.

En utilisant `swappable_dependency()`, vous informez le système des migrations que la migration dépend d’une autre migration qui définit un modèle interchangeable, donnant la possibilité de substituer le modèle par une implémentation différente dans le futur. Ceci est typiquement utilisé pour faire référence à des modèles sujets à personnalisation ou remplacement, tels que le modèle d’utilisateur personnalisé (`settings.AUTH_USER_MODEL`, qui contient par défaut `"auth.User"`) dans le système d’authentification de Django.

## Fichiers de migrations

Les migrations sont stockées sous forme de fichiers sur disque nommés « fichiers de migrations ». Ces fichiers sont en réalité des fichiers Python normaux avec une structure d’objets convenue, rédigés dans un style déclaratif.

Un fichier de migration simple ressemble à ceci :

    from django.db import migrations, models

    class Migration(migrations.Migration):
        dependencies = [("migrations", "0001_initial")]

        operations = [
            migrations.DeleteModel("Tribble"),
            migrations.AddField("Author", "rating", models.IntegerField(default=0)),
        ]

Lorsque Django charge un fichier de migration (sous forme de module Python), Django recherche une sous-classe de `django.db.migrations.Migration` nommée `Migration`. Il inspecte ensuite cet objet en cherchant quatre attributs, parmi lesquels deux sont utilisés la plupart du temps :

- `dependencies`, une liste de migrations dont celle-ci dépend.

- `operations`, une liste de classes `Operation` définissant ce que fait cette migration.

L’élément central, c’est les opérations ; il s’agit d’un ensemble d’instructions déclaratives indiquant à Django quelles sont les modifications de schéma qu’il doit effectuer. Django les analyse et construit une représentation en mémoire de tous ces changements de schéma pour toutes les applications, puis utilise cela pour générer le code SQL qui procédera aux modifications de schéma.

Cette structure en mémoire est également utilisée pour calculer les différences entre les modèles et l’état actuel des migrations ; Django parcourt toutes les modifications dans l’ordre en les appliquant à un ensemble de modèles en mémoire afin d’aboutir à l’état des modèles à l’instant de la dernière exécution de `makemigrations`. Il utilise ensuite ces modèles pour les comparer à ceux qui se trouvent dans les fichiers `models.py` afin de déterminer ce qui a changé.

Il est rarement nécessaire de devoir modifier des fichiers de migration à la main, mais il est tout à fait possible de les écrire manuellement en cas de besoin. Certaines des opérations les plus complexes ne sont pas détectables automatiquement et ne sont donc disponibles que si on les écrit manuellement ; il ne faut donc pas avoir peur de modifier les migrations en cas de nécessité.

### Champs personnalisés

Il n’est pas possible de modifier le nombre de paramètres positionnels dans un champ personnalisé déjà migré sans générer une exception `TypeError`. L’ancienne migration appellera la méthode `__init__` modifiée avec l’ancienne signature. Si donc vous avez besoin d’un nouveau paramètre, créez plutôt un paramètre nommé et ajoutez quelque chose comme `assert 'nom_du_paramètre' in kwargs` dans le constructeur.

### Gestionnaires de modèles

Si vous le souhaitez, il est possible de sérialiser les gestionnaires d’objets dans les migrations pour qu’ils soient disponibles lors des opérations `RunPython`. Cela peut se faire en définissant un attribut `use_in_migrations` sur la classe du gestionnaire :

    class MyManager(models.Manager):
        use_in_migrations = True

    class MyModel(models.Model):
        objects = MyManager()

Si vous utilisez la fonction `from_queryset()` pour générer dynamiquement une classe de gestionnaire, il est nécessaire d’hériter de la classe générée pour la rendre importable :

    class MyManager(MyBaseManager.from_queryset(CustomQuerySet)):
        use_in_migrations = True

    class MyModel(models.Model):
        objects = MyManager()

Veuillez vous référer aux notes sur les Modèles historiques dans les migrations pour connaître les implications que cela génère.

### Migrations initiales

Migration.initial

Les « migrations initiales » d’une application sont celles qui créent la première version des tables de l’application. Une application possède normalement une seule migration initiale, mais dans certains cas présentant des interdépendances de modèles complexes, elle peut en avoir deux ou même plus.

Les migrations initiales sont marquées avec un attribut `initial = True` sur la classe de migration. Si cet attribut est absent, une migration est tout de même considérée comme « initiale » s’il s’agit de la première migration de l’application (c’est-à-dire qu’elle ne présente aucune dépendance sur d’autres migrations de la même application).

Lorsque l’option `migrate --fake-initial` est utilisée, ces migrations initiales sont traitées spécialement. Pour une migration initiale qui crée une ou plusieurs tables (opérations `CreateModel`), Django vérifie que toutes ces tables existent déjà dans la base de données et considère alors la migration comme appliquée. De même, pour une migration qui ajoute un ou plusieurs champs (opérations `AddField`), Django vérifie que toutes les colonnes concernées existent déjà dans la base de données et considère la migration comme appliquée le cas échéant. Sans `--fake-initial`, les migrations initiales sont traitées exactement comme toutes les autres migrations.

### Cohérence de l’historique

Comme discuté précédemment, il peut être nécessaire de réconcilier manuellement des migrations lorsque deux branches de développement sont fusionnées. Lors de l’édition des dépendances de migration, il arrive qu’on crée involontairement un état historique incohérent où une migration a été appliquée mais pas certaines de ses dépendances. C’est une indication claire que les dépendances ne sont pas correctes, Django va donc refuser d’exécuter les migrations en d’en créer de nouvelles tant que ce n’est pas corrigé. Lors de l’utilisation de plusieurs bases de données, il est possible de faire appel à la méthode `allow_migrate()` des routeurs de base de données pour indiquer pour quelles bases de données `makemigrations` va contrôler l’historique.

## Ajout de migrations aux applications

Les nouvelles applications sont préconfigurées pour accepter les migrations, il est possible d’ajouter des migrations en exécutant `makemigrations` après avoir effectué un certain nombre de modifications.

Si l’application possède déjà des modèles et des tables de base de données et qu’elle n’a pas encore de migrations (par exemple parce que vous l’avez créée avec une version précédente de Django), vous allez devoir la convertir en vue de l’utilisation des migrations en exécutant :

    $ python manage.py makemigrations your_app_label

Cela va créer une nouvelle migration initiale pour l’application. Ensuite, lancez `python manage.py migrate --fake-initial`, et Django détectera qu’une migration initiale est présente et que les tables qu’il doit créer existent déjà ; il va alors marquer la migration comme déjà appliquée (sans l’option `migrate --fake-initial`, la commande produirait une erreur car les tables qu’elle essayerait de créer existent déjà).

Notez que cela ne fonctionne qu’à deux conditions :

- Les modèles n’ont pas été modifiés depuis la création des tables correspondantes. Pour que les migrations fonctionnent, vous devez d’abord créer la migration initiale, puis faire les modifications, car Django compare les modifications aux fichiers de migration, pas à la base de données.

- La base de données n’a pas été modifiée manuellement. Django n’est pas capable de détecter que la base de données ne correspond pas aux modèles, et au moment où les migrations essaieront de modifier ces tables, vous obtiendrez des erreurs.

## Inversion des migrations

Les migrations peuvent être inversées avec `migrate` en passant le numéro de la migration précédente. Par exemple, pour inverser la migration `books.0003`:

/ 

    $ python manage.py migrate books 0002
    Operations to perform:
      Target specific migration: 0002_auto, from books
    Running migrations:
      Rendering model states... DONE
      Unapplying books.0003_auto... OK

    ...\> py manage.py migrate books 0002
    Operations to perform:
      Target specific migration: 0002_auto, from books
    Running migrations:
      Rendering model states... DONE
      Unapplying books.0003_auto... OK

Si vous souhaitez défaire toutes les migrations appliquées d’une application, utilisez le terme `zero`:

/ 

    $ python manage.py migrate books zero
    Operations to perform:
      Unapply all migrations: books
    Running migrations:
      Rendering model states... DONE
      Unapplying books.0002_auto... OK
      Unapplying books.0001_initial... OK

    ...\> py manage.py migrate books zero
    Operations to perform:
      Unapply all migrations: books
    Running migrations:
      Rendering model states... DONE
      Unapplying books.0002_auto... OK
      Unapplying books.0001_initial... OK

Une migration est irréversible si elle contient au moins une opération irréversible. Si on tente d’inverser une telle migration, une exception `IrreversibleError` sera générée :

/ 

    $ python manage.py migrate books 0002
    Operations to perform:
      Target specific migration: 0002_auto, from books
    Running migrations:
      Rendering model states... DONE
      Unapplying books.0003_auto...Traceback (most recent call last):
    django.db.migrations.exceptions.IrreversibleError: Operation <RunSQL  sql='DROP TABLE demo_books'> in books.0003_auto is not reversible

    ...\> py manage.py migrate books 0002
    Operations to perform:
      Target specific migration: 0002_auto, from books
    Running migrations:
      Rendering model states... DONE
      Unapplying books.0003_auto...Traceback (most recent call last):
    django.db.migrations.exceptions.IrreversibleError: Operation <RunSQL  sql='DROP TABLE demo_books'> in books.0003_auto is not reversible

## Modèles historiques

Lorsque vous exécutez des migrations, Django se base sur des versions historiques des modèles stockées dans les fichiers de migration. Si vous rédigez du code Python en utilisant l’opération `RunPython` ou si vous avez des méthodes `allow_migrate` sur vos routeurs de base de données, vous devez utiliser ces versions historisées de modèles et non pas les importer directement.

Avertissement

Si vous importez des modèles directement plutôt que d’utiliser les modèles historisés, vos migrations peuvent fonctionner initialement, mais échoueront dans le futur lorsque vous essaierez de relancer des anciennes migrations (ce qui arrive habituellement lors d’une nouvelle installation qui rejoue toutes les migrations pour créer la base de données).

Cela signifie que les problèmes de modèles historisés peuvent ne pas apparaître immédiatement. Si vous rencontrez ce genre de problème, il est correct de modifier la migration pour utiliser les modèles historisés au lieu des importations directes et de valider ces modifications.

Comme il n’est pas possible de sérialiser du code Python arbitraire, ces modèles historiques ne posséderont pas de méthodes personnalisées que vous auriez pu définir. Ils auront cependant les mêmes champs, relations, gestionnaires (pour ceux qui possèdent l’attribut `use_in_migrations = True`) et options `Meta` (également historiques, pouvant donc être différents des éléments actuels).

Avertissement

Cela signifie que des méthodes `save()` personnalisées ne seront PAS appelées pour les objets manipulés dans les migrations, de la même manière que des constructeurs ou des méthodes d’instance personnalisées ne sont PAS accessibles. Planifiez avec précaution !

Les références à des fonctions dans les options de champ telles que `upload_to`, `limit_choices_to` et les déclarations de gestionnaire de modèle ayant l’attribut `use_in_migrations = True` sont sérialisées dans les migrations, ce qui fait que ces fonctions et classes doivent être conservées quelque part dans le code aussi longtemps que des migrations les référencent. Il s’agit également de conserver les champs de modèle personnalisés, car ils sont importés directement par les migrations.

De plus, les classes de base concrètes des modèles sont stockées sous forme de pointeurs, il faut donc toujours conserver ces classes de base quelque part aussi longtemps qu’une migration contient une référence vers elles. Le côté positif est que les méthodes et gestionnaires de ces classes de base héritent de façon normale, ce qui fait que si vous avez absolument besoin d’y avoir accès, une possibilité est de les déplacer dans une classe parente.

Pour supprimer d’anciennes références, vous pouvez fusionner les migrations ou, s’il n’y a pas trop de références, les copier dans les fichiers de migration.

## Considérations lors de la suppression de champs de modèles

Dans le même ordre d’idée que les considérations sur les « références aux fonctions historiques » dans la section précédente, la suppression de champs de modèles personnalisés d’un projet ou d’une application tierce posera un problème si ces champs sont référencés dans d’anciennes migrations.

Pour vous assister dans cette situation, Django fournit quelques attributs de champs de modèle qui peuvent aider à rendre obsolètes des champs de modèle à l’aide de l” infrastructure des contrôles systèmes.

Ajoutez l’attribut `system_check_deprecated_details` au champ de modèle concerné de cette manière :

    class IPAddressField(Field):
        system_check_deprecated_details = {
            "msg": (
                "IPAddressField has been deprecated. Support for it (except "
                "in historical migrations) will be removed in Django 1.9."
            ),
            "hint": "Use GenericIPAddressField instead.",  # optional
            "id": "fields.W900",  # pick a unique ID for your field.
        }

Après une période d’obsolescence de votre choix (deux ou trois versions principales pour les champs propres à Django), modifiez l’attribut `system_check_deprecated_details` en `system_check_removed_details` et mettez à jour le dictionnaire selon le modèle suivant :

    class IPAddressField(Field):
        system_check_removed_details = {
            "msg": (
                "IPAddressField has been removed except for support in "
                "historical migrations."
            ),
            "hint": "Use GenericIPAddressField instead.",
            "id": "fields.E900",  # pick a unique ID for your field.
        }

Il faut toujours conserver les méthodes de champ qui lui sont nécessaires pour fonctionner dans le contexte de migrations de base de données, telles que `__init__()`, `deconstruct()` et `get_internal_type()`. Conservez ce champ historisé aussi longtemps que des migrations le référençant existent encore. Par exemple, après avoir fusionné des migrations et effacé les anciennes, il est alors possible de supprimer complètement cet ancien champ.

## Migrations de données

En plus de modifier le schéma de base de données, les migrations peuvent aussi être utilisées pour modifier les données mêmes de la base de données, conjointement avec le schéma, si vous le souhaitez.

Les migrations qui modifient des données sont généralement appelées des « migrations de données » ; il est préférable d’en faire des migrations séparées, en parallèle des migrations de schéma.

Django ne sait pas générer automatiquement des migrations de données à votre place, comme il le fait pour les migrations de schéma, mais il n’est pas très compliqué de les écrire. Les fichiers de migration dans Django sont composés d’Operations, et la principale opération utilisée pour les migrations de données est `RunPython`.

Pour commencer, créez un fichier de migration vide qui constituera votre point de départ (Django place le fichier au bon endroit, suggère un nom et ajoute les dépendances pour vous) :

    python manage.py makemigrations --empty yourappname

Puis, ouvrez le fichier ; il devrait ressembler à quelque chose comme ceci :

    # Generated by Django A.B on YYYY-MM-DD HH:MM
    from django.db import migrations

    class Migration(migrations.Migration):
        dependencies = [
            ("yourappname", "0001_initial"),
        ]

        operations = []

Now, all you need to do is create a new function and have `RunPython` use it. `RunPython` expects a callable as its argument which takes two arguments - the first is an app registry that has the historical versions of all your models loaded into it to match where in your history the migration sits, and the second is a SchemaEditor, which you can use to manually effect database schema changes (but beware, doing this can confuse the migration autodetector).

Écrivons une migration qui remplit notre nouveau champ `name` avec les valeurs combinées de `first_name` et `last_name` (nous sommes retombés sur nos pieds et avons réalisé que tout le monde n’a pas forcément un nom et un prénom). Tout ce que nous avons à faire est d’utiliser le modèle historique et de parcourir chaque ligne :

    from django.db import migrations

    def combine_names(apps, schema_editor):
        # We can't import the Person model directly as it may be a newer
        # version than this migration expects. We use the historical version.
        Person = apps.get_model("yourappname", "Person")
        for person in Person.objects.all():
            person.name = f"{person.first_name} {person.last_name}"
            person.save()

    class Migration(migrations.Migration):
        dependencies = [
            ("yourappname", "0001_initial"),
        ]

        operations = [
            migrations.RunPython(combine_names),
        ]

Une fois que c’est fait, nous pouvons lancer normalement `python manage.py migrate` et la migration de données sera exécutée comme toute autre migration.

Il est possible de passer un second objet exécutable à `RunPython` pour exécuter toute logique adéquate dans le cas d’une migration inverse. Si cet objet est omis, la migration inverse générera une exception, le cas échéant.

### Accéder aux modèles d’autres applications

Lorsque vous écrivez une fonction appelée par `RunPython` et qui utilise des modèles issus d’applications autres que celle dans laquelle la migration est située, l’attribut `dependencies` de la migration doit inclure la dernière migration de chaque application qui est en cause, sinon vous pouvez obtenir une erreur semblable à : `LookupError: Aucune application installée pour 'myappname'` lorsque vous utilisez `apps.get_model()` pour essayer de récupérer le modèle dans la fonction appelée par `RunPython`.

Dans l’exemple suivant, nous avons une migration dans `app1` qui a besoin d’utiliser des modèles dans `app2`. Nous ne sommes pas préoccupés par les détails de `move_m1` mis à part que cette fonction aura besoin d’accéder à des modèles des deux applications. Par conséquent, nous avons ajouté une dépendance qui spécifie la dernière migration de `app2`

    class Migration(migrations.Migration):
        dependencies = [
            ("app1", "0001_initial"),
            # added dependency to enable using models from app2 in move_m1
            ("app2", "0004_foobar"),
        ]

        operations = [
            migrations.RunPython(move_m1),
        ]

### Migrations avancées

Si vous êtes intéressé aux opérations de migration plus avancées ou que vous voulez pouvoir écrire votre propre opération, consultez la référence des opérations de migration et le guide pratique sur l’écriture de migrations.

## Fusion de migrations

Il ne faut pas craindre de créer autant de migrations que nécessaire, ce n’est pas un problème. Le code de migration est optimisé pour traiter des centaines de migrations à la fois sans trop de lenteurs. Cependant, il peut arriver un moment où l’on souhaite réduire un grand nombre de migrations à juste quelques-unes, et c’est là que la fusion des migrations intervient.

La fusion est l’acte de réduire un ensemble existant de nombreuses migrations à une seule (ou parfois quelques-unes) qui représente toujours les mêmes changements.

Django opère cela en prenant toutes les migrations existantes, extrayant leurs opérations en les plaçant toutes à la suite, puis exécutant un optimiseur pour essayer de réduire au maximum cette liste d’opérations. Par exemple, il sait que `CreateModel` et `DeleteModel` s’annulent l’une l’autre et que `AddField` peut être intégrée dans `CreateModel`.

Une fois que la suite d’opérations a été réduite au minimum, Django écrit le résultat dans une nouvel ensemble de fichiers de migration. Ce nombre minimum dépend de la quantité de dépendances relatives entre les modèles et de la présence d’opérations `RunSQL` ou `RunPython` (qui ne peuvent pas subir d’optimisation, sauf si elles sont marquées comme `elidable`).

These files are marked to say they replace the previously-squashed migrations, so they can coexist with the old migration files, and Django will intelligently switch between them depending on where you are in the history. If you’re still part-way through the set of migrations that you squashed, it will keep using them until it hits the end and then switch to the squashed history, while new installs will use the new squashed migration and skip all the old ones.

Cela permet de fusionner des migrations sans perturber des systèmes actuellement en production qui ne sont pas encore totalement à jour. Le processus recommandé est de fusionner, de conserver les anciens fichiers, de valider et publier le résultat, d’attendre jusqu’à ce que tous les systèmes soient mis à jour avec la nouvelle version (ou dans le cas d’une application réutilisable, s’assurer que les utilisateurs mettent à jour en suivant les versions sans en sauter), puis de supprimer les anciens fichiers, de valider et de publier une seconde version.

La commande qui se charge de tout ceci est `squashmigrations`. Transmettez-lui l’étiquette d’application et le nom de la migration jusqu’à laquelle vous souhaitez fusionner, et elle se mettra au travail :

    $ ./manage.py squashmigrations myapp 0004
    Will squash the following migrations:
     - 0001_initial
     - 0002_some_change
     - 0003_another_change
     - 0004_undo_something
    Do you wish to proceed? [y/N] y
    Optimizing...
      Optimized from 12 operations to 7 operations.
    Created new squashed migration /home/andrew/Programs/DjangoTest/test/migrations/0001_squashed_0004_undo_something.py
      You should commit this migration but leave the old ones in place;
      the new migration will be used for new installs. Once you are sure
      all instances of the codebase have applied the migrations you squashed,
      you can delete them.

Utilisez l’option `squashmigrations --squashed-name` si vous souhaitez définir le nom de la migration fusionnée plutôt que d’utiliser le nom généré automatiquement.

Sachez que les interdépendances de modèles Django peuvent devenir vraiment complexes et la fusion peut aboutir à des migrations qui ne peuvent pas être exécutées ; il peut soit y avoir un problème d’optimisation (auquel cas vous pouvez essayer une nouvelle fois avec l’option `--no-optimize`, mais il serait aussi judicieux de signaler le problème), soit un problème d’erreur `CircularDependencyError`, et dans ce cas, vous pouvez le résoudre manuellement.

Pour résoudre manuellement une erreur `CircularDependencyError`, séparez l’une des clés étrangères de la boucle de dépendances circulaires dans une migration distincte, puis déplacez la dépendance sur l’autre application qui la contient. Si vous hésitez, examinez comment `makemigrations` s’occupe de ce problème lorsqu’on lui demande de créer de toutes nouvelles migrations à partir de vos modèles. Dans une version future de Django, `squashmigrations` sera mise à jour pour qu’elle puisse résoudre ces erreurs par elle-même.

Après avoir fusionné les migrations, ajoutez la migration résultante en parallèle à celles qu’elle remplace et distribuez cette modification à toutes les instances en production de votre projet, en prenant soin d’exécuter chaque fois `migrate` pour enregistrer la modification dans la base de données.

You can then transition the squashed migration to a normal migration by:

- supprimant tous les fichiers de migration qu’elle remplace ;

- mettant à jour toutes les migrations qui dépendent des migrations supprimées pour les faire dépendre de la migration fusionnée ;

- enlevant l’attribut `replaces` dans la classe `Migration` de la migration fusionnée (c’est ce qui permet à Django de savoir qu’il s’agit d’une migration fusionnée).

Note

You can squash squashed migrations themselves without transitioning to normal migrations, which might be useful for situations where every environment has not yet run the original squashed migration set. But in general it is better to transition squashed migrations to normal migrations to be able to clean up older migration files.

Nettoyage des références aux migrations supprimées

S’il est probable que vous puissiez réutiliser le nom d’une migration supprimée dans le futur, vous devriez supprimer les références à cette migration dans la table des migrations de Django avec l’option `migrate --prune`.

Changed in Django 6.0:

Support for squashing squashed migrations was added.

## Sérialisation de valeurs

Les migrations sont des fichiers Python contenant les anciennes définitions de vos modèles. Pour pouvoir les écrire, Django doit donc prendre l’état actuel des modèles et les sérialiser dans un fichier.

Bien que Django puisse sérialiser la plupart des objets, il y en a certains qui ne peuvent simplement pas être sérialisés en une représentation Python valide. Il n’existe pas de standard Python permettant à une valeur d’être retransformée en code (`repr()` ne fonctionne qu’avec des valeurs basiques et ne définit pas de chemins d’importation).

Django est capable de sérialiser ce qui suit :

- `int`, `float`, `bool`, `str`, `bytes`, `None`, `NoneType`

- `list`, `set`, `tuple`, `dict`, `range`

- instances `datetime.date`, `datetime.time` et `datetime.datetime` (y compris celles qui contiennent un fuseau horaire)

- `zoneinfo.ZoneInfo` instances

- instances `decimal.Decimal`

- instances `enum.Enum` et `enum.Flag`

- instances `uuid.UUID`

- `functools.partial()` and `functools.partialmethod` instances which have serializable `func`, `args`, and `keywords` values

- Pure and concrete path objects from `pathlib`. Concrete paths are converted to their pure path equivalent, e.g. `pathlib.PosixPath` to `pathlib.PurePosixPath`

- `os.PathLike` instances, e.g. `os.DirEntry`, which are converted to `str` or `bytes` using `os.fspath()`

- `LazyObject` instances which wrap a serializable value

- Enumeration types (e.g. `TextChoices` or `IntegerChoices`) instances

- tous les champs Django

- toute référence de fonction ou de méthode (par ex. `datetime.datetime.today`) (doit être définie au niveau principal du module)

  - Les fonctions peuvent être décorées si elles sont enveloppées correctement, c’est-à-dire avec `functools.wraps()`

  - Les décorateurs `functools.cache()` sont `functools.lru_cache()` sont explicitement pris en charge

- méthodes non liées utilisées depuis l’intérieur du corps de la classe

- toute référence de classe (doit être définie au niveau principal du module)

- tout objet comportant une méthode `deconstruct()` (voir ci-dessous)

Changed in Django 6.0:

Serialization support for `zoneinfo.ZoneInfo` instances was added.

Django ne peut pas sérialiser :

- Les classes imbriquées

- Des instances de classe arbitraires (par ex. `MaClasse(4.3, 5.7)`)

- Les fonctions lambdas

### Sérialiseurs personnalisés

Vous pouvez sérialiser d’autres types en écrivant un sérialiseur personnalisé. Par exempl, si Django ne savait pas sérialiser `Decimal` par défaut, vous auriez pu écrire

    from decimal import Decimal

    from django.db.migrations.serializer import BaseSerializer
    from django.db.migrations.writer import MigrationWriter

    class DecimalSerializer(BaseSerializer):
        def serialize(self):
            return repr(self.value), {"from decimal import Decimal"}

    MigrationWriter.register_serializer(Decimal, DecimalSerializer)

Le premier paramètre de `MigrationWriter.register_serializer()` est un type ou un itérable de types qui doivent utiliser ce sérialiseur.

La méthode `serialize()` de votre sérialiseur doit renvoyer une chaîne contenant la représentation de la valeur dans les migrations et un ensemble de toute importation nécessaire dans la migration.

### Ajout d’une méthode `deconstruct()`

Vous pouvez permettre à Django de sérialiser vos propres instances de classe personnalisées en définissant une méthode `deconstruct()` pour la classe. Elle n’accepte pas de paramètres et doit renvoyer un tuple de trois éléments `(path, args, kwargs)`:

- `path` doit contenir le chemin Python vers la classe, avec le nom de la classe inclus en dernier (par exemple, `monapp.quelque_chose.MaClasse`). Si la classe n’est pas définie au premier niveau du module, elle ne peut pas être sérialisée.

- `args` doit être une liste de paramètres positionnels à passer à la méthode `__init__` de la classe. Tous les éléments de cette liste doivent être eux-mêmes sérialisables.

- `kwargs` doit être un dictionnaire de paramètres nommés à passer à la méthode `__init__` de la classe. Toutes ses valeurs doivent être elles-mêmes sérialisables.

Note

Cette valeur de renvoi est différente de la méthode `deconstruct()` des champs personnalisés qui renvoie un tuple de quatre éléments.

Django écrit la valeur sous forme d’instanciation de la classe avec les paramètres donnés, de la même façon qu’il écrit les références aux champs Django.

Pour éviter qu’une nouvelle migration soit créée lors de chaque exécution de `makemigrations`, vous devriez aussi ajouter une méthode `__eq__()` à la classe décorée. Cette fonction sera appelée par le système des migrations de Django pour détecter d’éventuels changements d’état.

Pour autant que tous les paramètres passés au constructeur de la classe sont eux-même sérialisables, vous pouvez utiliser le décorateur de classe `@deconstructible` se trouvant dans `django.utils.deconstruct` pour ajouter la méthode `deconstruct()`:

    from django.utils.deconstruct import deconstructible

    @deconstructible
    class MyCustomClass:
        def __init__(self, foo=1):
            self.foo = foo
            ...

        def __eq__(self, other):
            return self.foo == other.foo

Ce décorateur ajoute la logique nécessaire pour capturer et préserver les paramètres lors de leur transmission au constructeur, puis renvoie ces mêmes paramètres lorsque `deconstruct()` est appelée.

## Prendre en charge plusieurs versions de Django

Si vous êtes responsable d’une application tierce qui contient des modèles, vous devrez peut-être fournir des migrations qui prennent en charge plusieurs versions de Django. Dans ce cas, vous devez toujours exécuter `makemigrations` avec le plus bas niveau de version de Django vous souhaitez prendre en charge.

Le système de migrations maintiendra la compatibilité ascendante selon la même politique que le reste de Django, afin que les fichiers de migration générées sur Django X.Y devraient fonctionner sans modification sur Django X.Y+1. Cependant, le système des migrations ne promet pas de compatibilité descendante. De nouvelles fonctionnalités peuvent être ajoutées, et les fichiers de migration générés avec les nouvelles versions de Django pourront ne pas fonctionner sur les anciennes versions.

Voir aussi

La référence des opérations de migration

  Documente l’API des opérations de schéma, les opérations spéciales ainsi que l’écriture de ses propres opérations.

Le guide pratique sur l’écriture de migrations

  Présente la façon de structurer et d’écrire des migrations de base de données pour différents scénarios auxquels vous pourriez être confrontés.