web-dev-qa-db-fra.com

Django 1.8: Créer les migrations initiales pour le schéma existant

J'ai démarré un projet Django 1.8, qui utilise le système de migration.
En quelque sorte, au fil du temps, les choses ont mal tourné. J'ai donc effacé les dossiers et la table des migrations de la base de données et j'essaie maintenant de les reconstruire, sans succès.

J'ai trois applications (3 fichiers models.py), Et les modèles reflètent EXACTEMENT les tables!

La meilleure approche que j'ai trouvée jusqu'à présent était:

  1. Effacer tous les dossiers migrations. Terminé!
  2. Supprimer tout de la table Django_migrations. Terminé!
  3. Exécutez python manage.py makemigrations --empty <app> Pour chaque application. Terminé!
  4. Exécutez python manage.py migrate --fake. Terminé! (Bien que cela ne fonctionne que si je le lance après chaque commande makemigrations.

Maintenant, j'ajoute un nouveau champ, lance la commande makemigrations et le message d'erreur suivant s'affiche:
Django.db.utils.OperationalError: (1054, "Unknown column 'accounts_plan.max_item_size' in 'field list'")

J'ai brûlé des heures sur cette chose. Comment puis-je initialiser les migrations pour pouvoir continuer à travailler sans interruption de migration à chaque fois?

Pourquoi est-ce si compliqué? Pourquoi n’existe-t-il pas une seule ligne: initiate_migrations_from_schema?

MODIFIER:
Maintenant, les choses deviennent encore plus mauvaises. J'ai tronqué la table Django_migrations Et supprimé tout le dossier migrations.
Maintenant, j'essaie d'exécuter python manage.py migrate --fake-initial (Quelque chose que j'ai trouvé dans la documentation DEV), juste pour qu'il configure toutes les applications 'internes' de Django (auth, session, etc.) et j'obtiens :
(1054, "Unknown column 'name' in 'Django_content_type'").
Maintenant, cette "colonne" n'est pas une vraie colonne. C'est un @property Défini dans l'application contenttypes de Django. WHAT IS EN COURS ICI? Pourquoi identifie-t-il la propriété name en tant que colonne réelle?

69
user1102018

Je l'ai enfin fait fonctionner, bien que je ne sache pas pourquoi et j'espère que cela fonctionnera à l'avenir.
Après avoir effectué de nombreux essais et avoir consulté le site de développement de Django ( lien ).
Voici les étapes (pour quiconque se heurte à ce problème):

  1. Videz le Django_migrations table: delete from Django_migrations;
  2. Pour chaque application, supprimez son dossier migrations: rm -rf <app>/migrations/
  3. Réinitialisez les migrations pour les applications "intégrées": python manage.py migrate --fake
  4. Pour chaque application lancée: python manage.py makemigrations <app>. Occupez-vous des dépendances (les modèles avec ForeignKey doivent fonctionner après leur modèle parent).
  5. Finalement: python manage.py migrate --fake-initial

Après cela, j'ai exécuté la dernière commande sans le --fake-initial drapeau, juste pour être sûr.

Maintenant tout fonctionne et je peux utiliser le système de migration normalement.

Je suis sûr que je ne suis pas le seul à rencontrer ce problème. Il doit être mieux documenté et même simplifié.

Mise à jour pour Django 1.9 utilisateurs:
J'ai eu ce scénario à nouveau avec un Django 1.9.4, et l'étape 5 a échoué.
Il me suffisait de remplacer --fake-initial avec --fake pour le faire fonctionner.

161
user1102018

J'ai rencontré ce scénario mais je n'ai jamais eu à abandonner la base de données pour le résoudre. Généralement, je supprime le dossier de migration de l'application et supprime les entrées de migration de la base de données.

J'essayais de faire des migrations une application à la fois. Si l'une des applications s'appuie sur d'autres tables, ajoutez-les évidemment en dernier.

De plus, je viens généralement de lancer python manage.py makemigrations puis juste python manage.py migrate Même avec la migration initiale, tout devrait bien fonctionner avec Django 1.7 et 1.8.

2
Chris Hawkes

Django ..., 1.8, 1.9, ...

Ce que vous voulez réaliser, c'est écraser les migrations existantes et utiliser leur remplacement.

Comment le faire correctement sans utiliser de commande lors de la libération (un cas sans impact sur la base de données et les collègues).

  1. Pour chaque application, supprimez son dossier de migration: mv <app>/migrations/ <app>/migrationsOLD/

  2. Pour chaque exécution de cette application: python manage.py makemigrations <app>.

  3. Personnalisez chaque nouvelle migration:

    • si vous avez un application complexe, ou plusieurs applications et modèles associés entre eux, afin d'éviter CircularDependencyError ou ValueError: Unhandled pending operations for models:

      prépare la deuxième migration vide dans <app>0002_initial2.py _ (mettez sa dépendance à app_other::0001_initial.py et <app> :: 0001_initial.py _ ainsi - tous les modèles ForeignKey, M2M liés aux modèles créés lors de l'étape de migration 0001 dans d'autres applications)

      Tout doit être en ordre - parfois, il faudra plus de migrations pour se préparer. Prenez soin de l'attribut dependencies ici dans chaque migration.

    • s'occuper des valeurs initiales - vérifiez vous-même toutes les RunPython actions de migrationsOLD et copiez le code dans la nouvelle migration initiale si nécessaire.

    • (optionnel pour --fake-initial) Ajouter initial=True à toutes les nouvelles classes de migration (0002 aussi si a été ajouté).

    • Ajoutez l'attribut replaces dans la nouvelle classe de migration. (comme propre custom a squashmigrations). Mettez là toutes les anciennes migrations de <app>
  4. Tout vérifier avec makemigrations.

    affirmer "Aucun changement détecté"

  5. Vérifier si migrate -l montrer [x] partout

    affirmer similaire:

    [X] 0001_initial

    [X] 0002_initial2 (102 migrations écrasées)

Exemple:

Pour vieux:

0001_initial.py
0002_auto.py
...
0103_auto.py

préparer:

0001_initial.py
0002_initial2.py  (optional but sometimes required to satisfy dependency)

et ajoutez à replacesà la dernière (0002 ici, peut être 0001):

replaces = [(b'<app>', '0002_auto.py'), ..., (b'<app>', '0103_auto.py')]

0001_initial.py doit être nommé de la même manière que l'ancien.

0002_initial2.py est nouveau, mais il remplace les anciennes migrations donc Django le traitera comme chargé.

2
Sławomir Lenart

Si vous utilisez des routeurs, cela pourrait poser un problème. Méthode de vérification allow_migrate s'il est exécuté correctement dans routers.py. Essayez de définir toujours la valeur de retour sur True et vérifiez si le problème est résolu.

def allow_migrate(self, db, app_label, model_name=None, **hints):
    return True
0
AmirM