python - Django中的多态模型继承

标签 python django inheritance django-models sqlalchemy

这个问题是关于 Django 中的模型继承。

我读过的几乎所有内容(包括 Django 文档本身)都强烈建议执行 '抽象基类' 继承而不是 '多表' 遗产。
我同意推理,因此完全支持该建议。然而,Django 没有
似乎支持:

  • 多态查询,或
  • 模型链接(即我无法从另一个模型创建一个指向抽象基类的 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 和其他 ORM(如 Hibernate)是可能的。

    使用 Django ORM,因为 Tool类是抽象的 I 不能创建如下链接:
  • ToolAttributeValue.tool -> tool_obj
  • ToolGroup.tools -> [tool_obj_1, tool_obj_2] .

  • 相反,我被迫在抽象类上创建反向链接,尽管它建模略有不同!这会导致 ToolAttributeValue 上出现各种丑陋的情况。和 ToolGroup对象,然后不再具有 .tools属性,而是有 RelatedManager每个子类型的字段。 IE。:
    tool_group_obj.handheldtool_set.all()
    tool_group_obj.powertool_set.all()
    ...etc, for every subtype of Tool
    

    这几乎破坏了抽象类的有用性。

    问题

    因此,考虑到这一点,我的问题是:
  • 这是“多表”继承的好例子吗?
  • 我是否过于努力地强制类型继承?我应该摆脱Tool ?如果是,那么我是否必须创建一个 *ToolGroup每个子类的模型?
  • 当前(Django 1.8)接受的解决方法是什么?当然,我不是第一个在 Django 中构建关系系统的人 ;-) 其他 ORM 已经考虑过这个问题的事实表明它是一种常见的设计选择。
  • 多态解决方案(可能通过 SQLAlchemy)是一种选择吗?这是否正在考虑用于 Django 1.9+?

  • 注意:我已阅读文档并测试了 https://github.com/chrisglass/django_polymorphic ,但它似乎不起作用
    用于“抽象基类”继承(即仅适用于多表)。如果我确实选择了多表继承,那么
    django-polymorphic 对我的问题的查询方面有好处,我想我的模型链接问题会消失。

    注意:这是一个与 this one 类似的问题。 ,但提供了更多细节。

    最佳答案

    好吧,所以我想我会回答我自己的问题......

  • 这是“多表”继承的好例子吗?

    看起来是这样。尽管有一些地方建议不要使用“多表”继承 (listed here for example),但一些反对意见是:
  • @Bruno Desthuilliers 指出这些意见并非来自“官方”文档,并且通过扩展,他暗示“多表”是一个非常好的功能可供使用。
  • 我对@dhke 的链接和评论的阅读是,您必须选择一个选项,并且“多表”选项是数据库真正支持继承的唯一方式。 IE。即使使用 Hibernate 或 SQLAlchemy 等工具的多态技巧,您仍然需要选择是 JOIN 表('multi-table' 选项)进行对象查找还是 UNION 表('abstract base'/'polymorphic' 选项)来创建集合。
  • @dhke 还指出,最好使用“多表”选项并告诉像 django-polymorphic 这样的库。在查找“整体”时不要进行子类解析,而不是让数据库对所有表执行 UNION(使用“抽象基类”选项进行“整体”查找时需要)。
  • 我是否过于努力地强制类型继承?我应该摆脱Tool ?如果是,那么我是否必须创建一个 *ToolGroup每个子类的模型?

    不,似乎不是这样。 Tool 的两种用途我提出的界面有不同的需求:
  • ToolGroup/分层分组用例是保留继承的Tool的好用例类(class)。如果您必须为每种类型的工具创建一组特定于类型的类,这将变得非常难看
  • ToolAttribute也为父类(super class)提供了一个很好的案例,除非您能够使用诸如 HSTORE 字段类型之类的东西(由 Postgres 提供,我不确定其他后端)。 This link给出了一个很好的概要,这可能是我在这里要做的(感谢@nigel222 对问题的研究!)。
  • 当前(Django 1.8)接受的解决方法是什么?当然,我不是第一个在 Django 中构建关系系统的人;-) 其他 ORM 已经考虑过这个问题的事实表明它是一种常见的设计选择。

    现在这是一个无关紧要的问题。基本上他们不担心。
  • 多态解决方案(可能通过 SQLAlchemy)是一种选择吗?这是否正在考虑用于 Django 1.9+?

    不是我能说的。
  • 关于python - Django中的多态模型继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32606262/

    相关文章:

    django - 渲染时捕获DoesNotExist : Photo matching query does not exist

    c++ - 使用 "new"分配内存给Derived类时,如何调用Base构造函数?

    php - 使子类继承基类的现有实例

    python - 使用小部件更新 Gmaps + Jupyter 中的标记

    python - 为什么 math.pow 会导致 ValueError : math domain error?

    python - 在python 3中将转义的utf-8字符串转换为utf

    python - 使用 django 表单上传文件

    python - Django 模板中 url 的 NoReverseMatch

    Swift 类型和良好的旧时尚分层单一继承

    python - 从列表中删除值,只留下唯一值