为什么我的所有 SQL 查询都被 Django 重复 4 次,使用 "Prefetch_related" 嵌套 MPTT children?
Why are all my SQL queries being duplicated 4 times for Django using "Prefetch_related" for nested MPTT children?
我有一个 Child 自身具有外键的 MPTT 模型:
class Child(MPTTModel):
title = models.CharField(max_length=255)
parent = TreeForeignKey(
"self", on_delete=models.CASCADE, null=True, blank=True, related_name="children"
)
我有一个递归序列化程序,因为我想为任何给定的 Child:
显示所有级别的 children
class ChildrenSerializer(serializers.HyperlinkedModelSerializer):
url = HyperlinkedIdentityField(
view_name="app:children-detail", lookup_field="pk"
)
class Meta:
model = Child
fields = ("url", "title", "children")
def get_fields(self):
fields = super(ChildrenSerializer, self).get_fields()
fields["children"] = ChildrenSerializer(many=True)
return fields
我正在尝试减少访问 Child 的 DetailView 时进行的 duplicate/similar 查询的数量。
下面的视图适用于深度 2 - 但是,“深度”并不总是已知的或静态的。
class ChildrenDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Child.objects.prefetch_related(
"children",
"children__children",
# A depth of 3 will additionally require "children__children__children",
# A depth of 4 will additionally require "children__children__children__children",
# etc.
)
serializer_class = ChildrenSerializer
lookup_field = "pk"
注意:如果我不使用 prefetch_related
而只是将查询集设置为 Child.objects.all()
,每个 SQL 查询都会重复四次...我不知道为什么。
如何利用 Child 的深度(即 Child 的 MPTT level
字段)来优化预取?我应该覆盖视图的 get_object
and/or retrieve
吗?
如果我在预取中添加多得离谱的深度,这有什么关系吗?例如。 children__children__children__children__children__children__children__children
?它似乎并没有增加不需要该深度级别的 Children objects 的查询数量。
编辑:
嗯,不知道为什么,但是当我尝试序列化任何 Child 的顶部 parent(即 MPTT 的 get_root
)时,它重复了 SQL 查询四次???
class Child(MPTTModel):
...
@property
def top_parent(self):
return self.get_root()
class ChildrenSerializer(serializers.HyperlinkedModelSerializer):
...
top_parent = ParentSerializer()
fields = ("url", "title", "children", "top_parent")
编辑 2
添加任意 SerializerMethodField 确认它被查询了四次...出于某种原因?例如
class ChildrenSerializer(serializers.HyperlinkedModelSerializer):
...
foo = serializers.SerializerMethodField()
def get_foo(self, obj):
print("bar")
return obj.get_root().title
这将打印“bar”四次。 SQL查询也根据django-debug-toolbar:
重复四次
SELECT ••• FROM "app_child" WHERE ("app_child"."parent_id" IS NULL AND "app_child"."tree_id" = '7') LIMIT 21
4 similar queries. Duplicated 4 times.
您是否在使用 DRF 的可浏览 API?它在 rest_framework.renderers.BrowsableAPIRenderer.get_context
.
中为 HTML 表单再初始化序列化程序 3 次
如果您使用 Postman 执行相同的请求,“bar”应该只打印一次。
我有一个 Child 自身具有外键的 MPTT 模型:
class Child(MPTTModel):
title = models.CharField(max_length=255)
parent = TreeForeignKey(
"self", on_delete=models.CASCADE, null=True, blank=True, related_name="children"
)
我有一个递归序列化程序,因为我想为任何给定的 Child:
显示所有级别的 childrenclass ChildrenSerializer(serializers.HyperlinkedModelSerializer):
url = HyperlinkedIdentityField(
view_name="app:children-detail", lookup_field="pk"
)
class Meta:
model = Child
fields = ("url", "title", "children")
def get_fields(self):
fields = super(ChildrenSerializer, self).get_fields()
fields["children"] = ChildrenSerializer(many=True)
return fields
我正在尝试减少访问 Child 的 DetailView 时进行的 duplicate/similar 查询的数量。
下面的视图适用于深度 2 - 但是,“深度”并不总是已知的或静态的。
class ChildrenDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Child.objects.prefetch_related(
"children",
"children__children",
# A depth of 3 will additionally require "children__children__children",
# A depth of 4 will additionally require "children__children__children__children",
# etc.
)
serializer_class = ChildrenSerializer
lookup_field = "pk"
注意:如果我不使用 prefetch_related
而只是将查询集设置为 Child.objects.all()
,每个 SQL 查询都会重复四次...我不知道为什么。
如何利用 Child 的深度(即 Child 的 MPTT level
字段)来优化预取?我应该覆盖视图的 get_object
and/or retrieve
吗?
如果我在预取中添加多得离谱的深度,这有什么关系吗?例如。 children__children__children__children__children__children__children__children
?它似乎并没有增加不需要该深度级别的 Children objects 的查询数量。
编辑:
嗯,不知道为什么,但是当我尝试序列化任何 Child 的顶部 parent(即 MPTT 的 get_root
)时,它重复了 SQL 查询四次???
class Child(MPTTModel):
...
@property
def top_parent(self):
return self.get_root()
class ChildrenSerializer(serializers.HyperlinkedModelSerializer):
...
top_parent = ParentSerializer()
fields = ("url", "title", "children", "top_parent")
编辑 2
添加任意 SerializerMethodField 确认它被查询了四次...出于某种原因?例如
class ChildrenSerializer(serializers.HyperlinkedModelSerializer):
...
foo = serializers.SerializerMethodField()
def get_foo(self, obj):
print("bar")
return obj.get_root().title
这将打印“bar”四次。 SQL查询也根据django-debug-toolbar:
重复四次 SELECT ••• FROM "app_child" WHERE ("app_child"."parent_id" IS NULL AND "app_child"."tree_id" = '7') LIMIT 21
4 similar queries. Duplicated 4 times.
您是否在使用 DRF 的可浏览 API?它在 rest_framework.renderers.BrowsableAPIRenderer.get_context
.
如果您使用 Postman 执行相同的请求,“bar”应该只打印一次。