我如何确定在 Django 中向 `prefetch_related` 提供哪些“关键路径”参数?

问题描述

注意:下面,我将提供给 prefetch_related 的参数称为“关键路径”。我不知道这是否是最好/正确的术语 - 如果有更好的术语可以使用,请告诉我,我会更新问题。

我在 django 中创建了一个高级搜索页面,该页面从 6 个不同的表(并非所有表都是单一的直接外键路径)中搜索多个字段中的任何一个,并在结果表中显示所有这些表中的选定字段。包括的“关键路径”是:

  • msrun__sample
  • msrun__sample__tissue
  • msrun__sample__animal
  • msrun__sample__animal__tracer_compound
  • msrun__sample__animal__studies

(注意:搜索显示中不包含 msrun 字段。此特定视图中的特定模型类仅用作视图中涉及的模型类之间的连接。)

当我包含像 .prefetch_related("msrun__sample__animal__studies") 这样的预取时,运行时间会有很大的不同,但是当我包含任何额外的预取“关键路径”时,我看不到明显的区别。

我的问题是:如何确定 prefetch_related 的参数中包含哪些“关键路径”或“关键路径”?我似乎不明白该决定的标准。 IE。比如说,为什么我会或不会在 prefetch_related 参数中包含所有相关的“关键路径”?

解决方法

使用以下模型进行了尝试:

class A(models.Model):
    name = models.CharField(max_length=100)
    my_b_set = models.ManyToManyField('B',related_name='my_a')


class B(models.Model):
    name = models.CharField(max_length=100)
    my_c_set = models.ManyToManyField('C',related_name='my_b')
    my_d_set = models.ManyToManyField('D',related_name='my_c')


class C(models.Model):
    name = models.CharField(max_length=100)


class D(models.Model):
    name = models.CharField(max_length=100)

并像这样填充关系:

a = A.objects.create(name='a1')
b1 = B.objects.create(name='b1')
b2 = B.objects.create(name='b2')
c1 = C.objects.create(name='c1')
c2 = C.objects.create(name='c2')
d1 = D.objects.create(name='d1')
d2 = D.objects.create(name='d2')

a.my_b_set.add(b1)
a.my_b_set.add(b2)

b1.my_c_set.add(c1,c2)
b1.my_d_set.add(d1,d2)

b2.my_c_set.add(c1,c2)
b2.my_d_set.add(d1,d2)

然后运行这个查询:

A.objects.prefetch_related('my_b_set','my_b_set__my_c_set','my_b_set__my_d_set')

正如预期的那样,它进行了 4 次查询:

-- Get all A's

SELECT "changelog_a"."id","changelog_a"."name" FROM "changelog_a"


-- Get all the related B's of A mapped with A's id

SELECT ("changelog_a_my_b_set"."a_id") AS "_prefetch_related_val_a_id","changelog_b"."id","changelog_b"."name" 
FROM "changelog_b" INNER JOIN "changelog_a_my_b_set" ON ("changelog_b"."id" = "changelog_a_my_b_set"."b_id") 
WHERE "changelog_a_my_b_set"."a_id" IN (2)


-- Get all the related C's of B mapped with B's id

SELECT ("changelog_b_my_c_set"."b_id") AS "_prefetch_related_val_b_id","changelog_c"."id","changelog_c"."name" 
FROM "changelog_c" INNER JOIN "changelog_b_my_c_set" ON ("changelog_c"."id" = "changelog_b_my_c_set"."c_id") 
WHERE "changelog_b_my_c_set"."b_id" IN (3,4)


-- Get all the related D's of B mapped with B's id

SELECT ("changelog_b_my_d_set"."b_id") AS "_prefetch_related_val_b_id","changelog_d"."id","changelog_d"."name" 
FROM "changelog_d" INNER JOIN "changelog_b_my_d_set" ON ("changelog_d"."id" = "changelog_b_my_d_set"."d_id") 
WHERE "changelog_b_my_d_set"."b_id" IN (3,4)

因此,在此示例中,与新关系重叠的关键路径不会重复对 B 的查询,而只会为这些新关系创建新查询。