web-dev-qa-db-fra.com

Comment forcer les migrations vers une base de données si certaines tables existent déjà dans Django?

J'ai un projet Python/Django. En raison de certains reculs et d'autres choses mixtes, nous nous sommes retrouvés dans une sorte de scénario étrange.

Le scénario actuel est le suivant:

  • DB a les bonnes tables
  • La base de données ne peut pas être annulée ou supprimée
  • Le code est à jour

  • Le dossier des migrations est derrière la base de données par une ou deux migrations. (Ces migrations ont été appliquées depuis un autre endroit et cet "ailleurs" n'existe plus)

  • J'ajoute et modifie certains modèles

  • Je lance makemigrations
  • De nouvelles migrations sont créées, mais il s'agit d'un mélange de nouvelles tables et de certaines tables qui existent déjà dans la base de données.
  • Si j'exécute migrate , il se plaindra que certaines des tables que j'essaie de créer existent déjà.

Ce dont j'ai besoin:

Pour pouvoir exécuter les migrations et "ignorer" les tables existantes et appliquer les nouvelles. Ou tout autre moyen d'y parvenir.

Est-ce possible?

16

Lorsque vous appliquez une migration, Django insère une ligne dans une table appelée Django_migrations. C'est la seule façon Django sait quelles migrations ont déjà été appliquées et lesquelles ne l'ont pas été. Les lignes de ce tableau doivent donc correspondre aux fichiers de votre répertoire migrations. Si vous ' Si vous avez perdu les fichiers de migration après leur application, ou fait quoi que ce soit d'autre pour désynchroniser les choses, vous aurez des problèmes .. car les numéros de migration dans votre base de données se réfèrent à des fichiers de migration différents de ceux de votre projet.

Donc, avant de faire quoi que ce soit d'autre, vous devez synchroniser les choses en supprimant le Django_migrations des lignes de tableau pour tous les fichiers de migration que vous avez perdus et que vous ne pouvez pas récupérer. Le tableau doit contenir des lignes uniquement pour les migrations que vous avez et qui ont été appliquées correctement à la base de données.

Maintenant, vous devez gérer toutes les modifications de votre base de données que Django Migrations ne connaît pas .. et pour cela, il y a quelques options:

Si les choses ont fonctionné de telle sorte que les modifications de la base de données qui ont déjà été appliquées à la base de données se trouvent dans des fichiers de migration différents de ceux qui ne l'étaient pas, vous pouvez le corriger en exécutant vos migrations une par une à l'aide de --fake option sur toutes les modifications qui sont en réalité déjà dans la base de données. La fausse option écrit simplement la ligne dans le Django_migrations table marquant la migration comme terminée. Ne faites cela que si la base de données contient déjà toutes les modifications contenues dans ce fichier de migration.

Et ces fichiers de migration qui contiennent uniquement des modifications qui n'ont pas été appliquées à la base de données, s'exécutent sans le --fake option et Django les appliquera. Par exemple:

# database already has it
manage.py migrate myapp 0003 --fake 
# need it
manage.py migrate myapp 0004
# database already has it
manage.py migrate myapp 0005 --fake

Si vous avez des fichiers de migration où certaines des modifications, mais pas toutes, ont été appliquées, vous avez un problème plus important. Dans ce cas, il existe plusieurs façons de procéder (choisissez UNE SEULE):

  1. Modifiez les fichiers de migration pour placer les modifications qui ont déjà été appliquées (que Django l'a fait ou que vous l'avez fait manuellement n'a pas d'importance) dans des migrations de nombre inférieur, et placez tout ce dont vous avez besoin dans des fichiers de numéro plus élevé . Maintenant vous pouvez --fake les plus petits et exécutez les plus hauts comme d'habitude. Disons que vous avez apporté 10 modifications à vos modèles, et que 5 de ces modifications sont déjà dans la base de données, mais Django ne les connaît pas .. donc quand vous exécutez makemigrations, une nouvelle migration est créée avec les 10 modifications. Cela échouera normalement car le serveur de base de données ne peut pas par exemple ajouter une colonne qui existe déjà. Déplacez ces modifications déjà appliquées de votre nouveau fichier de migration vers le précédent (déjà appliqué) fichier de migration. Django supposera alors que ceux-ci ont été appliqués avec la migration précédente et ne tentera pas de les appliquer à nouveau. Vous pouvez alors migrate comme d'habitude et le de nouveaux changements seront appliqués.

    Si vous ne souhaitez pas toucher à votre ancien fichier de migration, vous pouvez d'abord exécuter makemigrations --empty appname pour créer un fichier de migration vide. Ensuite, exécutez makemigrations qui créera une autre migration avec toutes les modifications que Django pense devoir faire. Déplacez les migrations déjà effectuées de ce fichier vers la migration vide que vous avez créée .. puis --fake celui-là. Cela permettra à Django de comprendre à quoi ressemble la base de données sera en phase avec la réalité et vous pouvez ensuite migrate comme d'habitude, en appliquant les modifications dans le dernier fichier de migration.

  2. Débarrassez-vous de toutes les nouvelles migrations que vous venez de créer à l'aide de makemigrations. Maintenant, commentez ou remettez dans vos modèles tout ce qui n'a pas été appliqué à la base de données, en laissant votre code correspondant à ce qui se trouve réellement dans la base de données. Vous pouvez maintenant faire makemigrations et migrate appname --fake et vous serez synchronisé. Décommentez ensuite votre nouveau code et exécutez 'makemigrations' puis migrate comme d'habitude et les modifications seront appliquées. Si les modifications sont faibles (par exemple, en ajoutant quelques champs), cela est parfois plus simple. Si les changements sont importants, ce n'est pas ...

  3. Vous pouvez aller de l'avant et (soigneusement) apporter vous-même les modifications à la base de données, en la mettant à jour. Maintenant, lancez simplement migrate --fake et si vous ne vous trompez pas, tout ira bien. Encore une fois, c'est facile pour les petits changements, pas aussi facile pour les plus compliqués.

  4. Tu peux courir manage.py sqlmigrate > mychanges.sql. Cela génère mychanges.sql contenant tous les SQL Django SERAIT exécuté sur la base de données. Modifiez maintenant ce fichier pour supprimer toutes les modifications qui ont déjà été appliquées, en laissant ce qui doit être fait. Exécutez ce SQL en utilisant pgadmin ou psql (vous utilisez postgresql, je l'espère). Maintenant, les modifications ont toutes été apportées .. afin que vous puissiez exécuter manage.py migrate --fake, cela mettra Django en synchronisation avec la réalité et vous devriez être prêt. Si vos compétences en SQL sont suffisantes, c'est probablement la solution la plus simple.

Je devrais ajouter deux avertissements:

Tout d'abord, si vous appliquez une migration ultérieure, par exemple 0003_foobar.py, puis que les choses ne fonctionnent pas et que vous décidez d'essayer de revenir en arrière et d'appliquer 0002_bazbuz.py, alors Django TAKE STUFF OUT DE VOTRE BASE DE DONNÉES. Par exemple, une colonne que vous pourriez avoir ajoutée en 0003 sera supprimée avec ses données. Puisque vous dites que vous ne pouvez pas perdre de données, soyez très prudent lorsque vous revenez en arrière.

Deuxièmement, ne vous précipitez pas pour exécuter --fake migrations. Assurez-vous que l'intégralité de la migration que vous vous apprêtez à simuler se trouve déjà dans la base de données. Sinon, cela devient très déroutant. Si vous regrettez de fausses migrations et que vous ne voulez pas revenir en arrière, vous pouvez effacer les connaissances de Django sur la migration contrefaite en supprimant cette ligne du Django_migrations table. C'est ok pour faire ça .. si vous comprenez ce que vous faites. Si vous savez que la migration n'a pas vraiment été appliquée, alors ça va.

32
little_birdie

Ce billet de blog le cloue vraiment. https://simpleisbetterthancomplex.com/tutorial/2016/07/26/how-to-reset-migrations.html

Permettez-moi de résumer les étapes de son scénario 2 (vous avez une base de données de production et souhaitez changer de schéma/modèles dans une ou plusieurs applications). Dans mon cas, j'avais deux applications, file d'attente et bordereau de routage, qui avaient des modifications de modèle que je devais appliquer à un système de production. La clé était que j'avais déjà la base de données, c'est donc là que --fake-initial entre en jeu.

Voici les étapes que j'ai suivies. Comme toujours, sauvegardez tout avant de commencer. Je travaille dans un VM donc j'ai juste pris un instantané avant d'aller de l'avant.

1) Supprimez l'historique de migration pour chaque application.

python manage.py migrate --fake queue zero
python manage.py migrate --fake routingslip zero

2) Supprimez tous les fichiers de migration dans l'ensemble du projet dans lequel les applications résident .

find . -path "*/migrations/*.py" -not -name "__init__.py" -delete
find . -path "*/migrations/*.pyc"  -delete

3) Faire des migrations

python manage.py makemigrations

4) Appliquez les migrations, en simulant l'initiale car la base de données existe déjà et nous voulons juste les changements:

python manage.py migrate --fake-initial

A très bien fonctionné pour moi.

2
slogan621