Python Django中的多态模型遗传
这个问题是关于Django中的模型继承的 我读过的几乎所有内容(包括Django文档本身)都强烈建议执行'abstract base class'继承,而不是'multi-table'继承。 我同意这个理由,因此我完全支持这项建议。然而,Django没有这样做 似乎支持:Python Django中的多态模型遗传,python,django,inheritance,django-models,sqlalchemy,Python,Django,Inheritance,Django Models,Sqlalchemy,这个问题是关于Django中的模型继承的 我读过的几乎所有内容(包括Django文档本身)都强烈建议执行'abstract base class'继承,而不是'multi-table'继承。 我同意这个理由,因此我完全支持这项建议。然而,Django没有这样做 似乎支持: 多态查询,或 模型链接(即,我无法从另一个模型创建指向抽象基类的ForeignKey字段) 处境 例如,我有一些实现“抽象基类”继承模式的模型: class Tool(models.Model): name = m
- 多态查询,或
- 模型链接(即,我无法从另一个模型创建指向抽象基类的ForeignKey字段)
class Tool(models.Model):
name = models.CharField(max_length=30)
group = models.ManyToManyField(ToolGroup, blank=True) # Link to 'ToolGroup' MUST be placed on abstract class
attributes = models.ManyToManyField(ToolAttributeValue, blank=True) # Link to 'ToolAttributeValue' MUST be placed on abstract class
class Meta:
abstract = True # Almost everything I read strongly recommends against making this its own table
class HandheldTool(Tool):
electrical_safe = models.BooleanField(default=False)
class PowerTool(Tool):
compliance_expiry_date = models.DateTimeField()
class ConsumableTool(Tool):
combustible = models.BooleanField(default=False)
best_before = models.DateTimeField(null=True)
我还有一些与工具相关的分组和信息类:
# Grouping related structures
#
# ToolHierarchy > ToolGroup (n times) > Tool
#
# "Tool Boxes" > "Day Shift" > "Builders" > HandheldTool[Hammer]
# > HandheldTool[Screwdriver - SAFE]
# > PowerTool[Drill]
#
# > "Demo Team" > HandheldTool[Sledgehammer 1]
# > PowerTool[Jackhammer]
# > ConsumableTool[Dynamite]
#
# > "Night Shift" > "Rock Breakers" > HandheldTool[Hammer]
# > HandheldTool[Sledgehammer 2]
# > PowerTool[Rocksaw]
class ToolHierarchy(models.Model):
name = models.CharField(blank=True, max_length=30)
class ToolGroup(models.Model):
name = models.CharField(blank=True, max_length=30)
parent = models.ForeignKey('self', related_name='children', null=True, blank=True)
hierarchy = models.ForeignKey(ToolHierarchy, null=True, blank=True, related_name='top_level_tools')
# tools = models.ManyToManyField(Tool) # CANNOT MAKE LINK, as 'Tool' is abstract
# 'Extra-info' structures
#
# ToolAttribute > ToolAttributeValue > Tool
#
# 'Brand' > 'Stanley' > HandheldTool[Hammer]
# > HandheldTool[Sledgehammer 1]
# > 'ACME' > HandheldTool[Sledgehammer 2]
# > ConsumableTool[Dynamite]
#
# 'Supplier' > 'Bash Brothers' > HandheldTool[Hammer]
# > HandheldTool[Sledgehammer 1]
# > HandheldTool[Sledgehammer 2]
class ToolAttribute(models.Model):
name = models.CharField(max_length=30)
data_type = models.CharField(max_length=30) # e.g. "STRING", "INT", "DATE" "FLOAT" -- Actually done with enum
unit_of_measure = models.CharField(max_length=30, blank=True)
class ToolAttributeValue(models.Model):
attribute = models.ForeignKey(ToolAttribute)
value = models.CharField(blank=True, max_length=30)
# tool = models.ForeignKey(Tool) # CANNOT MAKE LINK, as 'Tool' is abstract
问题
理想情况下,此继承模型将通过多态关系实现,但Django ORM没有
支持它。这在SQLAlchemy和Hibernate等其他ORM中是可能的
对于Django ORM,由于工具
类是抽象的,因此我无法创建如下链接:
或toolbattributevalue.tool->tool_obj
ToolGroup.tools->[tool_obj_1,tool_obj_2]
ToolAttributeValue
和ToolGroup
对象上出现各种各样的丑陋,这些对象不再具有.tools
属性,而是具有每个子类型的RelatedManager
字段。i、 e:
tool_group_obj.handheldtool_set.all()
tool_group_obj.powertool_set.all()
...etc, for every subtype of Tool
这几乎破坏了抽象类的实用性
问题
因此,考虑到这一点,我的问题是:
工具吗?如果是,那么我是否必须为每个子类创建*工具组模型
注:这是一个与类似的问题,但提供了更多细节。好的,所以我想我将回答我自己的问题
- @Bruno Desthuilliers指出,这些观点并非来自“官方”文档,因此,他暗示“多表”是一个非常好的功能,可供一个人使用
- 我对@dhke的链接和评论的理解是,您必须选择一个选项,“多表”选项是数据库真正支持继承的唯一方式。也就是说,即使使用Hibernate或SQLAlchemy等工具的多态技巧,您仍然可以选择是连接表(“多表”选项)进行对象查找,还是联合表(“抽象基”/“多态”选项)进行集合创建
- @dhke还指出,最好使用“multi table”选项,并告诉类库在查找“整集”时不要进行子类解析,而不是让数据库对所有表进行并集(使用“抽象基类”选项进行“整集”查找时需要这样做)
工具吗?如果是,那么我是否必须为每个子类创建*工具组模型?
不,看起来不是那样的。我介绍的工具
界面的两种用途有不同的需求:
/分层分组用例是保留继承的工具组
类的好用例。如果您必须为每种类型的工具创建一组特定于类型的类,那么这将变得非常糟糕工具
也为超类提供了一个很好的例子,除非您能够使用诸如HSTORE字段类型(由Postgres提供,我不确定是否有其他后端)。给出了一个很好的概述,这可能就是我在这里要做的(感谢@nigel222对这个问题的研究!)toolbattribute
导致我提出这个问题的案例是这样一个模型:
class PurchasableItem(models.Model):
class Meta:
abstract = True
class Cheesecake(PurchasableItem):
pass
class Coffee(PurchasableItem):
pass
我使用的解决方法是将父类转换为属性:
class PurchasableItem(models.Model):
class Meta:
abstract = False
class Cheesecake(models.Model):
purchasable_item = models.OneToOneField(PurchasableItem, on_delete=models.CASCADE)
class Coffee(models.Model):
purchasable_item = models.OneToOneField(PurchasableItem, on_delete=models.CASCADE)
这样,我可以同时获得行为和查询功能。好吧,选择abstract=True
避免了基表联接,但您也选择了根本不能拥有基模型的in-db表示(这意味着您也不能查询抽象模型)abstract=True
本质上意味着不实现这个模型,它只是