Pregunta

Tengo una aplicación de django con cuatro modelos en ella.Ahora me doy cuenta de que uno de estos modelos debe ser en una aplicación separada.Yo tengo el sur instalado para las migraciones, pero no creo que esto es algo que se puede gestionar de forma automática.¿Cómo puedo migrar uno de los modelos de la antigua aplicación en una nueva?

Además, tenga en mente que voy a necesitar esto para ser un proceso repetible, por lo que puedo migrar el sistema de producción y tal.

¿Fue útil?

Solución

Cómo migrar el uso del sur.

Digamos que tenemos dos aplicaciones:comunes y específicos:

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

Ahora queremos mover modelo común.modelos.gato de aplicación específica (precisamente a lo más específico.modelos.cat).Primero hacer los cambios en el código fuente y, a continuación, ejecute:

$ 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

Ahora tenemos que editar los archivos de migración:

#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

Ahora tanto las migraciones son conscientes del cambio y la vida apesta un poco menos :-) La configuración de esta relación entre las migraciones es la clave del éxito.Ahora si que debes hacer:

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

va a hacer la migración, y

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

migrar las cosas.

Observe que para la actualización de esquema que he utilizado comunes de la app y de la rebaja, he utilizado una aplicación específica.Eso es porque la manera en que la dependencia de aquí trabaja.

Otros consejos

Para construir sobre Potr Czachur 's respuesta , situaciones que implican ForeignKeys son más complicados y deben ser manejados de forma ligeramente diferente.

(El siguiente ejemplo se basa en las aplicaciones common y specific se refiere a la en la respuesta actual).

# common/models.py

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

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

cambiaría entonces a

# common/models.py

from specific.models import Cat

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

# specific/models.py

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

Ejecutar

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

generaría los siguientes (las migraciones estoy ignorando intencionadamente Django ContentType cambia-véase la respuesta referencia anteriormente para saber cómo manejar eso):

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

Como se puede ver, el FK debe ser modificado para hacer referencia a la nueva tabla. Tenemos que añadir una dependencia por lo que sabemos que el orden en el que se aplicarán las migraciones (y por lo tanto que existirá la mesa antes de tratar de añadir un FK a ella), pero también hay que asegurarse de que se vaya hacia atrás también funciona porque < strong> la dependencia se aplica en la dirección inversa .

# 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

Sur documentación , depends_on se asegurará de que se ejecuta antes de 0004_auto__add_cat 0009_auto__del_cat al migrar hacia delante , pero en el orden opuesto al migrar hacia atrás . Si dejamos db.rename_table('specific_cat', 'common_cat') en la reversión specific, la reversión common fracasaría al intentar migrar el ForeignKey porque no existiría la tabla tabla de referencia.

Esperamos que esto se acerca más a una situación "mundo real" que las soluciones existentes y alguien encontrarán útil esta información. Saludos!

Los modelos no están muy estrechamente vinculadas a las aplicaciones, por lo que moverse es bastante simple. Django usa el nombre de la aplicación en el nombre de la tabla de base de datos, por lo que si desea mover su aplicación que puede o bien cambiar el nombre de la tabla de base de datos a través de un comunicado ALTER TABLE SQL o - aún más simple - sólo tiene que utilizar el db_table parámetro en clase Meta de su modelo para referirse al antiguo nombre.

Si ha utilizado TiposContenido o relaciones genéricas cualquier parte del código hasta el momento, es probable que desee cambiar el nombre del app_label del señalador contenttype en el modelo que se está moviendo, por lo que las relaciones existentes se conservan.

Por supuesto, si usted no tiene ningún dato en absoluto para preservar, lo más fácil de hacer es eliminar las tablas de base de datos y ejecutar completamente ./manage.py syncdb de nuevo.

He aquí una solución más que excelente solución de Potr. Agregue lo siguiente a específica / 0003_create_cat

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

A menos que esta dependencia se establece Sur no garantiza que la tabla common_cat existe en el momento en específica / 0003_create_cat es correr, lanzar un error django.db.utils.OperationalError: no such table: common_cat en usted.

Sur corre migraciones en lexicográfico fin menos que la dependencia es explícitamente conjunto. Desde common viene antes specific todas las migraciones del common serían ubicadas antes del cambio de nombre de la tabla, por lo que probablemente no se reproducirían en el ejemplo original mostrada por Potr. Sin embargo, si cambia el nombre common a app2 y specific a app1 que se ejecutará en este problema.

El proceso que he actualmente se asentaron en desde que he estado aquí unas cuantas veces y decidió formalizar la misma.

Este fue construido originalmente en de Potr Czachur respuesta y de Matt Briançon respuesta , 0.8.4 usando Sur

Paso 1. Descubrir niño relaciones de clave externa

# 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>]

Así que en este caso prolongado, hemos descubierto otro modelo relacionado como:

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

    ...

Paso 2. Crear migraciones

# 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

Paso de control 3. Fuente: Registrar los cambios hasta el momento

.

Hace que sea un proceso más repetible si llegas a tener conflictos de fusión como compañeros que escriben las migraciones en las aplicaciones actualizadas.

Paso 4. Añadir las dependencias entre las migraciones.

Básicamente create_kittycat depende del estado actual de todo, y todo depende entonces de 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'),
    )
    ...

Paso 5. La mesa de cambio de nombre que queremos hacer.

# 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

Paso 6. Sólo si necesita hacia atrás () de trabajo y obtener una KeyError corriendo hacia atrás.

# 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']})
        },
        ...
    }

Paso 7. Prueba de ello - lo que funciona para mí puede no ser suficiente para su situación de la vida real:)

python manage.py migrate

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

Así que usando la respuesta original de la @Potr anterior no funcionó para mí en Sur 0.8.1 y 1.5.1 Django. Estoy publicar lo que hizo funciona para mí más adelante, con la esperanza de que sea útil a los demás.

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

Me voy a dar una versión más explícita de una de las cosas Daniel Roseman sugirió en su respuesta ...

Si acaba de cambiar el atributo Meta db_table del modelo se ha mudado a apuntar al nombre de tabla existente (en lugar del nuevo nombre de Django se lo daría si se le cayó e hizo un syncdb), entonces usted puede evitar las migraciones Sur complicados. por ejemplo:

Original:

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

Después de movimiento:

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

Ahora sólo tiene que hacer una migración de datos para actualizar el app_label para MyModel en la tabla django_content_type y usted debe ser bueno para ir ...

Ejecutar ./manage.py datamigration django update_content_type continuación, editar el archivo que crea Sur para usted:

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()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top