How do I migrate a model out of one django app and into a new one? How do I migrate a model out of one django app and into a new one? django django

How do I migrate a model out of one django app and into a new one?


How to migrate using south.

Lets say we got two apps: common and specific:

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

Now we want to move model common.models.cat to specific app (precisely to specific.models.cat).First make the changes in the source code and then run:

$ 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

Now we need to edit both migration files:

#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):    passdef backwards(self, orm):    pass

Now both apps migrations are aware of the change and life sucks just a little less :-)Setting this relationship between migrations is key of success.Now if you do:

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

will do both migration, and

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

will migrate things down.

Notice that for upgrading of schema I used common app and for downgrading, I used specific app. That's because how the dependency here works.


To build on Potr Czachur's answer, situations that involve ForeignKeys are more complicated and should be handled slightly differently.

(The following example builds on the common and specific apps referred to the in the current answer).

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

would then change to

# common/models.pyfrom specific.models import Catclass Toy(models.Model):    belongs_to = models.ForeignKey(Cat)    # ...# specific/models.pyclass Cat(models.Model):    # ...

Running

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

would generate the following the migrations (I'm intentionally ignoring Django ContentType changes—see previously referenced answer for how to handle that):

# common/migrations/0009_auto__del_cat.pyclass 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.pyclass Migration(SchemaMigration):    def forwards(self, orm):        db.create_table('specific_cat', (            # ...        ))    def backwards(self, orm):        db.delete_table('specific_cat')

As you can see, the FK must be altered to reference the new table. We need to add a dependency so that we know the order in which the migrations will be applied (and thus that the table will exist before we try to add a FK to it) but we also need to make sure rolling backwards works too because the dependency applies in the reverse direction.

# common/migrations/0009_auto__del_cat.pyclass 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.pyclass Migration(SchemaMigration):    def forwards(self, orm):        db.rename_table('common_cat', 'specific_cat')    def backwards(self, orm):        pass

Per the South documentation, depends_on will ensure that 0004_auto__add_cat runs before 0009_auto__del_cat when migrating forwards but in the opposite order when migrating backwards. If we left db.rename_table('specific_cat', 'common_cat') in the specific rollback, the common rollback would fail when trying to migrate the ForeignKey because the table referenced table wouldn't exist.

Hopefully this is closer to a "real world" situation than the existing solutions and someone will find this helpful. Cheers!


Models aren't very tightly coupled to apps, so moving is fairly simple. Django uses the app name in the name of the database table, so if you want to move your app you can either rename the database table via an SQL ALTER TABLE statement, or - even simpler - just use the db_table parameter in your model's Meta class to refer to the old name.

If you've used ContentTypes or generic relations anywhere in your code so far, you will probably want to rename the app_label of the contenttype pointing at the model that's moving, so that existing relations are preserved.

Of course, if you don't have any data at all to preserve, the easiest thing to do is to drop the database tables completely and run ./manage.py syncdb again.