Django多表继承:在子模型间移动实例
假设我在 models.py
文件里有这样的内容:
Class ModelA(models.Model):
# many fields, including
relatives = models.ManyToManyField(Person)
)
# also, A is foreign key to other models:
Class SomeOtherModel(models.Model):
mya = models.ForeignKey(A)
# now we produce two classes with multi-table inheritance
# WITHOUT any additional fileds
Class InhertA1(ModelA):
pass
Class InhertA2(ModelA):
pass
根据我的理解,这段代码会为 ModelA
、InheritA1
和 InheritA2
创建表格;每个 ModelA
的实例只会在 ModelA
表中有一行,而每个 InheritA1
的实例则会在 ModelA
表中有一行(包含所有数据),同时在 InheritA1
表中也会有一行(只包含一个主键和一个指向 ModelA
表行的 OneToOne 键)等等。查询 ModelA
对象时会得到所有对象,而查询 InheritA1
对象时只会得到 InheritA1
的对象。
现在我有一个 InheritA1
的对象 a1
,我想把它变成一个 InheritA2
的对象,但不想改变对应的 ModelA
对象。之前,a1
的父级 OneToOne 键指向的是主键为 3 的 ModelA
行(并且某个 SomeOtherModel
对象的外键也设置为 3)。最后,我希望有一个 InheritA2
对象 a2
指向同一行未改变的 ModelA
(并且将对象 a1
删除)。
看起来 Django 并没有提供这样的移动类的功能?
我可以安全地自己实现相应的 SQL 操作吗?
或者会出问题吗?也就是说,我能否直接执行以下 SQL 命令:
- 在
InheritA2
表中创建一行,将父级 OneToOne 键设置为a1
的那个键, - 删除
a1
在InheritA2
表中的行?
看起来我不能在非 SQL 的 Django 中这样做,而不自动创建一个 ModelA
的行。对于第一步,也许我可以以这种方式创建一个临时对象 x
,然后让 p
成为 x
的父级,然后将 x
的父级 OneToOne 键更改为指向 a1
的那个键,然后删除对象 p
?但对于第二步,我觉得在非 SQL 的 Django 中不可能在保留父对象的情况下删除子对象的实例。
另外,有没有好的 Django 方法可以复制实例并更改对它们的引用?
也就是说,我可以创建一个新的 InheritA2
对象 y
,并将 a1
的所有属性复制到新对象中,然后遍历数据库,找到所有指向 a1
的父对象的 ManyToMany 和 ForeignKey 记录,并将它们更改为指向 y
的父对象,然后删除 a1
。
这可以在非 SQL 的 Django 中完成,缺点是这似乎在性能上有些浪费(这对我的项目来说并不是大问题),而且如果我更改了 ModelA
或与之相关的其他模型,复制所有内容的代码可能会出错?(或者有没有好的方法可以做到类似于
Forall models M in my project:
Forall keys k used in M:
If k is a descendant of a ManyToMany or Foreign or ... key:
If k points to ModelA:
Forall instances x of M:
If x.k=a1:
x.k=y
前四行似乎有点可疑。
备注:
- 在不改变实例的情况下复制可以通过稳定、简单、标准的方式完成,见例如 这里,但我们仍然停留在同一个子类中(仍然需要修改 ForeignKeys 等)?
- 通过标准的 Python 方式声明来更改类,见 这里,对我来说不是一个选项,因为没有人知道这是否会严重破坏 Django。
相关问题:
- 暂无相关问题
1 个回答
如果你打算改变子类的内容,但不想动父类的话,或许可以考虑用 OneToOneField
,而不是直接继承。
class ModelA(models.Model):
pass
class InhertA1(models.Model):
a = models.OneToOneField(ModelA, primary_key=True)
class InhertA2(models.Model):
a = models.OneToOneField(ModelA, primary_key=True)
这样做会在数据库里生成相同的三张表。(唯一的不同是,InheritA1
和 InheritA2
的主键字段会使用父类的同一个ID。)
如果你想把 InheritA1
换成 InheritA2
,你需要先删除一个子类实例(这不会影响到父类实例),然后再创建一个新的实例,并把它指向父类实例。
其实,你甚至可以有一个父类实例,同时拥有来自两个不同模型的子类,但这需要在你的视图中进行检查,以防止出现问题。
如果这些信息对你有帮助,哪怕回答来得有点晚,也请告诉我。