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

Apr*_*che 125 migration django django-south

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

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

Pot*_*hur 183

如何使用南方迁移.

让我们说我们有两个应用程序:常见和具体:

myproject/
|-- common
|   |-- migrations
|   |   |-- 0001_initial.py
|   |   `-- 0002_create_cat.py
|   `-- models.py
`-- specific
    |-- migrations
    |   |-- 0001_initial.py
    |   `-- 0002_create_dog.py
    `-- models.py
Run Code Online (Sandbox Code Playgroud)

现在我们想将模型common.models.cat移动到特定的应用程序(精确到特定的.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
Run Code Online (Sandbox Code Playgroud)

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

#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')
Run Code Online (Sandbox Code Playgroud)
#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
Run Code Online (Sandbox Code Playgroud)

现在,这两个应用程序迁移都意识到了这一变化,生活变得简单了一些:-)在迁移之间设置这种关系是成功的关键.现在,如果你这样做:

python manage.py migrate common
 > specific: 0003_create_cat
 > common: 0003_drop_cat
Run Code Online (Sandbox Code Playgroud)

将进行迁移,和

python manage.py migrate specific 0002_create_dog
 < common: 0003_drop_cat
 < specific: 0003_create_cat
Run Code Online (Sandbox Code Playgroud)

会把事情搞砸.

请注意,为了升级架构,我使用了常见的应用程序,并且为了降级,我使用了特定的应用程序.那是因为这里的依赖关系如何运作.

  • 您可能还需要在django_content_type表中进行数据迁移. (11认同)
  • 为了访问`orm ['contenttypes.contenttype']`,你还需要在`schemamigration`命令中添加`--freeze contenttypes`选项. (2认同)

Mat*_*çon 35

基于Potr Czachur答案,涉及ForeignKeys的情况更复杂,应该稍微区别对待.

(以下示例以当前答案中引用的commonspecific应用程序为基础).

# common/models.py

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

class Toy(models.Model):
    belongs_to = models.ForeignKey(Cat)
    # ...
Run Code Online (Sandbox Code Playgroud)

然后会改为

# common/models.py

from specific.models import Cat

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

# specific/models.py

class Cat(models.Model):
    # ...
Run Code Online (Sandbox Code Playgroud)

运行

./manage.py schemamigration common --auto
./manage.py schemamigration specific --auto # or --initial
Run Code Online (Sandbox Code Playgroud)

将生成以下迁移(我故意忽略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')
Run Code Online (Sandbox Code Playgroud)

如您所见,必须更改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
Run Code Online (Sandbox Code Playgroud)

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

希望这比现有的解决方案更接近"现实世界"的情况,有人会觉得这很有帮助.干杯!


Dan*_*man 7

模型与应用程序的联系并不紧密,因此移动非常简单.Django在数据库表的名称中使用应用程序名称,因此如果要移动应用程序,可以通过SQL ALTER TABLE语句重命名数据库表,或者 - 甚至更简单 - 只需使用模型类中的db_table参数Meta来引用旧名.

如果到目前为止在代码中的任何位置使用了ContentTypes或泛型关系,则可能需要重命名app_label指向正在移动的模型的contenttype,以便保留现有关系.

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

  • 我如何通过南迁移来做到这一点? (2认同)