这个问题是关于Django中的模型继承。
我所阅读的几乎所有内容(包括Django文档本身)强烈建议执行'抽象基类'继承而不是'多表'继承。 我同意推理,因此完全支持这项建议。但是,Django没有 似乎支持:
例如,我有一些实现'抽象基类'继承模式的模型:
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和其他ORM(如Hibernate)实现。
使用Django ORM,因为Tool
类是抽象的,我不能创建如下链接:
ToolAttributeValue.tool -> tool_obj
或ToolGroup.tools -> [tool_obj_1, tool_obj_2]
。相反,我被迫在抽象类上创建反向链接,尽管它的建模稍有不同!然后,这会在ToolAttributeValue
和ToolGroup
对象上产生各种丑陋,然后不再具有.tools
属性,而是为每个子类型提供RelatedManager
个字段。即:
tool_group_obj.handheldtool_set.all()
tool_group_obj.powertool_set.all()
...etc, for every subtype of Tool
这几乎破坏了抽象类的有用性。
所以,考虑到这一点,我的问题是:
Tool
吗?如果是,那么我是否必须为每个子类创建一个*ToolGroup
模型?注意:我已阅读文档并测试了https://github.com/chrisglass/django_polymorphic,但似乎无效 用于'抽象基类'继承(即它仅用于多表)。如果我确实选择了多表继承那么 django-polymorphic对我的问题的查询方面有好处,我猜我的模型链接问题会消失。
注意:这是与this one类似的问题,但提供了更多细节。
答案 0 :(得分:3)
好的,所以我想我会回答我自己的问题......
这是“多表”继承的好例子吗?
看来是这样。虽然有一些地方建议不要使用“多表”继承(listed here for example),但有些对立点是:
@Bruno Desthuilliers指出,这些意见并非来自“官方”文档,而且,他认为“多表”是一个非常好的功能供人们使用。
我阅读@dhke的链接和评论是你有选择一个选项,而'multi-table'选项是数据库真正支持继承的唯一方式。即即使使用Hibernate或SQLAlchemy等工具的多态技巧,您仍然选择是用于对象查找的JOIN表('multi-table'选项)还是用于集合创建的UNION表('abstract base'/'polymorphic'选项)。
@dhke还指出,最好使用'multi-table'选项,告诉像django-polymorphic这样的库在查找'整集'时不要做子类解析而不是让数据库对所有表执行UNION(使用'abstract base class'选项进行'整集'查找时需要)。
我是否努力强制进行类型继承?我应该摆脱Tool
吗?如果是,那么我是否必须为每个子类创建一个*ToolGroup
模型?
不,这似乎不是那样的。我提出的Tool
接口的两种用法有不同的需求:
ToolGroup
/层次分组用例是保留继承的Tool
类的好用例。如果您必须为每种类型的工具创建特定于类型的类集,这将变得非常难看
ToolAttribute
也为超类提供了一个很好的理由,除非您能够使用HSTORE字段类型之类的东西(由Postgres提供,我不确定其他后端)。 This link给出了一个很好的纲要,这可能就是我在这里要做的(感谢@ nigel222进行了研究的问题!)。
目前(Django 1.8)接受的方式是什么?当然我不是第一个在Django中建立关系系统的人;-)而且其他ORM考虑过这个问题的事实表明它是一种常见的设计选择。
现在这是一个无关紧要的问题。基本上他们不担心。
多态解决方案(可能通过SQLAlchemy)是一个选项吗?是否考虑使用Django 1.9 +?
不是我能说出来的。
答案 1 :(得分:0)
导致我提出这个问题的案例是这样的模型:
class PurchasableItem(models.Model):
class Meta:
attract = True
class Cheesecake(PurchasableItem):
pass
class Coffee(PurchasableItem):
pass
我使用的解决方法是将父类转换为属性:
class Cheesecake(PurchasableItem):
purchasable_item = models.OneToOneField(PurchasableItem, on_delete=models.CASCADE)
class Coffee(PurchasableItem):
purchasable_item = models.OneToOneField(PurchasableItem, on_delete=models.CASCADE)
这样,我可以同时获得行为和查询功能。