class M1(models.Model):
name = models.CharField(max_length=10)
class M2(models.Model):
name = models.CharField(max_length=10)
select_relation = models.ForeignKey(M1, on_delete=models.CASCADE)
prefetch_relation = models.ManyToManyField(to='M3')
class M3(models.Model):
name = models.CharField(max_length=10)
class a(models.Model):
name = models.CharField(max_length=100)
class b(models.Model):
name = models.CharField(max_length=100)
a = models.ForeignKey(A, on_delete=models.CASCADE)
select_相关查询-〉
b.objects.select_related('a').first()
为此执行的SQL查询将为
SELECT * FROM "b" LEFT OUTER JOIN "a" ON ("b"."a_id" = "a"."id") LIMIT 1
在这里Django将使用JOIN获得“a”模型细节
预取相关查询-〉
B.objects.prefetch_related('a').first()
为此执行的SQL查询为
SELECT * FROM "b" LIMIT 1
SELECT * FROM "a" WHERE "a"."id" IN (ids collected from above query)
class ExampleClassA(models.Model):
title = models.CharField(max_length=50)
class ExampleClassB(models.Model):
example_class_a = models.ForeignKey(ExampleClassA,
on_delete=models.CASCADE)
objects = ExampleClassB.objects.all()
for obj in objects:
print(obj.example_class_a.title)
5条答案
按热度按时间f0brbegy1#
你的理解基本上是正确的。当你要选择的对象是一个单独的对象时,你使用
select_related
,比如OneToOneField
或ForeignKey
。当你要得到一个“集合”时,你使用prefetch_related
。因此ManyToManyField
s如您所述,或者反转ForeignKey
s。为了阐明“反转ForeignKey
s”的含义,下面是一个示例:不同之处在于
select_related
执行SQL连接,因此从SQL服务器获取结果作为表的一部分。另一方面,prefetch_related
执行另一个查询,因此减少了原始对象中的冗余列(上例中的ModelA
)。您可以将prefetch_related
用于任何可以使用select_related
的对象。代价是
prefetch_related
必须创建一个ID列表并将其发送回服务器,这可能需要一段时间。我不确定在事务中是否有好的方法来完成这一点,但我的理解是Django总是发送一个列表并说SELECT ... WHERE pk IN(...,...,...)基本上。在这种情况下,如果预取的数据是稀疏的(假设美国州对象链接到人们的地址)这可能是非常好的,然而如果它更接近一对一,这可能浪费大量通信。两种都试试看哪种性能更好。上面讨论的内容基本上都是关于与数据库的通信。然而,在Python方面,
prefetch_related
有一个额外的好处,即使用单个对象来表示数据库中的每个对象。使用select_related
,将在Python中为每个“父”对象创建重复的对象。由于Python中的对象有相当多的内存开销,这也是一个考虑因素。bq3bfh9z2#
这两种方法都达到了相同的目的,即放弃不必要的数据库查询。但它们使用不同的方法来提高效率。
使用这两种方法的唯一原因是当单个大查询比许多小查询更可取时,Django使用大查询在内存中抢先创建模型,而不是对数据库执行按需查询。
select_related
对每个查找执行一个连接,但是扩展select以包括所有连接表的列。连接有可能成倍增加查询中的行数。当你在外键或一对一字段上执行连接时,行数不会增加。然而,多对多连接没有这个保证。所以,Django将
select_related
限制为不会意外导致大规模连接的关系。prefetch_related
的 “join in python” 比它应该的要更令人担忧。它为每个要连接的表创建一个单独的查询。它使用WHERE IN子句过滤每个表,如下所示:每个表被拆分为一个单独的查询,而不是对可能过多的行执行单个联接。
kq0g1dla3#
浏览了一下已经发布的答案。只是觉得如果我加上一个有实际例子的答案会更好。
假设你有3个相关的Django模型。
在这里可以使用
select_relation
字段查询M2
模型及其相关M1
对象,使用prefetch_relation
字段查询M3
对象。然而,正如我们提到的
M1
与M2
的关系是ForeignKey
,对于任何M2
对象,它只返回1条记录,同样的事情也适用于OneToOneField
。但是
M3
与M2
的关系是ManyToManyField
,它可以返回任意数量的M1
对象。假设有两个
M2
对象m21
,m22
,它们有相同的5关联的M3
对象,这些对象的ID为1,2,3,4,5
,当你为每个M2
对象获取关联的M3
对象时,如果你使用select related,这就是它的工作方式。1.找到
m21
物体。1.查询与
m21
对象关联的所有标识为1,2,3,4,5
的M3
对象。1.对
m22
对象和所有其他M2
对象重复相同的操作。由于
m21
和m22
对象具有相同的1,2,3,4,5
ID,如果使用select_related选项,则将针对已提取的相同ID查询DB两次。相反,如果使用prefetch_related,当您尝试获取
M2
对象时,它将记录对象返回的所有ID(注意:只有ID),作为最后一步,Django将使用M2
对象返回的所有ID的集合查询M3
表,并使用Python而不是数据库将它们连接到M2
对象。这样你只需要查询所有的
M3
对象一次,这样可以提高性能,因为python连接比数据库连接便宜。7ivaypg94#
让我来演示一下Django是如何在select_related和prefetch_related中调用数据库的
select_相关查询-〉
为此执行的SQL查询将为
在这里Django将使用JOIN获得“a”模型细节
预取相关查询-〉
为此执行的SQL查询为
这里Django将执行两个SQL查询并通过python合并它们
mlmc2os55#
你不要误会
他们做同样的事情来减少查询的数量
例如:
查询次数(访问相关字段):N +1(#n是示例类A的对象编号)
如果我们使用这个查询:
查询次数只能为一次。