我想知道Django south是否可以进行以下迁移,并且仍然保留数据.

之前:

我目前有两个应用程序,一个叫TV,一个叫Movies,每个都有VideoFile模型(这里简化):

tv/models.py:

class VideoFile(models.Model):
    show = models.ForeignKey(Show, blank=True, null=True)
    name = models.CharField(max_length=1024, blank=True)
    size = models.IntegerField(blank=True, null=True)
    ctime = models.DateTimeField(blank=True, null=True)

movies/models.py:

class VideoFile(models.Model):
    movie = models.ForeignKey(Movie, blank=True, null=True)
    name = models.CharField(max_length=1024, blank=True)
    size = models.IntegerField(blank=True, null=True)
    ctime = models.DateTimeField(blank=True, null=True)

之后:

由于这两个videofile对象非常相似,我想消除重复,在一个名为media的单独应用程序中创建一个新模型,该应用程序包含一个通用videofile类,并使用继承来扩展它:

media/models.py:

class VideoFile(models.Model):
    name = models.CharField(max_length=1024, blank=True)
    size = models.IntegerField(blank=True, null=True)
    ctime = models.DateTimeField(blank=True, null=True)

tv/models.py:

class VideoFile(media.models.VideoFile):
    show = models.ForeignKey(Show, blank=True, null=True)

movies/models.py:

class VideoFile(media.models.VideoFile):
    movie = models.ForeignKey(Movie, blank=True, null=True)

所以我的问题是,我怎样才能在使用django south的情况下实现这一点,并且仍然保持现有数据?

所有这三个应用程序都已经由南方迁移管理,根据南方的文档,将模式和数据迁移结合起来是不好的做法,他们建议这应该在几个步骤内完成.

我认为可以使用如下所示的单独迁移来完成(假设已经创建了media.VideoFile)

  1. 进行模式迁移以重命名将移动到新媒体的tw.VideoFile和Movies.VideoFile中的所有字段.VideoFile模型,可能重命名为OLD_NAME、OLD_SIZE等名称
  2. 要从media.VideoFile继承的到tw.VideoFile和Movies.VideoFile的架构迁移
  3. 将OLD_NAME复制到NAME、将OLD_SIZE复制到SIZE等的数据迁移
  4. 删除旧_uu字段的方案迁移

在我做完所有这些工作之前,你认为这行得通吗?有没有更好的方法?

如果你感兴趣,这个项目在这里进行:http://code.google.com/p/medianav/

推荐答案

Check out response below by Paul for some notes on compatibility with newer versions of Django/South.


这似乎是一个有趣的问题,我正在成为南方的铁杆粉丝,所以我决定稍微研究一下.我在您上面描述的抽象的基础上构建了一个测试项目,并且已经成功地使用South执行了您所询问的migrations.在我们讨论代码之前,有几个注意事项:

  • South文档建议分别进行模式迁移和数据migrations.在这件事上我也跟着做了.

  • 在后端,Django通过在继承模型上自动创建ONEtoONE字段来表示继承表

  • 理解了这一点,我们的南方迁移需要手动正确地处理ONEtoONE字段,然而,在实验中,南方(或者Django本身)似乎不能在多个同名的继承表上创建ONEtoONE文件.正因为如此,我重命名了电影/电视应用程序中的每个子表,使其与其自己的应用程序(即.MovieVideoFile/ShowVideoFile).

  • 在使用实际的数据迁移代码时,South似乎更喜欢首先创建ONEtoONE字段,然后将数据分配给它.在创建期间将数据分配给ONEtoONE字段会导致SOUTH阻塞.(对于南方所有的凉爽来说,这是一个公平的妥协).

所以说了这些之后,我试着记录下发出的控制台命令.必要时我会插入 comments .最后的代码在底部.

命令历史记录

django-admin.py startproject southtest
manage.py startapp movies
manage.py startapp tv
manage.py syncdb
manage.py startmigration movies --initial
manage.py startmigration tv --initial
manage.py migrate
manage.py shell          # added some fake data...
manage.py startapp media
manage.py startmigration media --initial
manage.py migrate
# edited code, wrote new models, but left old ones intact
manage.py startmigration movies unified-videofile --auto
# create a new (blank) migration to hand-write data migration
manage.py startmigration movies videofile-to-movievideofile-data 
manage.py migrate
# edited code, wrote new models, but left old ones intact
manage.py startmigration tv unified-videofile --auto
# create a new (blank) migration to hand-write data migration
manage.py startmigration tv videofile-to-movievideofile-data
manage.py migrate
# removed old VideoFile model from apps
manage.py startmigration movies removed-videofile --auto
manage.py startmigration tv removed-videofile --auto
manage.py migrate

出于空间考虑,因为模型最终看起来总是一样的,所以我只打算用"电影"应用程序进行演示.

电影/模型.py

from django.db import models
from media.models import VideoFile as BaseVideoFile

# This model remains until the last migration, which deletes 
# it from the schema.  Note the name conflict with media.models
class VideoFile(models.Model):
    movie = models.ForeignKey(Movie, blank=True, null=True)
    name = models.CharField(max_length=1024, blank=True)
    size = models.IntegerField(blank=True, null=True)
    ctime = models.DateTimeField(blank=True, null=True)

class MovieVideoFile(BaseVideoFile):
    movie = models.ForeignKey(Movie, blank=True, null=True, related_name='shows')

电影/迁移/0002_统一视频文件.py(模式迁移)

from south.db import db
from django.db import models
from movies.models import *

class Migration:

    def forwards(self, orm):

        # Adding model 'MovieVideoFile'
        db.create_table('movies_movievideofile', (
            ('videofile_ptr', orm['movies.movievideofile:videofile_ptr']),
            ('movie', orm['movies.movievideofile:movie']),
        ))
        db.send_create_signal('movies', ['MovieVideoFile'])

    def backwards(self, orm):

        # Deleting model 'MovieVideoFile'
        db.delete_table('movies_movievideofile')

movies/migration/0003_videofile-to-movievideofile-data.py(数据迁移)

from south.db import db
from django.db import models
from movies.models import *

class Migration:

    def forwards(self, orm):
        for movie in orm['movies.videofile'].objects.all():
            new_movie = orm.MovieVideoFile.objects.create(movie = movie.movie,)
            new_movie.videofile_ptr = orm['media.VideoFile'].objects.create()

            # videofile_ptr must be created first before values can be assigned
            new_movie.videofile_ptr.name = movie.name
            new_movie.videofile_ptr.size = movie.size
            new_movie.videofile_ptr.ctime = movie.ctime
            new_movie.videofile_ptr.save()

    def backwards(self, orm):
        print 'No Backwards'

南方太棒了!

OK标准免责声明:您正在处理实时数据.我在这里给了您工作代码,但是请使用--db-dry-run来测试您的模式.在try 任何事情之前,始终要做好备份,并且通常要小心.

COMPATIBILITY NOTICE

我将保持我的原始消息不变,但是South已经将命令manage.py startmigration改为manage.py schemamigration.

Django相关问答推荐

如何在Django中获取标记<;输入>;的';值';属性?

Django allauth 社交帐户注册中邮箱的模板变量

当前路径 **/POST 与其中任何一个都不匹配

如何将数据库中的 None 序列化为空对象?

如何在 ModelViewSet 逻辑中读取查询字符串值?

在 Django Admin change_list 视图中更改 list_editable 字段时保存 Django 模型

如何解决 AssertionError: .accepted_renderer not set on Response in django and ajax

Python / Django 中的 Unicode 与 UTF-8 混淆?

Django error:不能分配必须是实例

如何将 Django forms.ChoiceField 呈现为 Twitter Bootstrap 下拉菜单

magic有什么问题?

在 Django 管理页面中注册应用程序中的每个表/类

如何在 django admin 中显示布尔属性

__init__() 得到了一个意外的关键字参数user

Python:获取异常的错误消息

模拟 Django 查询集以测试采用查询集的函数

import_module 的 Django 1.9 ImportError

刷新时重新提交的django表单

只使用 Django 的某些部分?

左加入 Django ORM