Django REST Serializer 对多个嵌套关系进行 N+1 数据库调用,3 层

2024-03-19

我的模型有外键关系的情况:

# models.py
class Child(models.Model):
    parent = models.ForeignKey(Parent,)

class Parent(models.Model):
    pass

和我的序列化器:

class ParentSerializer(serializer.ModelSerializer):
    child = serializers.SerializerMethodField('get_children_ordered')

    def get_children_ordered(self, parent):
        queryset = Child.objects.filter(parent=parent).select_related('parent')
        serialized_data = ChildSerializer(queryset, many=True, read_only=True, context=self.context)
        return serialized_data.data

    class Meta:
        model = Parent

当我在视图中调用 Parent 以获得 N 个父级时,Django 在获取子级时会在序列化器内执行 N 次数据库调用。有什么方法可以让所有父母的所有孩子都最小化数据库调用次数吗?

我已经尝试过,但它似乎没有解决我的问题:

class ParentList(generics.ListAPIView):

    def get_queryset(self):
        queryset = Parent.objects.prefetch_related('child')
        return queryset

    serializer_class = ParentSerializer
    permission_classes = (permissions.IsAuthenticated,)

EDIT

我更新了下面的代码以反映 Alex 的反馈......这解决了一个嵌套关系的 N+1 问题。

# serializer.py
class ParentSerializer(serializer.ModelSerializer):
    child = serializers.SerializerMethodField('get_children_ordered')

    def get_children_ordered(self, parent):
        # The all() call should hit the cache
        serialized_data = ChildSerializer(parent.child.all(), many=True, read_only=True, context=self.context)
        return serialized_data.data

    class Meta:
            model = Parent

# views.py
class ParentList(generics.ListAPIView):

    def get_queryset(self):
        children = Prefetch('child', queryset=Child.objects.select_related('parent'))
        queryset = Parent.objects.prefetch_related(children)
        return queryset

    serializer_class = ParentSerializer
    permission_classes = (permissions.IsAuthenticated,)

现在假设我还有一个模型,它是孙子模型:

# models.py
class GrandChild(models.Model):
    parent = models.ForeignKey(Child,)

class Child(models.Model):
    parent = models.ForeignKey(Parent,)

class Parent(models.Model):
    pass

如果我将以下内容放入我的views.py为家长queryset:

queryset = Parent.objects.prefetch_related(children, 'children__grandchildren')

看起来这些孙子并没有被带入 ChildSerializer,因此,我再次运行另一个 N+1 问题。对这个有什么想法吗?

EDIT 2

也许这会提供清晰的信息...也许我仍然遇到 N + 1 数据库调用的原因是因为我的子类和孙子类都是多态的...即

# models.py
class GrandChild(PolymorphicModel):
    child = models.ForeignKey(Child,)

class GrandSon(GrandChild):
    pass

class GrandDaughter(GrandChild):
    pass

class Child(PolymorphicModel):
    parent = models.ForeignKey(Parent,)

class Son(Child):
    pass

class Daughter(Child):
    pass

class Parent(models.Model):
    pass

我的序列化器看起来更像这样:

# serializer.py
class ChildSerializer(serializer.ModelSerializer):
    grandchild = serializers.SerializerMethodField('get_children_ordered')

    def to_representation(self, value):
        if isinstance(value, Son):
            return SonSerializer(value, context=self.context).to_representation(value)
        if isinstance(value, Daughter):
            return DaughterSerializer(value, context=self.context).to_representation(value)

    class Meta:
        model = Child

class ParentSerializer(serializer.ModelSerializer):
    child = serializers.SerializerMethodField('get_children_ordered')

    def get_children_ordered(self, parent):
        queryset = Child.objects.filter(parent=parent).select_related('parent')
        serialized_data = ChildSerializer(queryset, many=True, read_only=True, context=self.context)
        return serialized_data.data

    class Meta:
        model = Parent

另外,对于孙女、孙子来说也是如此,我不会为您提供代码细节,但我想您已经明白了。

当我运行 ParentList 视图并监视数据库查询时,我得到了大约 1000 个查询的信息,仅针对少数家长。

如果我在 django shell 中运行相同的代码,我可以在不超过 25 个查询的情况下完成相同的查询。我怀疑这可能与我使用 django 多态库有关?原因是,数据库中还有一个Child和GrandChild表,再加上每个Son/Daughter、孙/孙女表,总共6个表。跨越那些物体。所以我的直觉告诉我我错过了那些多态表。

或者也许我的数据模型有更优雅的解决方案?


据我记得,嵌套序列化器可以访问预取的关系,只需确保您不修改查询集(即使用all()):

class ParentSerializer(serializer.ModelSerializer):
    child = serializers.SerializerMethodField('get_children_ordered')

    def get_children_ordered(self, parent):
        # The all() call should hit the cache
        serialized_data = ChildSerializer(parent.child.all(), many=True, read_only=True, context=self.context)
        return serialized_data.data

    class Meta:
            model = Parent


class ParentList(generics.ListAPIView):

    def get_queryset(self):
        children = Prefetch('child', queryset=Child.objects.select_related('parent'))
        queryset = Parent.objects.prefetch_related(children)
        return queryset

    serializer_class = ParentSerializer
    permission_classes = (permissions.IsAuthenticated,)             
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Django REST Serializer 对多个嵌套关系进行 N+1 数据库调用,3 层 的相关文章

  • 为 HTML5 Django Web 应用程序实现实时通知系统

    我目前正在开发一个由 Django apache 提供服务的 HTML5 Web 应用程序 该应用程序的目标是监视多个设备 所有客户端逻辑都是用 Angular 编写的 所有数据都来自对后端进行的基于 JSON 的类似 REST 的调用 我
  • manage.pysyncdb 不会为某些模型添加表

    今天我的第二个不太熟练的问题 我有一个 django 项目 其中安装了四个应用程序 当我运行manage py syndb时 它只为其中两个创建表 据我所知 我的任何模型文件都没有问题 并且所有应用程序都在我的设置文件中的 INSTALLE
  • 使用 Google App Engine 和 Django 将第三方库 (twilio) 添加到项目中

    每个人 我是这个领域的新手 我使用 django 框架使用 google 应用引擎开发 Web 应用程序 我有一个关于 python lib dir 问题的故障排除 导入错误 没有名为 的模块 我的 appengine config py
  • Django 中的行级权限

    有没有办法在 django 中进行行级权限 我以为没有 但只是在文档中注意到了这一点 权限不仅可以按对象类型设置 还可以按对象设置 具体的对象实例 通过使用 has add permission 提供了 has change permiss
  • Django 迁移 RunPython 无法调用模型方法

    我正在使用以下命令创建数据迁移RunPython方法 但是 当我尝试在对象上运行方法时 没有定义任何方法 是否可以使用调用模型上定义的方法RunPython 模型方法在迁移中不可用 包括数据迁移 然而 有一个解决方法 它应该与调用模型方法非
  • GeoDjango:距离对象不可序列化

    我刚刚学习 geo django 我可以找出所有地方到一个点的距离 但是当我使用 values方法注释distance场 我得到 TypeError Object of type Distance is not JSON serializa
  • Django/Python:电子邮件回复已更新到站点[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何为 bcrypt.hashpw 设置盐?

    salt yhnqazolr123098765 password bcrypt hashpw password salt repeatpassword bcrypt hashpw repeatpassword salt 我在第二行遇到错误
  • Django Rest Framework 分页设置 - 内容范围

    6 30 15 我怎样才能让这个问题变得更好并且对其他人更有帮助 反馈会有帮助 谢谢 我使用 DRF 作为 Dojo Dgrid Web 应用程序的服务器端 Dojo 需要来自服务器的内容范围或范围响应 目前它不发送任何内容 因此 dgri
  • 无法使用 Django 应用程序从容器连接到 MySQL docker 容器

    当我尝试从运行 Django 应用程序的 docker 容器连接到运行 MySQL 的容器时 出现以下错误 django db utils OperationalError 2003 Can t connect to MySQL serve
  • 如何在 Django Admin 的“更改”页面中显示内嵌上传的图像?

    我正在尝试在中显示内联上传的图像 变更列表 页面在 Django 管理中 这是我的代码如下 models py from django db import models class Product models Model name mod
  • Django 在 TabularInline 视图中禁用编辑(但允许添加)

    我想禁用编辑特定 TabularInline 实例中的所有对象 同时仍然允许添加并仍然允许编辑父模型 我有这个简单的设置 class SuperviseeAdmin admin TabularInline model Supervisee
  • 数据库错误:值对于类型字符变化来说太长(100)

    我有一个 Django 网站 运行我们几年前在内部构建的迷你 CMS 它使用 postgresql 保存简单的标题和一段文本时 出现以下错误 value too long for type character varying 100 奇怪的
  • 在 Django 中翻译文件时的 Git 命令

    我在 Django 中有一个现有的应用程序 我想在页面上添加翻译 在页面上我有 trans Projects 在 po 文件中我添加了 templates staff site html 200 msgid Projects msgid P
  • 为什么我不能对普通变量进行多态?

    我是一名Java程序员 最近开始学习C 我对某事感到困惑 据我了解 在 C 中 要实现多态行为 您必须使用指针或引用 例如 考虑一个类Shape与实施的方法getArea 它有几个子类 每个子类都以不同的方式重写 getArea 然后考虑以
  • 使用 POST 时 Django 中的 MultiValueDictKeyError

    我是 Django Rest 框架的新手 被要求编写我们项目的令牌身份验证部分 需要注意的一件事是 因为我将来不会使用默认的管理站点 所以我编写了登录 注销 注册功能 并通过 POSTMAN 测试了功能 我现在想做的是让新用户注册 登录和注
  • 对原始模型进行预过滤后的相关对象的 Django 查询集

    给定一个模型的查询集 我想获取通过外键相关的另一个模型的查询集 采用 Django 项目文档的博客架构 class Blog models Model name models CharField max length 100 tagline
  • Django 响应总是用 text/html 分块无法设置内容长度

    在我的Django应用程序的views py中 我在尝试设置以下HTTP标头字段后返回一个HttpResponse对象 Create a Response Object with the content to return response
  • Django REST序列化器:创建对象而不保存

    我已经开始使用 Django REST 框架 我想做的是使用一些 JSON 发布请求 从中创建一个 Django 模型对象 然后使用该对象而不保存它 我的 Django 模型称为 SearchRequest 我所拥有的是 api view
  • 如何在 Django 中使用并发进程记录到单个文件而不使用独占锁

    给定一个在多个服务器上同时执行的 Django 应用程序 该应用程序如何记录到单个共享日志文件 在网络共享中 而不保持该文件以独占模式永久打开 当您想要利用日志流时 这种情况适用于 Windows Azure 网站上托管的 Django 应

随机推荐