复杂的模型继承 - Django

14

我认为这有点棘手,至少对我来说是这样。

所以我有四个模型Person, Singer, BassistNinja.

Singer, Bassist 和 Ninja 继承自 Person.


问题在于每个 Person 可以是其子类中的任何一个。

例如,一个人可以是 Singer 和 Ninja。另一个人可以是 Bassist 和 Ninja。另一个人可以是全部三个。

我该如何组织我的模型?


非常感谢您的帮助!


4
你正在从事哪种项目?听起来很棒!我想成为一个贝斯手-忍者混沌/邪恶。 - Alex Mcp
3
曾经我有一个朋友玩了一位吟游诗人。他死了,换了个心脏,变成了邪恶的人(别问为什么)。复活后,他扔掉了琴,拿起了一把电吉他(同样别问),并用刀弹奏着金属风格的音乐来激发勇气。显然,这很棒。 - Stefano Borini
1
+1 给忍者低音手... - cethegeek
4个回答

15

多重继承在数据库中效果不佳(而你的Django模型最终需要映射到数据库中),而且继承往往是建模“角色”的一种糟糕方式(因为人们的角色会发生变化)。我会将Singer、Bassist和Ninja作为“角色”,而不是Person的子类,并通过外键连接它们:

class Singer(models.Model):
    person = models.ForeignKey('Person')
    # ...

class Person(models.Model):
    # ...

@Alex:除了通过多重继承将角色合并在一起之外,是否还有其他方法可以将它们合并在一起?我对使用FK将角色与Person类配对感到不安,因为Person的信息完全取决于他们是谁。也许我应该再好好考虑一下,非常感谢你,Alex! - RadiantHex
这是正确的方法,除非“singer”仅仅是一个描述符……在这种情况下,只需向“Person”模型添加“is_singer = models.BooleanField()”,就可以创建一个简化的模型和更快的查询。 - Daniel Naab
1
@Radiant,使用thisperson.singer_set.all()等方法有什么问题吗? - Alex Martelli
2
@Radiant,看起来歌手和忍者可能有一个“模型”和一个“身份”的共同点——这对于将继承强加到关系基础上来说太少了,更不用说由多个继承所带来的巨大复杂性了!只需为两个模型分别命名并添加personID属性,忘记person模型,如果需要高级业务逻辑,则使用应用程序级别的Person类(不是模型!)处理1个或多个实际模型实例的数据库获取和存储,此外还有行为部分(建议使用策略设计模式)。 - Alex Martelli
@Alex:非常感谢你,Alex。你的回答总是非常有启发性和有帮助的!你让我的一天变得更美好了。 - RadiantHex
显示剩余3条评论

3

原则上,您可以像以下方式一样进行操作:

class Role(models.Model):       
     ......

class Ninja(Role):
     .......

class Person(models.Model):
      roles = models.ManyToManyField(Role)

但是你会遇到一个问题,即Person.roles.objects.all()只能给你Role类的实例。因此,你需要一种方法将每个Role实例转换为适当的子类,如Ninja或Pirate。这里有一个讨论这个问题的线程链接。

http://groups.google.com/group/django-users/browse_thread/thread/f4241bc16455f92d/7268c3f7bca6b046

所以简而言之,Alex和Stefano给出的答案比我更有用。

2

我同意Alex所描绘的角色解决方案。你拥有的不是人的不同子类,而是一个人可以拥有的不同角色。

但是我听到你说:“嘿,忍者可以有属性“numberOfStars”,而歌手可以有属性“highestNote”。接口也是同样的道理:忍者可以有方法throwStar()和disappear(),歌手可以有sing()和getWasted(),低音吉他手可以有goFunky()和slapPop()。”

在这里,你需要一个非常宽松的数据模型。如此宽松以至于你根本没有模式。如果歌手决定拿起贝斯演奏即兴曲,那没问题。如果他想扮演忍者,你调用throwStar()将会返回一个错误,因为他没有星星,但原则上,你可以给歌手分配星星并让他扔星星。

你正在涉足本体论的世界,而不是模式的世界。你有一个资源,这个资源是“某物”,这个“某物”可以是某种类型,具有某些属性等。一些属性的存在可以推断出类型,或者某些类型的存在可以推断出其他类型。你不能轻易地使用简单的django数据模型来描述这些信息。你需要一个上下文感知的、推理的图形存储,例如AllegroGraph,或使用rdflib实现你的自定义解决方案。


2
你在这里混淆了多个问题。 歌手和忍者可能需要不同的属性,但这绝不意味着您突然拥有一个完全开放式的动态模式。 表示特定类型人员的知识的明显简单方法是具有一个歌手表格来保存关于歌手的事实,一个相应的忍者表格等等,除了人员表格之外。 所有表都将具有person_id的主键,并适当使用外键。 - Marcelo Cantos

1
你可以在模型中让所有职业(忍者,贝斯手...)都继承自Person,然后在后端代码中使用函数isinstance来区分一个Person的职业。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接