我应该以任何方式避免Django中的多表(具体)继承吗?

我应该以任何方式避免Django中的多表(具体)继承吗?,django,inheritance,models,multi-table-inheritance,concrete-inheritance,Django,Inheritance,Models,Multi Table Inheritance,Concrete Inheritance,许多有经验的开发人员建议不要使用,因为其性能较差: 由,Django的核心贡献者 几乎在所有情况下,抽象继承都是一种更好的方法 从长远来看。我见过不少网站在负载下崩溃 由具体继承引入,所以我强烈建议 Django用户使用大量的 怀疑的剂量 由() 多表继承,有时称为“具体继承”,是 作者和许多其他开发者认为这是一件坏事。 我们强烈建议不要使用它 无论如何,每个人都应该避免多表继承 因为它增加了混乱和大量的开销。 使用显式OneToOneFields和 模型之间的外键,以便可以控制何时连接 横穿

许多有经验的开发人员建议不要使用,因为其性能较差:

  • 由,Django的核心贡献者

    几乎在所有情况下,抽象继承都是一种更好的方法 从长远来看。我见过不少网站在负载下崩溃 由具体继承引入,所以我强烈建议 Django用户使用大量的 怀疑的剂量

  • 由()

    多表继承,有时称为“具体继承”,是 作者和许多其他开发者认为这是一件坏事。 我们强烈建议不要使用它

    无论如何,每个人都应该避免多表继承 因为它增加了混乱和大量的开销。 使用显式OneToOneFields和 模型之间的外键,以便可以控制何时连接 横穿

  • 但是如果没有多表继承,我就不能轻松地

  • (必须使用GenericForeignKey或反向依赖项)

  • (请随意添加更多内容)

  • 那么,Django的这种继承有什么问题呢?为什么显式OneToOneFields更好

    连接对性能有多大影响?是否有任何基准显示性能的差异

    select_related()
    是否允许我们控制何时调用联接



    我已经将具体的示例移到了a,因为这个示例变得太广泛了,并且添加了一个使用多表继承的原因列表。

    据我所知,您在
    RelatedModel
    上使用了
    OneToOneField
    ,而在
    BaseModel
    上使用了
    ,您希望在
    RelatedModel
    和每个
    Submodel1
    Submodel9
    之间建立一对一的链接。如果是这样的话,有一种更有效的方法可以做到这一点,而无需多表继承或泛型关系

    只需去掉
    基本模型
    ,在每个
    子模型x
    中,都有一个
    OneToOneField
    相关模型

    class Submodel1(models.Model):
        related_model = models.OneToOneField(RelatedModel, null=True, blank=True, related_name='the_thing')
        some_field = models.TextField()
    
    # ...
    
    class Submodel9(models.Model):
        related_model = models.OneToOneField(RelatedModel, null=True, blank=True, related_name='the_thing')
        another_field = models.TextField()
    
    这将允许您使用名为
    the_thing
    的字段从
    RelatedModel
    的实例访问
    SubmodelX
    ,就像您第一次给出的多表继承示例一样

    请注意,您可以使用抽象继承来分解
    相关的_模型
    字段以及
    子模型1
    子模型9
    之间的任何其他公共字段


    使用多表继承效率低下的原因是,它为基础模型生成了一个额外的表,因此需要额外的连接来访问这些字段。如果以后发现需要从
    RelatedModel
    到每个
    SubmodelX
    ForeignKey
    字段,则使用泛型关系将更有效。但是,Django不支持
    select_related()
    中的泛型关系,您可能需要构建自己的查询才能有效地实现这一点。性能和编码易用性之间的权衡取决于您对服务器的期望负载和优化所需的时间。

    首先,继承并不是关系数据库体系结构的自然转换(好的,我知道,Oracle类型的对象和其他一些RDBMS支持继承,但django没有利用这个功能)

    此时,请注意,than django会为子类生成新表,并编写大量的
    左连接
    ,以从该“子表”检索数据。而且,在高性能场景中,如游戏后端或其他情况下,您应该避免它,并使用nulls、OneToOne或fo等工件“手动”解决继承问题统治键。在
    OneToOne
    场景中,您可以直接调用相关表,也可以仅在需要时调用相关表

    …但是…

    “在我看来(TGW)”您应该在企业项目中包含模型继承,当它符合您的时。我这样做,并且由于此功能,我为客户节省了大量的开发时间。此外,代码变得干净优雅,这意味着易于维护(请注意,此类项目每秒不会有数百个或多个请求)

    逐条提问

    问:Django的这种继承有什么问题?
    A:很多表,很多左连接


    问:为什么显式OneToOneFields更好?
    答:您可以直接访问相关模型,无需左连接

    问:是否有任何示例(基准)?
    答:没有可比性

    问:选择_related()是否允许我们控制何时调用联接?
    答:django连接所需的表

    问:当我需要引用另一个模型中的基类时,有哪些方法可以替代多表继承?
    无效。一对一关系和许多代码行。这取决于应用程序的需要

    问:在这种情况下,GenericForeignKey更好吗?
    A:我没有

    问:如果我需要一个OneToOneField来创建基础模型怎么办?
    答:写出来。这没有问题。例如,你可以扩展用户模型,也可以为一些用户提供一个OneToOne到用户的基本模型

    结论

    您应该知道在没有模型继承的情况下编写和维护代码的成本,以及支持模型继承应用程序并采取相应行动的硬件成本

    只是一个笑话:你可以在汇编代码上编写它,它会运行得更快。

    世界已经改变了。 首先要注意的是,在提出这个问题时,这篇题为“Django”的文章已经有将近四年的历史了;在2014年,Django和RDBMs系统从那时起都取得了长足的进步(例如mysql 5.0或5.1是广泛使用的版本,5.5的通用性仍然很差)
    from django.db import models
    
    class Parent(models.Model):
        parent_field = models.CharField(max_length=10)
    
    
    class ChildOne(Parent):
        child_one_field = models.CharField(max_length=10)
    
    
    class ChildTwo(Parent):
        child_two_field = models.CharField(max_length=10)
    
    import re
    from django.test import TestCase
    from inheritance.models import (Parent, ChildOne, ChildTwo)
    
    def count_joins(query, inner_outer):
        """ Count the occurrences of JOIN in the query """
        return len(re.findall('{} join'.format(inner_outer), str(query).lower()))
    
    
    class TestMultiTableInheritance(TestCase):
        def test_queries(self):
            # get children (with parent info)
            query = ChildOne.objects.all().query
            self.assertEqual(1, count_joins(query, 'inner'))
            self.assertEqual(0, count_joins(query, 'outer'))
            # get parents
            query = Parent.objects.all().query
            self.assertEqual(0, count_joins(query, 'inner'))
            self.assertEqual(0, count_joins(query, 'outer'))
            # filter children by parent field
            query = ChildOne.objects.filter(parent_field=parent_value).query
            self.assertEqual(1, count_joins(query, 'inner'))
            self.assertEqual(0, count_joins(query, 'outer'))
            # filter parents by child field
            query = Parent.objects.filter(childone__child_one_field=child_value).query
            self.assertEqual(1, count_joins(query, 'inner'))
            self.assertEqual(0, count_joins(query, 'outer'))
            # get child field values via parent
            query = Parent.objects.values_list('childone__child_one_field').query
            self.assertEqual(0, count_joins(query, 'inner'))
            self.assertEqual(1, count_joins(query, 'outer'))
            # get multiple child field values via parent
            query = Parent.objects.values_list('childone__child_one_field',
                                               'childtwo__child_two_field').query
            self.assertEqual(0, count_joins(query, 'inner'))
            self.assertEqual(2, count_joins(query, 'outer'))
            # get child-two field value from child-one, through parent
            query = ChildOne.objects.values_list('parent_ptr__childtwo__child_two_field').query
            self.assertEqual(1, count_joins(query, 'inner'))
            self.assertEqual(1, count_joins(query, 'outer'))
            # get parent field value from parent, but through child
            query = Parent.objects.values_list('childone__parent_field').query
            self.assertEqual(0, count_joins(query, 'inner'))
            self.assertEqual(2, count_joins(query, 'outer'))
            # filter parents by parent field, but through child
            query = Parent.objects.filter(childone__parent_field=parent_value).query
            self.assertEqual(2, count_joins(query, 'inner'))
            self.assertEqual(0, count_joins(query, 'outer'))