如何解决为什么我的所有 SQL 查询都为 Django 使用“Prefetch_related”为嵌套的 MPTT 子项复制了 4 次?
class Child(MPTTModel):
title = models.CharField(max_length=255)
parent = TreeForeignKey(
"self",on_delete=models.CASCADE,null=True,blank=True,related_name="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 时进行的重复/类似查询的数量。
下面的视图适用于深度为 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
和/或 retrieve
吗?
如果我在预取中添加一个荒谬数量的深度是否重要?例如。 children__children__children__children__children__children__children__children
?它似乎不会增加对不需要该深度级别的 Children 对象的查询数量。
编辑:
嗯,不知道为什么,但是当我尝试序列化任何 Child 的顶级父级(即 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","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”应该只打印一次。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。