我应该以任何方式避免Django中的多表(具体)继承吗?
许多有经验的开发人员建议不要使用,因为其性能较差:我应该以任何方式避免Django中的多表(具体)继承吗?,django,inheritance,models,multi-table-inheritance,concrete-inheritance,Django,Inheritance,Models,Multi Table Inheritance,Concrete Inheritance,许多有经验的开发人员建议不要使用,因为其性能较差: 由,Django的核心贡献者 几乎在所有情况下,抽象继承都是一种更好的方法 从长远来看。我见过不少网站在负载下崩溃 由具体继承引入,所以我强烈建议 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'))