使用Django South从具体继承迁移到抽象继承
我有一个现成的Django项目,里面有几个模型是基于一个基础类的具体继承。经过仔细考虑,并且阅读了一些像Jacob Kaplan-Moss这样的人对这个问题的看法(链接),我发现在我的情况下,使用这种具体继承其实是没必要的。我想把它改成使用一个抽象的基础类。
这件事比较复杂的是,我的网站已经上线,并且有用户输入的数据。因此,在这个转换过程中,我需要确保所有的数据都能保持完整。
为了更具体一点,我给个例子:
之前:
app1/models.py:
class Model1(base_app.models.BaseModel):
field1 = models.CharField(max_length=1000)
field2 = models.CharField(max_length=1000)
app2/models.py:
class Model2(base_app.models.BaseModel):
field1 = models.CharField(max_length=1000)
field2 = models.CharField(max_length=1000)
base_app/models.py:
class BaseModel(models.Model):
user = models.ForeignKey(User)
another_field = models.CharField(max_length=1000)
之后:
app1/models.py:
class Model1(base_app.models.BaseModel):
field1 = models.CharField(max_length=1000)
field2 = models.CharField(max_length=1000)
app2/models.py:
class Model2(base_app.models.BaseModel):
field1 = models.CharField(max_length=1000)
field2 = models.CharField(max_length=1000)
base_app/models.py:
class BaseModel(models.Model):
user = models.ForeignKey(User)
another_field = models.CharField(max_length=1000)
class Meta:
abstract = True
目前,我的计划是先在BaseModel里加上 abstract = True
。然后,对于每一个使用了 BaseModel
的模型,我会逐个处理:
- 使用south来迁移数据库,并用--auto标志来创建这个迁移
- 使用south的数据迁移。比如,我会遍历Model1中的每个对象,找到在BaseModel中具有相同主键的对象,然后把BaseModel对象的每个字段的值复制到Model1对象中。
所以首先,这样做可行吗?其次,有没有更好的方法?
更新:
我的最终解决方案在这里详细描述:
http://www.markliu.me/2011/aug/23/migrating-a-django-postgres-db-from-concrete-inhe/
2 个回答
Sebastjan Trepča的回答可能不错,但还有另一种方法可以手动创建迁移:
在你的基础模型中添加
abstract = True
。运行
schemamigration --auto
,生成的迁移可能不太好,但你可以把它当作基础来使用。编辑迁移文件。在
forward
部分,你需要按以下顺序添加:a. 对于每个子模型,添加
db.delete_foreign_key(table_name, column)
。这会删除父表和子表之间的外键关系。b. 添加
db.delete_table(BaseModel)
来删除基础模型的表(这个命令可能已经在 --auto 生成时包含了)。c. 可能需要将所有子模型的主键列重命名为 'id'。我不太确定。如果需要这样做:对每个子模型使用
db.rename_column(table_name, column_name, 'id')
。删除
forward
中所有不合理的自动生成代码。运行迁移:
migrate
这个方法的作用是删除基础类的表以及基础类表和其子类之间的外键,因为它们在抽象基础类中并不使用。
我没有测试过这个方法,所以可能会遇到一些问题。这种方法比其他方法复杂,但好处是你不需要迁移数据,并且你会理解发生了什么。它的运行速度也应该很快,这对于实时迁移来说是个好事。
你可以查看 South API 获取更多信息。
有一点非常重要,无论你使用哪种方法,都要在你系统和数据库的本地副本上操作。当你确定迁移工作正常后,备份你的生产数据库,然后应用迁移,最后重启你的网络服务器(以加载你修改后的模型代码)。
添加一个叫做NewBaseModel的新模型,我们用不同的名字是为了避免和现在的非抽象模型冲突(否则South会把BaseModel删掉)。
class NewBaseModel(models.Model): user = models.ForeignKey(User) another_field = models.CharField(max_length=1000) class Meta: abstract = True
让Model1和Model2继承自NewBaseModel。
- 运行命令schemamigration --auto,这样Model1和Model2会增加两个新字段。
- 运行命令datamigration --empty,然后把BaseModel里的值填到新字段里。
- 加载生产数据库,仔细检查一下所有数据是否都迁移正确。
- 删除BaseModel,并把NewBaseModel改名为BaseModel。
- 再运行一次schemamigration --auto(这个应该能成功 ;))。
- 部署!
注意:在迁移时使用orm变量,以便使用你模型当前的状态。