125 votes

Comment faire migrer un modèle d'une application Django vers une nouvelle ?

J'ai une application Django qui contient quatre modèles. Je réalise maintenant que l'un de ces modèles devrait être dans une application séparée. J'ai installé South pour les migrations, mais je ne pense pas que ce soit quelque chose qu'il puisse gérer automatiquement. Comment puis-je faire migrer l'un des modèles de l'ancienne application vers la nouvelle ?

Gardez également à l'esprit que je vais avoir besoin d'un processus reproductible, afin de pouvoir migrer le système de production et autres.

183voto

Potr Czachur Points 1556

Comment migrer en utilisant le sud.

Disons que nous avons deux applications : commune et spécifique :

myproject/
|-- common
|   |-- migrations
|   |   |-- 0001_initial.py
|   |   `-- 0002_create_cat.py
|   `-- models.py
`-- specific
    |-- migrations
    |   |-- 0001_initial.py
    |   `-- 0002_create_dog.py
    `-- models.py

Maintenant nous voulons déplacer le modèle common.models.cat vers l'application spécifique (précisément vers specific.models.cat). Faites d'abord les changements dans le code source et ensuite exécutez :

$ python manage.py schemamigration specific create_cat --auto
 + Added model 'specific.cat'
$ python manage.py schemamigration common drop_cat --auto
 - Deleted model 'common.cat'

myproject/
|-- common
|   |-- migrations
|   |   |-- 0001_initial.py
|   |   |-- 0002_create_cat.py
|   |   `-- 0003_drop_cat.py
|   `-- models.py
`-- specific
    |-- migrations
    |   |-- 0001_initial.py
    |   |-- 0002_create_dog.py
    |   `-- 0003_create_cat.py
    `-- models.py

Maintenant, nous devons modifier les deux fichiers de migration :

#0003_create_cat: replace existing forward and backward code
#to use just one sentence:

def forwards(self, orm):
    db.rename_table('common_cat', 'specific_cat') 

    if not db.dry_run:
        # For permissions to work properly after migrating
        orm['contenttypes.contenttype'].objects.filter(
            app_label='common',
            model='cat',
        ).update(app_label='specific')

def backwards(self, orm):
    db.rename_table('specific_cat', 'common_cat')

    if not db.dry_run:
        # For permissions to work properly after migrating
        orm['contenttypes.contenttype'].objects.filter(
            app_label='specific',
            model='cat',
        ).update(app_label='common')

#0003_drop_cat:replace existing forward and backward code
#to use just one sentence; add dependency:

depends_on = (
    ('specific', '0003_create_cat'),
)
def forwards(self, orm):
    pass
def backwards(self, orm):
    pass

Maintenant, les deux migrations d'applications sont au courant du changement et la vie est un peu moins pénible :-) L'établissement de cette relation entre les migrations est la clé du succès. Maintenant si vous le faites :

python manage.py migrate common
 > specific: 0003_create_cat
 > common: 0003_drop_cat

fera les deux migrations, et

python manage.py migrate specific 0002_create_dog
 < common: 0003_drop_cat
 < specific: 0003_create_cat

va migrer les choses vers le bas.

Notez que pour la mise à niveau du schéma, j'ai utilisé l'application commune et pour la mise à niveau inférieure, j'ai utilisé l'application spécifique. Cela s'explique par la façon dont la dépendance fonctionne ici.

35voto

Matt Briançon Points 725

S'appuyer sur Potr Czachur 's réponse Les situations impliquant des ForeignKeys sont plus compliquées et doivent être traitées de manière légèrement différente.

(L'exemple suivant s'appuie sur le common y specific Les applications auxquelles il est fait référence dans la présente réponse).

# common/models.py

class Cat(models.Model):
    # ...

class Toy(models.Model):
    belongs_to = models.ForeignKey(Cat)
    # ...

deviendrait alors

# common/models.py

from specific.models import Cat

class Toy(models.Model):
    belongs_to = models.ForeignKey(Cat)
    # ...

# specific/models.py

class Cat(models.Model):
    # ...

Running

./manage.py schemamigration common --auto
./manage.py schemamigration specific --auto # or --initial

générerait les migrations suivantes (j'ignore intentionnellement les changements de type de contenu de Django - voir la réponse précédemment référencée pour savoir comment gérer cela) :

# common/migrations/0009_auto__del_cat.py

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.delete_table('common_cat')
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['specific.Cat']))

    def backwards(self, orm):
        db.create_table('common_cat', (
            # ...
        ))
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['common.Cat']))

# specific/migrations/0004_auto__add_cat.py

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.create_table('specific_cat', (
            # ...
        ))

    def backwards(self, orm):
        db.delete_table('specific_cat')

Comme vous pouvez le voir, le FK doit être modifié pour faire référence à la nouvelle table. Nous devons ajouter une dépendance afin de connaître l'ordre dans lequel les migrations seront appliquées (et donc que la table existe avant que nous essayions d'y ajouter un FK) mais nous devons également nous assurer que le retour en arrière fonctionne également car la dépendance s'applique dans le sens inverse .

# common/migrations/0009_auto__del_cat.py

class Migration(SchemaMigration):

    depends_on = (
        ('specific', '0004_auto__add_cat'),
    )

    def forwards(self, orm):
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['specific.Cat']))

    def backwards(self, orm):
        db.rename_table('specific_cat', 'common_cat')
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['common.Cat']))

# specific/migrations/0004_auto__add_cat.py

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.rename_table('common_cat', 'specific_cat')

    def backwards(self, orm):
        pass

Par le Documentation sur le sud , depends_on veillera à ce que 0004_auto__add_cat s'exécute avant 0009_auto__del_cat lors de la migration vers l'avant mais dans le ordre inverse lors de la migration vers l'arrière . Si nous avons laissé db.rename_table('specific_cat', 'common_cat') dans le specific rollback, le common Le retour en arrière échouerait lors de la tentative de migration de la clé étrangère car la table référencée n'existerait pas.

J'espère que cette solution est plus proche d'une situation réelle que les solutions existantes et que quelqu'un la trouvera utile. À la vôtre !

7voto

Daniel Roseman Points 199743

Les modèles ne sont pas très étroitement liés aux applications, leur déplacement est donc assez simple. Django utilise le nom de l'application dans le nom de la table de la base de données, donc si vous voulez déplacer votre application, vous pouvez soit renommer la table de la base de données via une commande SQL ALTER TABLE ou - encore plus simple - utilisez simplement l'instruction db_table paramètre dans l'interface de votre modèle Meta pour faire référence à l'ancien nom.

Si vous avez utilisé des ContentTypes ou des relations génériques dans votre code jusqu'à présent, vous voudrez probablement renommer la fonction app_label du type de contenu pointant vers le modèle qui se déplace, afin que les relations existantes soient préservées.

Bien entendu, si vous n'avez aucune donnée à préserver, le plus simple est de supprimer complètement les tables de la base de données et de lancer l'opération suivante ./manage.py syncdb encore.

4voto

Ihor Kaharlichenko Points 1659

Voici une autre correction à l'excellente solution de Potr. Ajoutez ce qui suit à spécifique/0003_créer_cat

depends_on = (
    ('common', '0002_create_cat'),
)

Si cette dépendance n'est pas définie, South ne garantira pas que l'option common_cat existe au moment où spécifique/0003_créer_cat est exécuté, en lançant un django.db.utils.OperationalError: no such table: common_cat erreur sur vous.

Les migrations vers le Sud en ordre lexicographique sauf si la dépendance est explicitement définie. Puisque common vient avant specific tous les common Les migrations de Potr seraient exécutées avant le renommage des tables, donc cela ne se reproduirait probablement pas dans l'exemple original montré par Potr. Mais si vous renommez common à app2 y specific à app1 vous rencontrerez ce problème.

3voto

Tim Sutton Points 241

L'utilisation de la réponse originale de @Potr ci-dessus n'a pas fonctionné pour moi avec South 0.8.1 et Django 1.5.1. pour moi sur South 0.8.1 et Django 1.5.1. Je publie ci-dessous ce qui a Je publie ci-dessous ce qui a fonctionné pour moi dans l'espoir que cela soit utile à d'autres.

from south.db import db
from south.v2 import SchemaMigration
from django.db import models

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('common_cat', 'specific_cat') 

        if not db.dry_run:
             db.execute(
                "update django_content_type set app_label = 'specific' where "
                " app_label = 'common' and model = 'cat';")

    def backwards(self, orm):
        db.rename_table('specific_cat', 'common_cat')
            db.execute(
                "update django_content_type set app_label = 'common' where "
                " app_label = 'specific' and model = 'cat';")

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X