【问题标题】:Django Rest Framework: optimize nester serializers performanceDjango Rest Framework:优化嵌套序列化程序的性能
【发布时间】:2022-02-10 11:25:27
【问题描述】:

我的端点性能有问题,它返回大约 40 个项目,响应大约需要 17 秒。

我有模特:

class GameTask(models.Model):
    name= models.CharField()
    description = RichTextUploadingField()
    ...

还有另一个类似的模型:

class TaskLevel(models.Model):
    master_task = models.ForeignKey(GameTask, related_name="sub_levels", on_delete-models.CASCADE)
    sub_tasks = models.ManyToManyField(GameTask, related_name="master_levels")
    ...

所以基本上我可以执行“正常”任务,但是当我创建 TaskLevel 对象时,我可以将 master_task 添加为一个任务,该任务将固定添加到 sub_tasks 字段的其他任务。

我的序列化器看起来像:

class TaskBaseSerializer(serializers.ModelSerializer):
    fields created by serializers.SerializerMethodField()
    ...

class TaskLevelSerializer(serializers.ModelSerializer):
    sub_tasks = serializers.SerializerMethodField()

    class Meta:
        model = TaskLevel

    def get_sub_tasks(self, obj: TaskLevel):
        sub_tasks = get_sub_tasks(level=obj, user=self.context["request"].user) # method from other module
        return TaskBaseSerializer(sub_tasks, many=True, context=self.context).data

class TaskSerializer(TaskBaseSerializer):
    levels_config = serializers.SerializerMethodField()

    class Meta:
        model = GameTask

    def get_levels_config(self, obj: GameTask):
        if is_mastertask(obj):
            return dict(
                sub_levels=TaskLevelSerializer(
                    obj.sub_levels.all().order_by("number"), many=True, context=self.context
                ).data,
                progress=get_progress(
                    master_task=obj, user=self.context["request"].user
                ),
            )
        return None

当我尝试测量时间时,发现get_levels_config 方法对于一个多级任务(包含 7 个子任务)大约需要 0.25 秒。有什么办法可以提高这种性能吗?如果需要更详细的方法,我会添加它们

【问题讨论】:

    标签: django performance django-rest-framework query-optimization django-serializer


    【解决方案1】:

    您的代码可能遇到 N+1 问题。 TaskSerializer.get_levels_config() 执行来自 obj.sub_levels.all().order_by("number") 的数据库查询。

    序列化多个实例时会发生什么,例如:

    TaskSerializer(tasks, many=True)
    

    每个实例调用.get_levels_config()

    你可以使用prefetch_related & selected_related(更多解释here)。

    由于您使用的是SerializerMethodField,因此您必须手动检查预取对象。还有函数 get_progressget_sub_tasks 我假设它们会进行另一个查询。


    以下是一些可用于您的代码的示例:

    预取:

    GameTask.objects.prefetch_related("sub_levels", "master_levels")
    
    # Accessing deeper level
    GameTask.objects.prefetch_related(
        "sub_levels",
        "master_levels",
        "sub_levels__sub_tasks",
    ).select_related(
        "master_levels__master_task",
    )
    

    检查预取:

    def get_sub_tasks(self, obj: TaskLevel):
        if hasattr(obj, "_prefetched_objects_cache") and obj._prefetched_objects_cache.get("sub_tasks", None):
            return obj._prefetched_objects_cache
        return obj.sub_tasks.all()
    

    【讨论】:

      猜你喜欢
      • 2020-11-17
      • 1970-01-01
      • 1970-01-01
      • 2016-02-09
      • 2018-07-06
      • 1970-01-01
      • 2014-08-20
      • 1970-01-01
      • 2018-05-20
      相关资源
      最近更新 更多