i 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.
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).
Dans ces fichiers, il est indiqué qu’ils remplacent les migrations fusionnées précédentes, ce qui signifie qu’ils peuvent coexister avec les anciens fichiers de migration ; Django choisit intelligemment les fichiers à prendre en compte en fonction de la position actuelle dans l’historique de migration. Si un projet n’a pas encore complètement appliqué un ensemble de migrations qui a été fusionné, Django continue d’utiliser ces anciennes migrations jusqu’au moment de la fusion, après quoi il se rattache à l’historique fusionné, tandis que de nouvelles installations du projet vont utiliser la nouvelle migration fusionnée et laisser de côté les anciennes.
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? [yN] 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.
Vous devez alors faire passer la migration fusionnée vers une migration normale en :
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
Après avoir créé une migration fusionnée, vous ne pouvez pas refusionner celle-ci avant d’avoir effectué la transition complète vers une migration normale.
Nettoyage des références aux migrations supprimées
New in Django 4.1.
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.
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)
instances decimal.Decimal
instances enum.Enum et enum.Flag
instances uuid.UUID
instances de functools.partial() et de functools.partialmethod qui possèdent des valeurs sérialisables de func, args et keywords
des objets chemins purs et concrets de pathlib. Les chemins concrets sont convertis en leur équivalent chemin pur, par ex. pathlib.PosixPath en pathlib.PurePosixPath
instances os.PathLike, par ex. os.DirEntry, qui sont converties en str ou bytes en utilisant os.fspath()
instances LazyObject qui décorent une valeur sérialisable
instances de types énumératifs (ex. TextChoices ou IntegerChoices)
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)
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 4.2:
La prise en charge de la sérialisation pour enum.Flag a été ajoutée.
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