我尝试按照T Stone提供的解决方案,但是我遇到了一些问题。尽管它是一个非常好的起点,并且解释了应该如何完成任务,但我仍然遇到了一些困难。
我认为现在大多数情况下你不需要再创建父类的表项了,也就是说你不需要:
new_movie.videofile_ptr = orm['media.VideoFile'].objects.create()
不再需要手动添加null=True
了。Django现在会自动为您执行此操作(如果有非空字段,则上述方法对我不起作用并导致数据库错误)。
我认为这可能是由于Django和South的更改,以下是适用于我在Ubuntu 10.10上使用Django 1.2.3和South 0.7.1的版本。模型略有不同,但您将获得要点:
初始设置
post1/models.py:
class Author(models.Model):
first = models.CharField(max_length=30)
last = models.CharField(max_length=30)
class Tag(models.Model):
name = models.CharField(max_length=30, primary_key=True)
class Post(models.Model):
created_on = models.DateTimeField()
author = models.ForeignKey(Author)
tags = models.ManyToManyField(Tag)
title = models.CharField(max_length=128, blank=True)
content = models.TextField(blank=True)
post2/models.py:
class Author(models.Model):
first = models.CharField(max_length=30)
middle = models.CharField(max_length=30)
last = models.CharField(max_length=30)
class Tag(models.Model):
name = models.CharField(max_length=30)
class Category(models.Model):
name = models.CharField(max_length=30)
class Post(models.Model):
created_on = models.DateTimeField()
author = models.ForeignKey(Author)
tags = models.ManyToManyField(Tag)
title = models.CharField(max_length=128, blank=True)
content = models.TextField(blank=True)
extra_content = models.TextField(blank=True)
category = models.ForeignKey(Category)
很明显有许多重叠之处,所以我想将共同点因素提取出来放在一个通用帖子模型中,只保留其他模型类的差异。
新设置:
genpost/models.py:
class Author(models.Model):
first = models.CharField(max_length=30)
middle = models.CharField(max_length=30, blank=True)
last = models.CharField(max_length=30)
class Tag(models.Model):
name = models.CharField(max_length=30, primary_key=True)
class Post(models.Model):
created_on = models.DateTimeField()
author = models.ForeignKey(Author)
tags = models.ManyToManyField(Tag)
title = models.CharField(max_length=128, blank=True)
content = models.TextField(blank=True)
post1/models.py:
import genpost.models as gp
class SimplePost(gp.Post):
class Meta:
proxy = True
post2/models.py:
import genpost.models as gp
class Category(models.Model):
name = models.CharField(max_length=30)
class ExtPost(gp.Post):
extra_content = models.TextField(blank=True)
category = models.ForeignKey(Category)
如果您想跟进,首先需要将这些模型导入到South:
$./manage.py schemamigration post1 --initial
$./manage.py schemamigration post2 --initial
$./manage.py migrate
数据迁移
如何进行数据迁移?首先编写新的应用程序 genpost,并使用 south 进行初始迁移:
$./manage.py schemamigration genpost --initial
(我使用$
来代表shell提示符,所以不要输入它。)
接下来在post1/models.py和post2/models.py中分别创建新的类SimplePost和ExtPost(不要删除其他类)。然后为这两个类创建模式迁移:
$./manage.py schemamigration post1 --auto
$./manage.py schemamigration post2 --auto
现在我们可以应用所有这些迁移:
$./manage.py migrate
让我们来到问题的核心,将post1和post2的数据迁移到genpost:
$./manage.py datamigration genpost post1_and_post2_to_genpost --freeze post1 --freeze post2
然后编辑genpost/migrations/0002_post1_and_post2_to_genpost.py:
class Migration(DataMigration):
def forwards(self, orm):
for auth1 in orm['post1.author'].objects.all():
new_auth = orm.Author()
new_auth.first = auth1.first
new_auth.last = auth1.last
new_auth.save()
for auth2 in orm['post2.author'].objects.all():
new_auth = orm.Author()
new_auth.first = auth2.first
new_auth.middle = auth2.middle
new_auth.last = auth2.last
new_auth.save()
for tag in orm['post1.tag'].objects.all():
new_tag = orm.Tag()
new_tag.name = tag.name
new_tag.save()
for tag in orm['post2.tag'].objects.all():
new_tag = orm.Tag()
new_tag.name = tag.name
new_tag.save()
for post1 in orm['post1.post'].objects.all():
new_genpost = orm.Post()
new_genpost.created_on = post1.created_on
new_genpost.title = post1.title
new_genpost.content = post1.content
new_genpost.author = orm['genpost.author'].objects.filter(\
first=post1.author.first,last=post1.author.last)[0]
new_genpost.save()
for tag in post1.tags.all():
new_genpost.tags.add(\
orm['genpost.tag'].objects.get(name=tag.name))
new_genpost.save()
post1.delete()
for post2 in orm['post2.post'].objects.all():
new_extpost = p2.ExtPost()
new_extpost.created_on = post2.created_on
new_extpost.title = post2.title
new_extpost.content = post2.content
new_extpost.author_id = orm['genpost.author'].objects.filter(\
first=post2.author.first,\
middle=post2.author.middle,\
last=post2.author.last)[0].id
new_extpost.extra_content = post2.extra_content
new_extpost.category_id = post2.category_id
new_extpost.save()
for tag in post2.tags.all():
new_extpost.tags.add(tag.name)
new_extpost.save()
post2.delete()
orm['post1.author'].objects.all().delete()
orm['post1.tag'].objects.all().delete()
orm['post2.author'].objects.all().delete()
orm['post2.tag'].objects.all().delete()
def backwards(self, orm):
raise RuntimeError("No backwards.")
现在应用这些迁移:
$./manage.py migrate
接下来,您可以从post1/models.py和post2/models.py中删除现在不再需要的部分,然后创建模式迁移以将表更新为新状态:
$./manage.py schemamigration post1 --auto
$./manage.py schemamigration post2 --auto
$./manage.py migrate
就是这样!希望所有的都能正常工作并且你已经重构了你的模型。
startmigration
实际上被拆分为schemamigration
和datamigration
。后者不需要使用--auto
或--initial
标志,只需指定应用程序名称和迁移名称,即可获得一个带有空白的forwards
和backwards
方法的迁移文件。2)根据 South 文档,你应该使用raise RuntimeError("Cannot reverse this migration.")
代替print 'No Backwards'
。 - Mike DeSimoneclass Meta: abstract = True
? - muudscope