Frage

Ich habe eine django App mit vier Modellen drin. Ich weiß jetzt, dass eines dieser Modelle in einer separaten Anwendung sein sollte. Ich Süden für Migrationen installiert haben, aber ich glaube nicht, das etwas ist, es automatisch verarbeiten kann. Wie kann ich aus der alten App in eine neue eines der Modelle migrieren?

Auch bedenken Sie, dass ich werde das brauche ein wiederholbarer Prozess zu sein, so dass ich das Produktionssystem und so wandern können.

War es hilfreich?

Lösung

Wie migrieren Süden mit.

Lassen Sie uns sagen wir haben zwei Anwendungen: gemeinsame und spezifische:

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

Jetzt wollen wir Modell common.models.cat auf bestimmte App (genau specific.models.cat) bewegen. Zuerst die Änderungen im Quellcode und führen Sie:

$ 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

Jetzt müssen wir beiden Migrationsdateien bearbeiten:

#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

Jetzt sind beide Apps Migration bewusst die Veränderung und das Leben saugt nur ein wenig weniger :-) Diese Beziehung zwischen Migrationen Einstellung ist der Schlüssel zum Erfolg. Nun, wenn Sie tun:

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

wird sowohl die Migration tun, und

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

werden die Dinge wandern nach unten.

Beachten Sie, dass ich für das Upgrade von Schema gemeinsame App verwendet und für die Herabstufung, habe ich bestimmte App. Das liegt daran, wie hier die Abhängigkeit funktioniert.

Andere Tipps

Potr Czachur 's Antwort , Situationen, die Foreign sind komplizierter und sollte etwas anders gehandhabt werden.

(Das folgende Beispiel baut auf dem common und specific Apps den in der aktuellen Antwort bezeichnet).

# common/models.py

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

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

würde dann ändern

# common/models.py

from specific.models import Cat

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

# specific/models.py

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

Ausführen

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

würde die folgenden die Migrationen erzeugen (ich ignoriere absichtlich Django Content ändert man gesehen zuvor referenzierten Antwort, wie das zu handhaben):

# 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')

Wie Sie sehen können, muss der FK die neue Tabelle verweisen geändert werden. Wir brauchen eine Abhängigkeit hinzuzufügen, so dass wir den Auftrag in dem sich die Wanderungen angewendet wird (und damit, dass die Tabelle existieren, bevor wir versuchen, ein FK, um es hinzuzufügen), aber wir müssen auch dafür sorgen, rollen arbeitet nach hinten zu, weil < die Abhängigkeit gilt in umgekehrter Richtung strong> .

# 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

Per Süd Dokumentation wird depends_on sicherzustellen, dass 0004_auto__add_cat läuft vor 0009_auto__del_cat , wenn nach vorne , aber in der kehrter Reihenfolge der Migration bei der Migration nach hinten . Wenn wir db.rename_table('specific_cat', 'common_cat') im specific Rollback links, würde der common Rollbacks scheitert beim Versuch, die ForeignKey zu migrieren, da die Tabelle referenzierten Tabelle nicht existieren würde.

Hoffentlich ist näher an einer „realen Welt“ Situation als die bestehenden Lösungen und jemand wird dies hilfreich. Cheers!

Die Modelle sind nicht sehr eng gekoppelt an Apps, so bewegend ist ziemlich einfach. Django verwendet den Namen der App auf den Namen der Datenbanktabelle, so dass, wenn Sie Ihre App verschieben möchten, können Sie entweder die Datenbanktabelle über eine SQL ALTER TABLE Anweisung umbenennen oder - noch einfacher - nur die db_table Parameter in Ihrem Modell der Meta Klasse auf den alten Namen verweisen.

Wenn Sie Content oder generische Beziehungen überall in Ihrem Code bisher verwendet haben, werden Sie wahrscheinlich wollen die app_label des content zeigt auf das Modell umzubenennen, die sich bewegt, so dass die bestehenden Beziehungen erhalten bleiben.

Natürlich, wenn Sie irgendwelche Daten nicht zu erhalten, ist das einfachste, was zu tun ist, um die Datenbanktabellen vollständig fallen zu lassen und läuft ./manage.py syncdb wieder.

Hier ist ein weiteres Update zu Potr ausgezeichneten Lösung. Fügen Sie die folgenden spezifische / 0003_create_cat

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

Es sei denn, diese Abhängigkeit gesetzt Süden wird nicht garantieren, dass die common_cat Tabelle zu dem Zeitpunkt existiert, wenn spezifische / 0003_create_cat ausgeführt wird, um Ihnen einen django.db.utils.OperationalError: no such table: common_cat Fehler zu werfen.

Süd läuft Migrationen in lexikographischer Reihenfolge es sei denn, die Abhängigkeit explizit ist einstellen. Da common vor specific kommt die Migrationen der alle common würde, bevor Tabelle Umbenennung fahren werden, so wäre es wahrscheinlich nicht in dem ursprünglichen Beispiel durch Potr gezeigt reproduzieren. Aber wenn Sie common umbenennen app2 und specific app1 Sie in dieses Problem wird.

Der Prozess, den ich zur Zeit habe lasse mich auf, da ich ein paar Mal wieder hier war, und beschlossen, es zu formalisieren.

Dies war ursprünglich gebaut auf Potr Czachur Antwort und Matt Briançon Antwort , mit Süd-0.8.4

Schritt 1. Entdecken Kind Fremdschlüsselbeziehungen

# Caution: This finds OneToOneField and ForeignKey.
# I don't know if this finds all the ways of specifying ManyToManyField.
# Hopefully Django or South throw errors if you have a situation like that.
>>> Cat._meta.get_all_related_objects()
[<RelatedObject: common:toy related to cat>,
 <RelatedObject: identity:microchip related to cat>]

So in diesem erweiterten Fall haben wir eine weiteres ähnliches Modell wie entdeckt:

# Inside the "identity" app...
class Microchip(models.Model):

    # In reality we'd probably want a ForeignKey, but to show the OneToOneField
    identifies = models.OneToOneField(Cat)

    ...

Schritt 2. Erstellen Sie Migrationen

# Create the "new"-ly renamed model
# Yes I'm changing the model name in my refactoring too.
python manage.py schemamigration specific create_kittycat --auto

# Drop the old model
python manage.py schemamigration common drop_cat --auto

# Update downstream apps, so South thinks their ForeignKey(s) are correct.
# Can skip models like Toy if the app is already covered
python manage.py schemamigration identity update_microchip_fk --auto

Schritt 3. Quellcodeverwaltung: Änderungen durch so weit

.

Macht es ein wiederholbarer Prozess, wenn Sie in Merge Konflikte wie Teamkollegen laufen Migrationen auf den aktualisierten Anwendungen zu schreiben.

Schritt 4. Fügen Sie Abhängigkeiten zwischen den Migrationen.

Im Grunde create_kittycat hängt vom aktuellen Zustand von allem, und alles hängt dann von create_kittycat.

# create_kittycat
class Migration(SchemaMigration):

    depends_on = (
        # Original model location
        ('common', 'the_one_before_drop_cat'),

        # Foreign keys to models not in original location
        ('identity', 'the_one_before_update_microchip_fk'),
    )
    ...


# drop_cat
class Migration(SchemaMigration):

    depends_on = (
        ('specific', 'create_kittycat'),
    )
    ...


# update_microchip_fk
class Migration(SchemaMigration):

    depends_on = (
        ('specific', 'create_kittycat'),
    )
    ...

Schritt 5. Die Tabelle umbenennen ändern wollen wir machen.

# create_kittycat
class Migration(SchemaMigration):

    ...

    # Hopefully for create_kittycat you only need to change the following
    # 4 strings to go forward cleanly... backwards will need a bit more work.
    old_app = 'common'
    old_model = 'cat'
    new_app = 'specific'
    new_model = 'kittycat'

    # You may also wish to update the ContentType.name,
    # personally, I don't know what its for and
    # haven't seen any side effects from skipping it.

    def forwards(self, orm):

        db.rename_table(
            '%s_%s' % (self.old_app, self.old_model),
            '%s_%s' % (self.new_app, self.new_model),
        )

        if not db.dry_run:
            # For permissions, GenericForeignKeys, etc to work properly after migrating.
            orm['contenttypes.contenttype'].objects.filter(
                app_label=self.old_app,
                model=self.old_model,
            ).update(
                app_label=self.new_app,
                model=self.new_model,
            )

        # Going forwards, should be no problem just updating child foreign keys
        # with the --auto in the other new South migrations

    def backwards(self, orm):

        db.rename_table(
            '%s_%s' % (self.new_app, self.new_model),
            '%s_%s' % (self.old_app, self.old_model),
        )

        if not db.dry_run:
            # For permissions, GenericForeignKeys, etc to work properly after migrating.
            orm['contenttypes.contenttype'].objects.filter(
                app_label=self.new_app,
                model=self.new_model,
            ).update(
                app_label=self.old_app,
                model=self.old_model,
            )

        # Going backwards, you probably should copy the ForeignKey
        # db.alter_column() changes from the other new migrations in here
        # so they run in the correct order.
        #
        # Test it! See Step 6 for more details if you need to go backwards.
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['common.Cat']))
        db.alter_column('identity_microchip', 'identifies_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['common.Cat']))


# drop_cat
class Migration(SchemaMigration):

    ...

    def forwards(self, orm):
        # Remove the db.delete_table(), if you don't at Step 7 you'll likely get
        # "django.db.utils.ProgrammingError: table "common_cat" does not exist"

        # Leave existing db.alter_column() statements here
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['specific.KittyCat']))

    def backwards(self, orm):
        # Copy/paste the auto-generated db.alter_column()
        # into the create_kittycat migration if you need backwards to work.
        pass


# update_microchip_fk
class Migration(SchemaMigration):

    ...

    def forwards(self, orm):
        # Leave existing db.alter_column() statements here
        db.alter_column('identity_microchip', 'identifies_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['specific.KittyCat']))

    def backwards(self, orm):
        # Copy/paste the auto-generated db.alter_column()
        # into the create_kittycat migration if you need backwards to work.
        pass

Schritt 6. Nur wenn Sie rückwärts () zu arbeiten und ein KeyError rückwärts laufen zu bekommen.

# the_one_before_create_kittycat
class Migration(SchemaMigration):

    # You many also need to add more models to South's FakeORM if you run into
    # more KeyErrors, the trade-off chosen was to make going forward as easy as
    # possible, as that's what you'll probably want to do once in QA and once in
    # production, rather than running the following many times:
    #
    # python manage.py migrate specific <the_one_before_create_kittycat>

    models = {
        ...
        # Copied from 'identity' app, 'update_microchip_fk' migration
        u'identity.microchip': {
            'Meta': {'object_name': 'Microchip'},
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
            'identifies': ('django.db.models.fields.related.OneToOneField', [], {to=orm['specific.KittyCat']})
        },
        ...
    }

Schritt 7. Testen Sie es - was für mich kann für Ihre Lebenssituation nicht genug sein:)

python manage.py migrate

# If you need backwards to work
python manage.py migrate specific <the_one_before_create_kittycat>

So mit der die ursprüngliche Antwort von @Potr oben nicht funktioniert für mich auf der South 0.8.1 und Django 1.5.1. Ich veröffentliche was tat Arbeit für mich unten in der Hoffnung, dass es für andere hilfreich ist.

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';")

Ich werde eine explizitere Version eines der Dinge, Daniel Roseman in seiner Antwort vorgeschlagen geben ...

Wenn Sie nur die db_table ändern Meta-Attribut des Modells Sie verschoben haben, um die vorhandenen Tabellennamen Punkt (anstelle des neuen Namens Django würde es geben, wenn Sie fallen gelassen und haben eine syncdb), dann können Sie komplizierte Süd-Migration zu vermeiden. zB:

Original:

# app1/models.py
class MyModel(models.Model):
    ...

Nach dem Umzug:

# app2/models.py
class MyModel(models.Model):
    class Meta:
        db_table = "app1_mymodel"

Jetzt müssen Sie nur eine Datenmigration tun, um die app_label für MyModel in der django_content_type Tabelle zu aktualisieren, und Sie sollten gut zu gehen ...

Ausführen ./manage.py datamigration django update_content_type dann bearbeiten Sie die Datei, die Süd für Sie erstellt:

def forwards(self, orm):
    moved = orm.ContentType.objects.get(app_label='app1', model='mymodel')
    moved.app_label = 'app2'
    moved.save()

def backwards(self, orm):
    moved = orm.ContentType.objects.get(app_label='app2', model='mymodel')
    moved.app_label = 'app1'
    moved.save()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top