使用 South 重构带有继承的 Django 模型
我想知道 Django south 是否可以进行以下迁移并仍然保留数据。
之前:
我目前有两个应用程序,一个称为电视,一个称为电影,每个应用程序都有一个 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)
:
因为这两个视频文件对象非常相似,所以我想消除重复并在一个名为 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)
- 架构迁移以重命名 tv.VideoFile 和 movie.VideoFile 中的所有字段,这些字段将移动到新的 media.VideoFile 模型,也许会移动到 old_name 之类的东西、old_size 等
- 架构迁移到 tv.VideoFile 和 movie.VideoFile 以从 media.VideoFile 继承
- 数据迁移将 old_name 复制到 name,old_size 复制到 size 等
- 架构迁移以删除 old_ 字段
在我完成所有这些工作之前,您认为那会起作用吗?有更好的办法吗?
如果您感兴趣,该项目托管在此处:http://code.google.com/p /中值/
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)
- 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
- Schema migration to tv.VideoFile and movies.VideoFile to inherit from media.VideoFile
- Data migration to copy old_name to name, old_size to size, etc
- 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/
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
查看下面 Paul 的回复,了解有关与较新版本 Django/South 的兼容性的一些说明。
这似乎是一个有趣的问题,而且我正在成为 South 的忠实粉丝,因此我决定研究一下这个有点。我根据您上面描述的摘要构建了一个测试项目,并成功使用 South 来执行您所询问的迁移。在我们开始编写代码之前,请注意以下几点:
South 文档建议分开进行架构迁移和数据迁移。我也这样做了。
在后端,Django 通过在继承模型上自动创建 OneToOne 字段来表示继承表
理解这一点,我们的 South 迁移需要手动正确处理 OneToOne 字段,但是,在对此进行实验时,South(或者可能是 Django 本身)似乎无法在具有相同名称的多个继承表上创建 OneToOne 字段。因此,我将电影/电视应用程序中的每个子表重命名为各自的应用程序(即 MovieVideoFile/ShowVideoFile)。
在实际的数据迁移代码中,South 似乎更喜欢先创建 OneToOne 字段,然后向其分配数据。在创建过程中将数据分配给 OneToOne 字段会导致 South 感到窒息。 (对 South 的所有酷性的公平妥协)。
说了这么多,我尝试保留发出的控制台命令的日志。必要时我会插入评论。最终代码在底部。
命令历史
为了节省篇幅,并且由于模型最终看起来总是相同的,我将仅使用“电影”应用程序进行演示。
movie/models.py
movie/migrations/0002_unified-videofile.py(架构迁移)
movies/migration/0003_videofile-to-movievideofile-data.py(数据迁移)
南太棒了!
好的标准免责声明:您正在处理实时数据。我在这里为您提供了工作代码,但请使用
--db-dry-run
来测试您的架构。在尝试任何操作之前务必先进行备份,并且通常要小心。兼容性通知
我将保持原始消息不变,但 South 已将命令
manage.py startmigration
更改为manage.py schemamigration
。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
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
movies/migrations/0002_unified-videofile.py (schema migration)
movies/migration/0003_videofile-to-movievideofile-data.py (data migration)
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
intomanage.py schemamigration
.我确实尝试过介绍 T Stone 概述的解决方案,虽然我认为这是一个极好的入门,并解释了应该如何完成工作,但我遇到了一些问题。
我认为大多数情况下您不需要需要再为父类创建表条目,即您不再需要了
。 Django 现在将自动为您执行此操作(如果您有非空字段,则上述内容对我不起作用并给我一个数据库错误)。
我认为这可能是由于 django 和 South 的变化造成的,这是一个在 ubuntu 10.10 上使用 django 1.2.3 和 South 0.7.1 对我有用的版本。这些模型有点不同,但您会明白要点:
初始设置
post1/models.py:
post2/models.py:
显然有很多重叠,所以我想考虑一下共同点
放入通用帖子模型中,只保留其他模型中的差异
模型类。
新设置:
genpost/models.py:
post1/models.py:
post2/models.py:
如果您想继续,您将首先需要将这些模型南移:
迁移数据
如何进行?首先编写新的应用程序 genpost 并进行初始操作
向南迁移:(
我使用
$
来表示 shell 提示符,所以不要输入它。)接下来创建新类 SimplePost 和 ExtPost 在 post1/models.py 中
和 post2/models.py 分别(不要删除其余的类)。
然后也为这两个创建 schemamigrations:
现在我们可以应用所有这些迁移:
让我们进入问题的核心,将数据从 post1 和 post2 迁移到 genpost:
然后编辑 genpost/migrations/0002_post1_and_post2_to_genpost.py:
现在应用这些迁移:
接下来,您可以从 post1/models.py 和 post2/models.py 中删除现在多余的部分,然后创建 schemamigrations 将表更新到新状态:
应该就是这样!希望一切顺利,并且您已经重构了您的模型。
I did try to walk through the solution outlined by T Stone and while I think it's a superb starter and explains how things should be done I ran into a few problems.
I think mostly you don't need to create the table entry for the parent class anymore, i.e. you don't need
anymore. Django will now do this automatically for you (if you have non-null fields then the above did not work for me and gave me a database error).
I think it is probably due to changes in django and south, here is a version that worked for me on ubuntu 10.10 with django 1.2.3 and south 0.7.1. The models are a little different, but you will get the gist:
Initial setup
post1/models.py:
post2/models.py:
There is obviously a lot of overlap, so I wanted to factor the commonalities
out into a general post model and only keep the differences in the other
model classes.
new setup:
genpost/models.py:
post1/models.py:
post2/models.py:
If you want to follow along you will first need to get these models into south:
Migrating the data
How to go about it? First write the new app genpost and do the initial
migrations with south:
(I am using
$
to represent the shells prompt, so don't type that.)Next create the new classes SimplePost and ExtPost in post1/models.py
and post2/models.py respectively (don't delete the rest of the classes yet).
Then create schemamigrations for these two as well:
Now we can apply all these migrations:
Let's get to the heart of the matter, migrating the data from post1 and post2 to genpost:
Then edit genpost/migrations/0002_post1_and_post2_to_genpost.py:
Now apply these migrations:
Next you can delete the now redundant parts from post1/models.py and post2/models.py and then create schemamigrations to update the tables to the new state:
And that should be it! Hopefully it all works and you have refactored your models.
抽象模型
可能是通用关系对您也很有用。
Abstract Model
May be generic relation will be useful for you too.
我进行了类似的迁移,并且选择分多个步骤进行。除了创建多个迁移之外,我还创建了向后迁移,以便在出现问题时提供后备。然后,我抓取了一些测试数据并将其向前和向后迁移,直到我确信向前迁移时它能够正确输出。最后我迁移了生产站点。
I did a similar migration and I chose to do it in multiple steps. In addition to creating the multiple migrations, I also created the backward migration to provide a fallback if things went wrong. Then, I grabbed some test data and migrated it forward and backwards until I was sure it was coming out correctly when I migrated forwards. Finally, I migrated the production site.