微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么我的所有 SQL 查询都为 Django 使用“Prefetch_related”为嵌套的 MPTT 子项复制了 4 次?

如何解决为什么我的所有 SQL 查询都为 Django 使用“Prefetch_related”为嵌套的 MPTT 子项复制了 4 次?

我有一个 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"
    )

我有一个递归序列化器,因为我想显示任何给定孩子的所有级别的孩子:

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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?