使用南方重构具有继承的Django模型 [英] Using south to refactor a Django model with inheritance

查看:152
本文介绍了使用南方重构具有继承的Django模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否可以使用Django south 进行以下迁移,并保留数据。



之前:



我目前有两个应用程序,一个叫做电视,一个叫电影,每个都有一个VideoFile模型这里):



电视/ models.py:

 code> 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)
pre>

电影/ 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)



之后:



由于两个视频文件对象非常相似,我想摆脱重复,并在一个名为media的单独应用程序中创建一个新模型,该应用程序包含一个通用的VideoFile类并使用继承来扩展它: p>

media / models.py:

  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,空白= True,null = True)

电影/ models.py:

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

所以我的问题是,我如何用django-south完成这个,但仍然保持现有数据?



所有这三个应用程序已经由南方迁移管理,根据南方文档,组合模式和数据迁移是不好的做法,他们建议应该



我认为可以使用这样的单独的迁移来完成(假设media.VideoFile已经创建)


  1. 模式迁移,以重命名将要移动到新media.VideoFile模型的电视VideoFile和movies.VideoFile中的所有字段,也许是像old_name,old_size等等。 >
  2. 模式迁移到tv.VideoFile和movies.VideoFile继承media.VideoFile

  3. 数据迁移以将old_name复制到name,old_size到size等。 li>
  4. 计划迁移以删除旧字段

在我完成所有这些工作之前, nk会工作吗?有没有更好的方法?



如果您有兴趣,该项目将在此托管: http://code.google.com/p/medianav/

解决方案

请保留以下保罗对与Django / South的更新版本兼容的注意事项。






这似乎是一个有趣的问题,我正在成为南方的大粉丝,所以我决定去看一下这个。我在上面描述的内容的抽象中构建了一个测试项目,并且已经成功地使用了South来执行你所要求的迁移。在我们得到代码之前,先看几下注释:




  • 南方文档建议单独执行模式迁移和数据迁移。我在后面跟着。


  • 在后端,Django代表一个继承的表,通过在继承的模型上自动创建一个OneToOne字段


  • 了解这一点,我们的南移民需要手动正确处理OneToOne字段,但是在实验中,似乎South(或者Django本身)无法创建OneToOne提交具有相同名称的多个继承的表。因此,我将电影/电视应用程序中的每个子表重命名为自己的应用程序(即MovieVideoFile / ShowVideoFile)。


  • 使用实际的数据迁移代码,似乎South优先先创建OneToOne字段,然后为其分配数据。在创建过程中将数据分配给OneToOne字段会导致南方窒息。 (南方所有酷的公平妥协)。




所以说了一切,我试图保持发出控制台命令的日志。我会在必要时插入评论。最终的代码在底部。



命令历史



  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#添加了一些假数据...
manage.py startapp media
manage.py startmigration media - 初始
manage.py migrate
#编辑代码,编写新模型,但保留旧的完整
manage.py startmigration电影统一视频文件--auto
#创建一个新的空白)迁移到手写数据迁移
manage.py startmigration movies videofile-to-movievideofile-data
manage.py migrate
#编辑代码,写了新的模型,但保留了旧的完整
manage.py startmigration tv统一视频文件--auto
#创建一个新的(空白)迁移手写数据迁移
manage.py startmigration tv videofile- to-movievideofile-data
manage.py migrate
#从应用程序中删除旧的VideoFile模型
manage.py startmigration movies removed-videofile --auto
manage.py startmigration tv removed-视频文件--auto
manage.py migrate

为了空间的缘故,由于模型总是看起来一样,我只会用'电影'应用程序演示。



电影/ models.py


$ b来自django.db导入模型$ b $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $从模式中删除
#it。注意名称与media.models冲突
class VideoFile(models.Model):
movie = models.ForeignKey(Movie,blank = True,null = True)
name = models.CharField
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_unified-videofile.py(模式迁移)



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

class Migration:

def forward(self, orm)'

#添加模型'MovieVideoFile'
db.create_table('movies_movievideofile',(
('videofile_ptr',orm ['movies.movi​​evideofile:videofile_ptr']) ,
('movie',orm ['movies.movi



$ b def backwards(self,orm):$ b $($)
db.send_create_signal('movies',['MovieVideoFile']) b
#删除模型MovieVideoFile
db.delete_table('movies_movievideofile')



< h2>电影/迁移/ 0003_videofile-to-movievideofile-data.py(数据迁移)

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

class Migration:

def forward(self,orm):
对于电影在orm ['movies.videofile']。objects.all():
new_movie = orm.MovieVideoFile.objects.create(movie = movie.movi​​e,)
new_movie.videofile_ptr = orm [' media.VideoFile']。objects.create()

#videofile_ptr必须先创建值才能分配
new_movie.videofile_ptr.name = movie.name
new_movie.videofile_ptr .siz e = movie.size
new_movie.videofile_ptr.ctime = movie.ctime
new_movie.videofile_ptr.save()

def backwards(self,orm):
print '没有向后'



南非常棒!



Ok标准免责声明:您正在处理实时数据。我在这里给你工作代码,但请使用 - db-dry-run 来测试你的架构。



兼容性通知



我将保留原始邮件的完整性,但是South已将命令 manage.py startmigration 更改为 manage.py schemamigration


I was wondering if the following migration is possible with Django south and still retain data.

Before:

I currently have two apps, one called tv, one called movies, each with a VideoFile model (simplified here):

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)

After:

Because the two videofile objects are so similar I want to get rid of duplication and create a new model in a separate app called media that contains a generic VideoFile class and use inheritance to extend it:

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)

So my question is, how can I accomplish this with django-south and still maintain existing data?

All three these apps are already managed by south migrations and according to the south documentation it is bad practice to combine a schema and data migration and they recommend it should be done in a few steps.

I think it could be done using separate migrations like this (assuming media.VideoFile is already created)

  1. Schema migration to rename all fields in tv.VideoFile and movies.VideoFile that will move to the new media.VideoFile model, maybe to something like old_name, old_size, etc
  2. Schema migration to tv.VideoFile and movies.VideoFile to inherit from media.VideoFile
  3. Data migration to copy old_name to name, old_size to size, etc
  4. Scheme migration to remove old_ fields

Before I go through all that work, do you think that will work? Is there a better way?

If you're interested, the project is hosted here: http://code.google.com/p/medianav/

解决方案

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


This seemed like an interesting problem, and I'm becoming a big fan of South, so I decided to look into this a bit. I built a test project on the abstract of what you've described above, and have successfully used South to perform the migration you are asking about. Here's a couple of notes before we get to the code:

  • The South documentation recommends doing schema migrations and data migrations separate. I've followed suit in this.

  • On the backend, Django represents an inherited table by automatically creating a OneToOne field on the inheriting model

  • Understanding this, our South migration needs to properly handle the OneToOne field manually, however, in experimenting with this it seems that South (or perhaps Django itself) cannot create a OneToOne filed on multiple inherited tables with the same name. Because of this, I renamed each child-table in the movies/tv app to be respective to it's own app (ie. MovieVideoFile/ShowVideoFile).

  • In playing with the actual data migration code, it seems South prefers to create the OneToOne field first, and then assign data to it. Assigning data to the OneToOne field during creation cause South to choke. (A fair compromise for all the coolness that is South).

So having said all that, I tried to keep a log of the console commands being issued. I'll interject commentary where necessary. The final code is at the bottom.

Command History

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

For space sake, and since the models invariably look the same in the end, I'm only going to demonstrate with 'movies' app.

movies/models.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')

movies/migrations/0002_unified-videofile.py (schema migration)

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 (data migration)

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'

South is awesome!

Ok standard disclaimer: You're dealing with live data. I've given you working code here, but please use the --db-dry-run to test your schema. Always make a backup before trying anything, and generally be careful.

COMPATIBILITY NOTICE

I'm going to keep my original message intact, but South has since changed the command manage.py startmigration into manage.py schemamigration.

这篇关于使用南方重构具有继承的Django模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆