Comment déplacer un modèle entre deux applications Django (Django 1.7)

133

Donc, il y a environ un an, j'ai commencé un projet et comme tous les nouveaux développeurs, je ne me suis pas vraiment concentré sur la structure, mais maintenant je suis plus avancé avec Django, il a commencé à apparaître que la mise en page de mon projet, principalement mes modèles sont horribles dans la structure .

J'ai des modèles principalement détenus dans une seule application et vraiment la plupart de ces modèles devraient être dans leurs propres applications individuelles, j'ai essayé de résoudre cela et de les déplacer avec le sud, mais je l'ai trouvé délicat et vraiment difficile en raison de clés étrangères, etc.

Cependant, en raison de Django 1.7 et de la prise en charge intégrée des migrations, y a-t-il un meilleur moyen de le faire maintenant?

Sam Buckingham
la source
4
Vous voudrez peut-être envisager de modifier la réponse acceptée.
Babken Vardanyan
Pour les personnes qui rencontreront cela à l'avenir: Django 3.x ici, et l'approche détaillée sur realpython.com/move-django-model/... a fonctionné pour moi. J'avais plusieurs clés étrangères entre les modèles de l'ancienne application et les modèles de la nouvelle application.
pradeepcep

Réponses:

16

Je supprime l'ancienne réponse car cela peut entraîner une perte de données. Comme ozan l'a mentionné , nous pouvons créer 2 migrations une dans chaque application. Les commentaires sous cet article font référence à mon ancienne réponse.

Première migration pour supprimer le modèle de la 1ère application.

$ python manage.py makemigrations old_app --empty

Modifiez le fichier de migration pour inclure ces opérations.

class Migration(migrations.Migration):

    database_operations = [migrations.AlterModelTable('TheModel', 'newapp_themodel')]

    state_operations = [migrations.DeleteModel('TheModel')]

    operations = [
      migrations.SeparateDatabaseAndState(
        database_operations=database_operations,
        state_operations=state_operations)
    ]

Deuxième migration qui dépend de la première migration et créer la nouvelle table dans la deuxième application. Après avoir déplacé le code du modèle vers la 2ème application

$ python manage.py makemigrations new_app 

et modifiez le fichier de migration en quelque chose comme ça.

class Migration(migrations.Migration):

    dependencies = [
        ('old_app', 'above_migration')
    ]

    state_operations = [
        migrations.CreateModel(
            name='TheModel',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
            ],
            options={
                'db_table': 'newapp_themodel',
            },
            bases=(models.Model,),
        )
    ]

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]
ChillarAnand
la source
J'ai des données existantes et beaucoup d'entre elles que je ne peux tout simplement pas perdre, est-ce possible de le faire avec ça?
Sam Buckingham le
@KevinChristopherHenry a modifié le code. Cela préserve les données existantes.
ChillarAnand
@SamBuckingham Oui, vous pouvez essayer avec le code modifié de migrer sans perdre les données.
ChillarAnand
2
Je pense que ça va être la meilleure façon vraiment, merci pour toute l'aide qui a été géniale.
Sam Buckingham
1
IMO c'est une mauvaise solution, l'hypothèse de base des migrations est que si vous exécutez ./manage.py migratetout se terminera en bon état. Simuler manuellement les migrations est une mauvaise manière pour l'OMI.
jb.
341

Cela peut être fait assez facilement en utilisant migrations.SeparateDatabaseAndState. Fondamentalement, nous utilisons une opération de base de données pour renommer la table simultanément avec deux opérations d'état pour supprimer le modèle de l'historique d'une application et le créer dans celui d'une autre.

Supprimer de l'ancienne application

python manage.py makemigrations old_app --empty

Dans la migration:

class Migration(migrations.Migration):

    dependencies = []

    database_operations = [
        migrations.AlterModelTable('TheModel', 'newapp_themodel')
    ]

    state_operations = [
        migrations.DeleteModel('TheModel')
    ]

    operations = [
        migrations.SeparateDatabaseAndState(
            database_operations=database_operations,
            state_operations=state_operations)
    ]

Ajouter à une nouvelle application

Tout d'abord, copiez le modèle dans le model.py de la nouvelle application, puis:

python manage.py makemigrations new_app

Cela générera une migration avec une CreateModelopération naïve comme seule opération. Enveloppez cela dans une SeparateDatabaseAndStateopération telle que nous n'essayons pas de recréer la table. Incluez également la migration précédente en tant que dépendance:

class Migration(migrations.Migration):

    dependencies = [
        ('old_app', 'above_migration')
    ]

    state_operations = [
        migrations.CreateModel(
            name='TheModel',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
            ],
            options={
                'db_table': 'newapp_themodel',
            },
            bases=(models.Model,),
        )
    ]

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]
ozan
la source
14
Vraiment bonne explication. Cela devrait être la réponse, en renommant la table, vous évitez de perdre des données.
Remiz
11
C'est la meilleure façon de le faire et c'est bien mieux que le mien. Note ajoutée en haut de ma réponse.
ChillarAnand
4
J'ai fait cela, mais lorsque j'exécute "makemigrations" sur la nouvelle application après cela, il génère une migration AlterModelTable en le renommant None.
Diego Ponciano
4
J'ai trouvé un moyen de résoudre mon problème sur la base de ces instructions. Le problème est plus compliqué si vous avez des références de clé étrangère qui sont des champs obligatoires. J'ai dû ajouter quelques étapes pour déplacer les références.
Nostalg.io
14
En raison de plusieurs demandes, j'ai créé une réponse détaillée sur les migrations de modèles FK avec un exemple GitHub. stackoverflow.com/questions/30601107/…
Nostalg.io
26

J'ai rencontré le même problème. La réponse d'Ozan m'a beaucoup aidé mais malheureusement pas assez. En effet j'avais plusieurs ForeignKey liés au modèle que je souhaitais déplacer. Après quelques maux de tête, j'ai trouvé la solution alors j'ai décidé de la poster pour résoudre le temps des gens.

Vous avez besoin de 2 étapes supplémentaires:

  1. Avant de faire quoi que ce soit, changez tous vos ForeignKey liens TheModelen Integerfield. Puis courspython manage.py makemigrations
  2. Après avoir effectué les étapes d'Ozan, reconvertissez vos clés étrangères: remettez-les au ForeignKey(TheModel)lieu deIntegerField() . Puis refaites les migrations ( python manage.py makemigrations). Vous pouvez ensuite migrer et cela devrait fonctionner ( python manage.py migrate)

J'espère que ça aide. Bien sûr, testez-le en local avant d'essayer en production pour éviter les mauvaises surprises :)

otranzer
la source
8
qu'en est-il des relations ManyToManyField?
tomcounsell
1
@tomcounsell excellent commentaire, je suppose qu'en ajoutant un modèle through spécifique uniquement aux fins des migrations. Beaucoup de travail est nécessaire pour laisser les données intactes ...
Wtower
Comme une relation plusieurs-à-plusieurs n'est généralement qu'une table avec deux clés étrangères, d'un point de vue SQL, vous pouvez appliquer l'astuce de cette réponse. Mais pour y parvenir uniquement via Django, une approche à laquelle je peux penser serait celle de la réponse @ozan, sauf que la première étape serait de dupliquer les tables impliquées dans la relation MTM (une version des dupes dans chaque application) , migrez toutes les clés étrangères vers la nouvelle application, puis supprimez les dupes dans l'ancienne application. Disclaimer: Je n'ai pas testé :)
Arnaud P
15

Comment je l'ai fait (testé sur Django == 1.8, avec postgres, donc probablement aussi 1.7)

Situation

app1.YourModel

mais vous voulez qu'il aille sur: app2.YourModel

  1. Copiez YourModel (le code) de app1 vers app2.
  2. ajoutez ceci à app2.YourModel:

    Class Meta:
        db_table = 'app1_yourmodel'
  3. $ python manage.py makemigrations app2

  4. Une nouvelle migration (par exemple 0009_auto_something.py) est effectuée dans app2 avec une instruction migrations.CreateModel (), déplacez cette instruction vers la migration initiale de app2 (par exemple 0001_initial.py) (ce sera comme si elle avait toujours été là). Et maintenant, supprimez la migration créée = 0009_auto_something.py

  5. Tout comme vous agissez, comme app2.YourModel a toujours été là, supprimez maintenant l'existence de app1.YourModel de vos migrations. Signification: commentez les instructions CreateModel et chaque ajustement ou migration de données que vous avez utilisé par la suite.

  6. Et bien sûr, chaque référence à app1.YourModel doit être remplacée par app2.YourModel via votre projet. N'oubliez pas non plus que toutes les clés étrangères possibles vers app1.YourModel dans les migrations doivent être remplacées par app2.YourModel

  7. Maintenant, si vous faites migrer $ python manage.py, rien n'a changé, même lorsque vous faites $ python manage.py makemigrations, rien de nouveau n'a été détecté.

  8. Maintenant, la touche finale: supprimez la classe Meta de app2.YourModel et faites $ python manage.py makemigrations app2 && python manage.py migrez app2 (si vous examinez cette migration, vous verrez quelque chose comme ça :)

        migrations.AlterModelTable(
        name='yourmodel',
        table=None,
    ),

table = None, signifie qu'il prendra le nom de table par défaut, qui dans ce cas sera app2_yourmodel.

  1. FAIT, avec les données enregistrées.

PS lors de la migration, il verra que l'application content_type app1.yourmodel a été supprimée et peut être supprimée. Vous pouvez dire oui à cela, mais seulement si vous ne l'utilisez pas. Si vous en dépendez beaucoup pour que les FK de ce type de contenu soient intacts, ne répondez pas encore par oui ou par non, mais allez dans la base de données cette fois-ci manuellement, supprimez le contentype app2.yourmodel et renommez le contenttype app1. yourmodel à app2.yourmodel, puis continuez en répondant non.

Michael van de Waeter
la source
3
Bien que cette solution soit définitivement plus "hack" que celle de @ ozan et qu'elle ait certainement besoin de plus d'édition, elle a bien fonctionné pour moi (et c'est correct d'éditer les migrations - elles sont censées être modifiables, selon la documentation).
pgcd
1
Utilisez éventuellement également l' app_label = 'app1'option meta.
Wtower
Génie! Cela a très bien fonctionné pour moi pour les relations ForeignKey. Je suppose que cela fonctionnera également pour les champs ManyToMany.
Babken Vardanyan
1
J'ai suivi vos étapes, mais le champ dans certains modèles appartenant à app1 consiste en une clé étrangère avec une relation récursive avec le modèle (myModel) à déplacer. Comme field1 = models.ForeignKey('app1.myModel').lorsque je migre, j'obtiens un ValueError indiquant quefield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
Deesha
12

Je reçois des migrations de codage manuel nerveuses (comme l'exige la réponse d' Ozan ), donc ce qui suit combine les stratégies d' Ozan et de Michael pour minimiser la quantité de codage manuel requise:

  1. Avant de déplacer des modèles, assurez-vous de travailler avec une ligne de base propre en exécutant makemigrations.
  2. Déplacer le code du modèle de app1versapp2
  3. Comme recommandé par @Michael, nous pointons le nouveau modèle vers l'ancienne table de base de données en utilisant l' db_tableoption Meta sur le "nouveau" modèle:

    class Meta:
        db_table = 'app1_yourmodel'
  4. Courez makemigrations. Cela générera CreateModeldans app2et DeleteModeldans app1. Techniquement, ces migrations se réfèrent exactement à la même table et supprimeraient (y compris toutes les données) et recréeraient la table.

  5. En réalité, nous ne voulons (ou n'avons pas besoin) de faire quoi que ce soit à la table. Nous avons juste besoin de Django pour croire que le changement a été fait. Selon la réponse de @ Ozan, le state_operationsdrapeau SeparateDatabaseAndStatefait cela. Nous enveloppons donc toutes les migrationsentrées DANS LES DEUX FICHIERS DE MIGRATIONS avec SeparateDatabaseAndState(state_operations=[...]). Par exemple,

    operations = [
        ...
        migrations.DeleteModel(
            name='YourModel',
        ),
        ...
    ]

    devient

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=[
            ...
            migrations.DeleteModel(
                name='YourModel',
            ),
            ...
        ])
    ]
  6. Vous devez également vous assurer que la nouvelle CreateModelmigration «virtuelle» dépend de toute migration qui a réellement créé ou modifié la table d'origine . Par exemple, si vos nouvelles migrations sont app2.migrations.0004_auto_<date>(pour le Create) et app1.migrations.0007_auto_<date>(pour le Delete), la chose la plus simple à faire est:

    • Ouvrez app1.migrations.0007_auto_<date>et copiez sa app1dépendance (par exemple ('app1', '0006...'),). Il s'agit de la migration «immédiatement antérieure» vers app1et doit inclure des dépendances sur toute la logique de création de modèle réelle.
    • Ouvrez app2.migrations.0004_auto_<date>et ajoutez la dépendance que vous venez de copier à sa dependenciesliste.

Si vous avez des ForeignKeyrelations avec le modèle que vous déplacez, ce qui précède peut ne pas fonctionner. Cela se produit parce que:

  • Les dépendances ne sont pas créées automatiquement pour les ForeignKeymodifications
  • Nous ne voulons pas englober les ForeignKeymodifications state_operations, nous devons donc nous assurer qu'elles sont distinctes des opérations de table.

REMARQUE: Django 2.2 a ajouté un avertissement ( models.E028) qui rompt cette méthode. Vous pourrez peut-être contourner ce problème, managed=Falsemais je ne l'ai pas testé.

L'ensemble d'opérations «minimum» diffère selon la situation, mais la procédure suivante devrait fonctionner pour la plupart / toutes les ForeignKeymigrations:

  1. COPIEZ le modèle de app1à app2, définissez db_table, mais NE changez aucune référence FK.
  2. Exécutez makemigrationset encapsulez toutes les app2migrations state_operations(voir ci-dessus)
    • Comme ci-dessus, ajoutez une dépendance dans app2 CreateTablela dernière app1migration
  3. Pointez toutes les références FK vers le nouveau modèle. Si vous n'utilisez pas de références de chaîne, déplacez l'ancien modèle vers le bas de models.py(NE PAS le supprimer) afin qu'il ne soit pas en concurrence avec la classe importée.
  4. Exécutez makemigrationsmais NE PAS encapsuler quoi que ce soit state_operations(les changements FK devraient réellement se produire). Ajoutez une dépendance dans toutes les ForeignKeymigrations (c'est AlterField-à- dire ) à la CreateTablemigration vers app2(vous aurez besoin de cette liste pour l'étape suivante, alors gardez-en une trace). Par exemple:

    • Recherchez la migration qui inclut le CreateModelpar exemple app2.migrations.0002_auto_<date>et copiez le nom de cette migration.
    • Trouvez toutes les migrations qui ont une ForeignKey vers ce modèle (par exemple en recherchant app2.YourModeldes migrations comme:

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
          ]
      
          operations = [
              migrations.AlterField(
                  model_name='relatedmodel',
                  name='fieldname',
                  field=models.ForeignKey(... to='app2.YourModel'),
              ),
          ]
    • Ajoutez la CreateModelmigration en tant que dépendance:

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
              ('app2', '0002_auto_<date>'),
          ]  
  5. Supprimer les modèles de app1

  6. Exécutez makemigrationset encapsulez la app1migration state_operations.
    • Ajoutez une dépendance à toutes les ForeignKeymigrations (c'est-à-dire AlterField) de l'étape précédente (peut inclure des migrations dans app1et app2).
    • Quand j'ai construit ces migrations, cela DeleteTabledépendait déjà des AlterFieldmigrations, donc je n'avais pas besoin de les appliquer manuellement (c'est-à-dire Alteravant Delete).

À ce stade, Django est prêt à partir. Le nouveau modèle pointe vers l'ancienne table et les migrations de Django l'ont convaincu que tout a été déplacé de manière appropriée. La grande mise en garde (de la réponse de @ Michael) est qu'un nouveau ContentTypeest créé pour le nouveau modèle. Si vous ForeignKeycréez un lien (par exemple par ) vers des types de contenu, vous devrez créer une migration pour mettre à jour la ContentTypetable.

Je voulais nettoyer après moi (options Meta et noms de table) alors j'ai utilisé la procédure suivante (de @Michael):

  1. Supprimer l' db_tableentrée Meta
  2. Exécutez à makemigrationsnouveau pour générer le renommage de la base de données
  3. Modifiez cette dernière migration et assurez-vous qu'elle dépend de la DeleteTablemigration. Cela ne semble pas nécessaire car cela Deletedevrait être purement logique, mais j'ai rencontré des erreurs (par exemple, cela app1_yourmodeln'existe pas) si je ne le fais pas.
Claytond
la source
Cela a parfaitement fonctionné, merci! Je ne pense pas que la modification de la dernière migration importe car elle se trouve de toute façon au bas de l'arbre des dépendances.
James Meakin
1
Bonne réponse! Je pense que vous devez ajouter une parenthèse fermante aux migrations.SeparateDatabaseAndState, non?
guichet du
Cela a fonctionné pour moi. Je n'ai pas non plus modifié la dernière migration (étape 3, la toute dernière ligne de la réponse entière) comme @JamesMeakin et cela fonctionnait toujours bien
Megawatt
dans le deuxième scénario, celui avec les FK, la deuxième étape a échoué pour moi avec une erreur qui a du sens:table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
Mihai Zamfir
J'ai utilisé la procédure plusieurs fois. Si vous comparez la documentation pour 2.2 ( docs.djangoproject.com/en/2.2/ref/checks ) et 2.1 ( docs.djangoproject.com/en/2.1/ref/checks ), vous pouvez voir qu'elle a été ajoutée en 2.2. Il est possible de contourner le problème, managed=Falsemais je ne suis pas en mesure de vérifier.
claytond
1

Une autre alternative hacky si les données ne sont pas volumineuses ou trop compliquées, mais toujours importantes à maintenir, est de:

  • Obtenez des données fixes à l' aide de manage.py dumpdata
  • Procéder correctement aux modifications du modèle et aux migrations, sans rapporter les modifications
  • Global remplace les appareils de l'ancien modèle et des noms d'application par le nouveau
  • Charger des données à l'aide de manage.py loaddata
Wtower
la source
1

Copié de ma réponse à https://stackoverflow.com/a/47392970/8971048

Si vous avez besoin de déplacer le modèle et que vous n'avez plus accès à l'application (ou que vous ne voulez plus l'accès), vous pouvez créer une nouvelle opération et envisager de créer un nouveau modèle uniquement si le modèle migré ne le fait pas exister.

Dans cet exemple, je passe «MyModel» de old_app à myapp.

class MigrateOrCreateTable(migrations.CreateModel):
    def __init__(self, source_table, dst_table, *args, **kwargs):
        super(MigrateOrCreateTable, self).__init__(*args, **kwargs)
        self.source_table = source_table
        self.dst_table = dst_table

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        table_exists = self.source_table in schema_editor.connection.introspection.table_names()
        if table_exists:
            with schema_editor.connection.cursor() as cursor:
                cursor.execute("RENAME TABLE {} TO {};".format(self.source_table, self.dst_table))
        else:
            return super(MigrateOrCreateTable, self).database_forwards(app_label, schema_editor, from_state, to_state)


class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_some_migration'),
    ]

    operations = [
        MigrateOrCreateTable(
            source_table='old_app_mymodel',
            dst_table='myapp_mymodel',
            name='MyModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=18))
            ],
        ),
    ]
Chanteuse gal
la source
Veuillez ne pas ajouter la même réponse à plusieurs questions. Répondez à la meilleure et marquez le reste comme des doublons. Voir Est-il acceptable d'ajouter une réponse en double à plusieurs questions?
Petter Friberg
0

Ceci est testé grossièrement, alors n'oubliez pas de sauvegarder votre DB !!!

Par exemple, il existe deux applications: src_appet dst_app, nous voulons déplacer le modèle MoveMede src_appvers dst_app.

Créez des migrations vides pour les deux applications:

python manage.py makemigrations --empty src_app
python manage.py makemigrations --empty dst_app

Supposons que les nouvelles migrations soient XXX1_src_app_newet XXX1_dst_app_new, les principales migrations précédentes sont XXX0_src_app_oldet XXX0_dst_app_old.

Ajoutez une opération qui renomme la table du MoveMemodèle et renomme son app_label dans ProjectState en XXX1_dst_app_new. N'oubliez pas d'ajouter une dépendance à la XXX0_src_app_oldmigration. La XXX1_dst_app_newmigration qui en résulte est:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

# this operations is almost the same as RenameModel
# https://github.com/django/django/blob/1.7/django/db/migrations/operations/models.py#L104
class MoveModelFromOtherApp(migrations.operations.base.Operation):

    def __init__(self, name, old_app_label):
        self.name = name
        self.old_app_label = old_app_label

    def state_forwards(self, app_label, state):

        # Get all of the related objects we need to repoint
        apps = state.render(skip_cache=True)
        model = apps.get_model(self.old_app_label, self.name)
        related_objects = model._meta.get_all_related_objects()
        related_m2m_objects = model._meta.get_all_related_many_to_many_objects()
        # Rename the model
        state.models[app_label, self.name.lower()] = state.models.pop(
            (self.old_app_label, self.name.lower())
        )
        state.models[app_label, self.name.lower()].app_label = app_label
        for model_state in state.models.values():
            try:
                i = model_state.bases.index("%s.%s" % (self.old_app_label, self.name.lower()))
                model_state.bases = model_state.bases[:i] + ("%s.%s" % (app_label, self.name.lower()),) + model_state.bases[i+1:]
            except ValueError:
                pass
        # Repoint the FKs and M2Ms pointing to us
        for related_object in (related_objects + related_m2m_objects):
            # Use the new related key for self referential related objects.
            if related_object.model == model:
                related_key = (app_label, self.name.lower())
            else:
                related_key = (
                    related_object.model._meta.app_label,
                    related_object.model._meta.object_name.lower(),
                )
            new_fields = []
            for name, field in state.models[related_key].fields:
                if name == related_object.field.name:
                    field = field.clone()
                    field.rel.to = "%s.%s" % (app_label, self.name)
                new_fields.append((name, field))
            state.models[related_key].fields = new_fields

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        old_apps = from_state.render()
        new_apps = to_state.render()
        old_model = old_apps.get_model(self.old_app_label, self.name)
        new_model = new_apps.get_model(app_label, self.name)
        if self.allowed_to_migrate(schema_editor.connection.alias, new_model):
            # Move the main table
            schema_editor.alter_db_table(
                new_model,
                old_model._meta.db_table,
                new_model._meta.db_table,
            )
            # Alter the fields pointing to us
            related_objects = old_model._meta.get_all_related_objects()
            related_m2m_objects = old_model._meta.get_all_related_many_to_many_objects()
            for related_object in (related_objects + related_m2m_objects):
                if related_object.model == old_model:
                    model = new_model
                    related_key = (app_label, self.name.lower())
                else:
                    model = related_object.model
                    related_key = (
                        related_object.model._meta.app_label,
                        related_object.model._meta.object_name.lower(),
                    )
                to_field = new_apps.get_model(
                    *related_key
                )._meta.get_field_by_name(related_object.field.name)[0]
                schema_editor.alter_field(
                    model,
                    related_object.field,
                    to_field,
                )

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        self.old_app_label, app_label = app_label, self.old_app_label
        self.database_forwards(app_label, schema_editor, from_state, to_state)
        app_label, self.old_app_label = self.old_app_label, app_label

    def describe(self):
        return "Move %s from %s" % (self.name, self.old_app_label)


class Migration(migrations.Migration):

    dependencies = [
       ('dst_app', 'XXX0_dst_app_old'),
       ('src_app', 'XXX0_src_app_old'),
    ]

    operations = [
        MoveModelFromOtherApp('MoveMe', 'src_app'),
    ]

Ajoutez une dépendance XXX1_dst_app_newà XXX1_src_app_new. XXX1_src_app_newest une migration sans opération nécessaire pour s'assurer que les futures src_appmigrations seront exécutées aprèsXXX1_dst_app_new .

Passer MoveMede src_app/models.pyà dst_app/models.py. Puis exécutez:

python manage.py migrate

C'est tout!

Sergey Fedoseev
la source
Notez que ce code n'est probablement utile que pour django 1.7. Essayer cela dans django 2.0 ne fonctionnera pas. Cela signifie également que l'utilisation de ce mécanisme pour déplacer des modèles ajoute une charge de maintenance à la mise à niveau de votre version de django.
Paul dans 't Hout
0

Vous pouvez essayer ce qui suit (non testé):

  1. déplacer le modèle de src_appversdest_app
  2. migrer dest_app; assurez-vous que la migration du schéma dépend de la dernière src_appmigration ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )
  3. ajouter une migration de données vers dest_app, qui copie toutes les données desrc_app
  4. migrer src_app; assurez-vous que la migration de schéma dépend de la dernière migration (de données) de dest_app- c'est-à-dire: la migration de l'étape 3

Notez que vous allez copier la table entière, au lieu de la déplacer , mais de cette façon, les deux applications n'ont pas à toucher une table qui appartient à l'autre application, ce qui, à mon avis, est plus important.

Webthusiast
la source
0

Disons que vous déplacez le modèle TheModel de app_a vers app_b.

Une autre solution consiste à modifier manuellement les migrations existantes. L'idée est que chaque fois que vous voyez une opération modifiant TheModel dans les migrations d'app_a, vous copiez cette opération à la fin de la migration initiale d'app_b. Et chaque fois que vous voyez une référence 'app_a.TheModel' dans les migrations d'app_a, vous la changez en 'app_b.TheModel'.

Je viens de faire cela pour un projet existant, dans lequel je voulais extraire un certain modèle vers une application réutilisable. La procédure s'est bien déroulée. Je suppose que les choses seraient beaucoup plus difficiles s'il y avait des références de app_b à app_a. De plus, j'avais une table Meta.db_table définie manuellement pour mon modèle, ce qui aurait pu aider.

Vous vous retrouverez notamment avec un historique de migration modifié. Cela n'a pas d'importance, même si vous avez une base de données avec les migrations d'origine appliquées. Si les migrations d'origine et réécrites se retrouvent avec le même schéma de base de données, une telle réécriture devrait être correcte.

akaariai
la source
0
  1. changez les noms des anciens modèles en 'model_name_old'
  2. faire des migrations
  3. créer de nouveaux modèles nommés 'model_name_new' avec des relations identiques sur les modèles associés (par exemple, le modèle utilisateur a maintenant user.blog_old et user.blog_new)
  4. faire des migrations
  5. écrire une migration personnalisée qui migre toutes les données vers les nouvelles tables de modèle
  6. tester l'enfer de ces migrations en comparant les sauvegardes avec les nouvelles copies de base de données avant et après l'exécution des migrations
  7. quand tout est satisfaisant, supprimez les anciens modèles
  8. faire des migrations
  9. remplacez les nouveaux modèles par le nom correct 'model_name_new' -> 'model_name'
  10. tester l'ensemble des migrations sur un serveur intermédiaire
  11. arrêtez votre site de production pendant quelques minutes afin d'exécuter toutes les migrations sans que les utilisateurs n'interfèrent

Faites-le individuellement pour chaque modèle qui doit être déplacé. Je ne suggérerais pas de faire ce que l'autre réponse dit en passant aux entiers et en revenant aux clés étrangères Il y a une chance que les nouvelles clés étrangères soient différentes et que les lignes puissent avoir des ID différents après les migrations et je ne voulais pas courir de risque des identifiants incompatibles lors du retour aux clés étrangères.

tomcounsell
la source