Django Rest Framework分页极其缓慢

Smi*_*lls 9 python django postgresql pagination django-rest-framework

我在Django Rest框架中进行了分页,看起来速度非常慢.Count看起来像罪魁祸首,由于表中数百万行,每次返回都要花费数百毫秒.

我使用postgresql作为数据库.有没有办法不计算行数仍然使用分页?如果我手动过滤了查询集,则在启用此功能之前性能很好.

Flo*_*ian 14

问题是,用于计数的查询与用于获取数据的潜在复杂查询相同。这样比较浪费。PageNumberPaginationPaginator内部使用 Django 自己的。

为了使计数查询更简单,覆盖分页器类 DRF 使用:

from django.core.paginator import Paginator
from django.utils.functional import cached_property
from rest_framework.pagination import PageNumberPagination

class FasterDjangoPaginator(Paginator):
    @cached_property
    def count(self):
        # only select 'id' for counting, much cheaper
        return self.object_list.values('id').count()


class FasterPageNumberPagination(PageNumberPagination):
    django_paginator_class = FasterDjangoPaginator
Run Code Online (Sandbox Code Playgroud)

  • 在到处使用它之前,请注意,选择“id”将更改涉及聚合的查询(“id”将附加到您要分组的其他内容中)。 (2认同)

Ala*_*air 8

覆盖get_paginated_response分页类的方法,不包括计数.您可以参考基本实现了的PageNumberPagination类来看看你应该返回什么.

from rest_framework.pagination import PageNumberPagination
from collections import OrderedDict # requires Python 2.7 or later

class PageNumberPaginationWithoutCount(PageNumberPagination):
    # Set any other options you want here like page_size

    def get_paginated_response(self, data):
        return Response(OrderedDict([
            ('next', self.get_next_link()),
            ('previous', self.get_previous_link()),
            ('results', data)
        ]))
Run Code Online (Sandbox Code Playgroud)

然后在你的settings.py,设置DEFAULT_PAGINATION_CLASS你的新分页课程.

DEFAULT_PAGINATION_CLASS = 'path.to.PageNumberPaginationWithoutCount'
Run Code Online (Sandbox Code Playgroud)

此方法用于分页文档中的示例.

编辑:从下面的评论中听起来这可能不足以阻止慢速sql查询,所以你可能还需要覆盖paginate_queryset.

  • "覆盖分页类的get_paginated_response方法,不包括计数." 借调,是的.您可能还需要考虑对大型数据集使用CursorPagination,因为与页面和偏移样式不同,它通过用户页面不会变得越来越慢. (3认同)
  • 我尝试了,它从结果中删除了计数,但仍然根据django调试工具包运行它.但是,重写paginate_queryset(self,queryset,request,view = None):并设置self.count = 0而不是self.count = _get_count(queryset)对其进行排序.我也刚刚禁用了下一个和之前的,因为我不需要它们. (2认同)
  • 你是如何覆盖paginate_queryset的? (2认同)

xra*_*age 6

如果你不需要计数,下一个和上一个链接就可以了,可以使用以下自定义类。

import sys
from collections import OrderedDict

from django.core.paginator import Paginator
from django.utils.functional import cached_property
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response


class CustomPaginatorClass(Paginator):
    @cached_property
    def count(self):
        return sys.maxsize


# To Avoid large table count query, We can use this paginator class
class LargeTablePagination(PageNumberPagination):
    django_paginator_class = CustomPaginatorClass

    def get_paginated_response(self, data):
        return Response(OrderedDict([
            ('page', self.page.number),
            ('results', data)
        ]))
Run Code Online (Sandbox Code Playgroud)