我有一个 Django 应用程序,里面有四个模型。我现在意识到这些模型之一应该位于单独的应用程序中。我确实安装了 South 用于迁移,但我不认为这是它可以自动处理的事情。如何将旧应用程序中的模型之一迁移到新应用程序中?

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

有帮助吗?

解决方案

如何利用南方迁移。

假设我们有两个应用:普通和特殊:

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

现在我们要移动模型common.models.cat特定的应用程序(精确specific.models.cat)。 首先使在源代码中的变化,然后运行:

$ 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

现在,我们需要编辑两个迁移文件:

#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

现在这两个应用迁移都知道的变化和生活吮吸只是少一点:-) 设置迁移之间的这种关系是关键的成功。 现在,如果你这样做:

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

将两者都做迁移,和

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

将迁移下来。

<强>注意,用于模式的升级我使用的常见的应用程序和为降级,我使用的特定应用程序。这是因为这里是如何依赖的作品。

其他提示

建立在 波特·查楚尔回答, ,涉及外键的情况更加复杂,处理方式应该略有不同。

(以下示例建立在 commonspecific 应用程序在当前答案中提到)。

# common/models.py

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

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

然后将更改为

# 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 更改 - 请参阅之前引用的答案以了解如何处理该问题):

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

正如您所看到的,必须更改 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

根据 南方文档, depends_on 将确保 0004_auto__add_cat 之前运行 0009_auto__del_cat 向前迁移时 但在 向后迁移时的顺序相反. 。如果我们离开 db.rename_table('specific_cat', 'common_cat') 在里面 specific 回滚,即 common 尝试迁移外键时回滚将失败,因为表引用的表不存在。

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

的模型不很紧密耦合到应用程序,这样的移动是相当简单的。 Django的数据库表的名称使用的应用程序的名称,所以如果你想将你的应用程序,你可以通过SQL ALTER TABLE语句重命名数据库表,或者 - 更简单 - 只需使用的 db_table参数在模型中的Meta类以引用旧名称。

如果你已经在你的代码中使用CONTENTTYPES或一般关系的任何地方,到目前为止,你可能要重新命名那个在动模型中的contentType指向的app_label,让现有的关系被保留。

当然,如果你不带任何数据保护,最容易做的事情是完全删除数据库表,然后再次运行./manage.py syncdb

下面是一个更固定到Potr的最佳选择。添加以下为特定/ 0003_create_cat

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

除非这种依赖关系设置南将无法保证common_cat表格存在于时候的特定/ 0003_create_cat 运行,在你抛出一个django.db.utils.OperationalError: no such table: common_cat错误。

南运行在词典编纂顺序迁移除非依赖性是显式组。由于commonspecific之前的所有common的迁移将表重命名之前得到运行,所以它可能不会通过Potr所示的原来的例子重现。但是,如果您重命名commonapp2specificapp1你会遇到这个问题。

在过程中我目前看中了,因为我已经回来了几次,决定正式化。

此最初建立在 Potr Czachur的回答马特布里昂松的回答, 使用南0.8.4

步骤1.发现孩子外键关系

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

    ...

步骤2.创建迁移

# 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

步骤3.源控制:到目前为止提交更改

使得它更可重复的过程,如果你遇到这样的队友更新的应用程序编写迁移合并冲突。

步骤4.添加的依赖关系之间的迁移。

基本上create_kittycat取决于一切的当前状态,然后一切取决于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'),
    )
    ...

步骤5.表重命名改变我们想使

# 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

步骤6.只有当你需要向后()的工作,并得到一个KeyError异常向后运行。

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

步骤7.测试它 - 我什么工作你现实生活中的情况可能是不够的:)

python manage.py migrate

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

因此,使用从该@Potr原始响应上述不工作 我在南方0.8.1和1.5.1的Django。我张贴做什么 希望对我的工作下面,它是帮助他人。

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

我想给的东西丹尼尔·罗斯曼在他的回答提出一个更明确的版本...

如果你只需要改变你已经移到指向现有的表名(而不是新名称的Django会给它,如果你掉,做了db_table)模型的syncdb元属性,那么就可以避免复杂的南方迁移。例如:

原件:

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

移动之后:

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

现在,你只需要做一个数据迁移到更新app_labelMyModeldjango_content_type表,你应该是好去...

运行./manage.py datamigration django update_content_type然后编辑该南为您创建的文件:

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()
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top