如何将模型从一个django应用程序迁移到一个新应用程序中?

如何将模型从一个django应用程序迁移到一个新应用程序中?,django,migration,django-south,Django,Migration,Django South,我有一个django应用程序,里面有四种型号。我现在意识到其中一个模型应该在一个单独的应用程序中。我确实为迁移安装了south,但我认为这不是它可以自动处理的。如何将其中一个模型从旧应用迁移到新应用 另外,请记住,我需要这是一个可重复的过程,以便我可以迁移生产系统等等 模型与应用程序的耦合不是很紧密,因此移动相当简单。Django在数据库表的名称中使用应用程序名称,因此,如果您想移动应用程序,您可以通过SQLALTER table语句重命名数据库表,或者更简单地说,只需使用模型的Meta类中的来

我有一个django应用程序,里面有四种型号。我现在意识到其中一个模型应该在一个单独的应用程序中。我确实为迁移安装了south,但我认为这不是它可以自动处理的。如何将其中一个模型从旧应用迁移到新应用


另外,请记住,我需要这是一个可重复的过程,以便我可以迁移生产系统等等

模型与应用程序的耦合不是很紧密,因此移动相当简单。Django在数据库表的名称中使用应用程序名称,因此,如果您想移动应用程序,您可以通过SQL
ALTER table
语句重命名数据库表,或者更简单地说,只需使用模型的
Meta
类中的来引用旧名称

如果到目前为止您在代码中的任何地方使用过ContentTypes或泛型关系,则可能需要重命名指向移动模型的contenttype的
app\u标签
,以便保留现有关系

当然,如果您没有任何要保留的数据,最简单的方法是完全删除数据库表,然后再次运行
/manage.py syncdb

如何使用south进行迁移。 假设我们有两个应用程序:通用和专用:

myproject/
|--普通的
||——迁移
|| |--0001|u首字母.py
||`--0002_创建_cat.py
|`--models.py
`--具体的
|--迁移
||--0001_首字母.py
|`--0002_创建_dog.py
`--models.py
现在我们想将model common.models.cat移动到特定的应用程序(精确到特定的.models.cat)。 首先在源代码中进行更改,然后运行:

$python manage.py特定于schemamigration的create\u cat--auto
+添加了型号“特定.cat”
$python manage.py schemamigration common drop_cat--auto
-已删除型号“common.cat”
我的项目/
|--普通的
||——迁移
|| |--0001|u首字母.py
|| |--0002_创建_cat.py
||`--0003_drop_cat.py
|`--models.py
`--具体的
|--迁移
||--0001_首字母.py
||--0002_创建_dog.py
|`--0003_创建_cat.py
`--models.py
现在我们需要编辑两个迁移文件:

#0003_create_cat:替换现有正向和反向代码
#只用一句话:
def转发(自身、orm):
db.rename_table('common_cat','specific_cat'))
如果不是db.dry_运行:
#用于在迁移后使权限正常工作
orm['contenttypes.contenttype'].objects.filter(
app_label='common',
“猫”型,
).更新(应用程序标签='specific')
def向后(自身,orm):
db.rename_table('specific_cat','common_cat'))
如果不是db.dry_运行:
#用于在迁移后使权限正常工作
orm['contenttypes.contenttype'].objects.filter(
app_label='specific',
“猫”型,
).update(应用程序标签='common')

#0003_drop_cat:替换现有正向和反向代码
#只用一句话;添加依赖项:
取决于(
('specific'、'0003_create_cat'),
)
def转发(自身、orm):
通过
def向后(自身,orm):
通过
现在,两个应用程序迁移都意识到了变化,生活变得更糟了:-) 在迁移之间设置这种关系是成功的关键。 现在如果你这样做了:

python manage.py迁移通用
>具体:0003\u创建\u目录
>普通:0003_下降_猫
将执行迁移和

python manage.py迁移特定0002\u创建\u狗
会把东西移下来

请注意,对于架构升级,我使用了普通应用程序;对于降级,我使用了特定应用程序。这是因为这里的依赖关系是如何工作的。

要建立在的基础上,涉及外键的情况更为复杂,应该以稍微不同的方式处理

(以下示例基于当前答案中提到的
通用
特定
应用程序)

然后将更改为

# common/models.py

from specific.models import Cat

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

# specific/models.py

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

./manage.py schemamigration common --auto
./manage.py schemamigration specific --auto # or --initial
将在迁移过程中生成以下内容(我有意忽略Django ContentType更改,请参阅前面引用的答案以了解如何处理该更改):

如您所见,必须修改FK以引用新表。我们需要添加一个依赖项,以便知道迁移的应用顺序(因此,在我们尝试向表添加FK之前,该表将存在),但我们还需要确保向后滚动也能正常工作,因为依赖项的应用方向相反

# 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
根据,向前迁移时,
dependens\u
将确保
0004\u auto\u add\u cat
0009\u auto\u del\u cat
之前运行,向后迁移时则以相反的顺序运行。如果我们在
specific
rollback中离开
db.rename_table('specific_cat','common_cat')
,在尝试迁移ForeignKey时,
common
回滚将失败,因为表引用的表不存在


希望这比现有的解决方案更接近“真实世界”的情况,并且有人会发现这很有帮助。干杯

所以使用上面@Potr的原始响应不起作用 对于我来说,南方0.8.1和Django 1.5.1。我正在发布你做了什么 在下面为我工作,希望对其他人有帮助

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

我将对丹尼尔·罗斯曼在回答中提出的一个问题给出一个更明确的版本

如果您只是更改已移动模型的
db_table
Meta属性,以指向现有的表名(如果您删除并执行
syncdb
,Django会给出新名称,而不是新名称),则可以避免复杂的南部迁移。例如:

原件:

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

# app2/models.py
class MyModel(models.Model):
    class Meta:
        db_table = "app1_mymodel"
现在你只需要做一个数据迁移
# app1/models.py
class MyModel(models.Model):
    ...
# app2/models.py
class MyModel(models.Model):
    class Meta:
        db_table = "app1_mymodel"
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()
depends_on = (
    ('common', '0002_create_cat'),
)
# 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>]
# 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)

    ...
# 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
# 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'),
    )
    ...
# 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
# 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']})
        },
        ...
    }
python manage.py migrate

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