Django Rest Framework分页极其缓慢

时间:2015-07-31 07:05:15

标签: python django postgresql pagination django-rest-framework

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

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

5 个答案:

答案 0 :(得分:7)

覆盖分页类的get_paginated_response方法,不包括计数。您可以参考PageNumberPagination课程的base implementation来查看您应该返回的内容。

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)
        ]))

然后在settings.py中,将DEFAULT_PAGINATION_CLASS设置为新的分页类。

DEFAULT_PAGINATION_CLASS = 'path.to.PageNumberPaginationWithoutCount'

此方法用于example in the pagination docs

从下面的评论中

编辑:听起来这可能不足以阻止慢速sql查询,因此您可能还需要覆盖paginate_queryset

答案 1 :(得分:2)

问题是,用于计数的查询与用于获取数据的查询具有相同的复杂性。这相当浪费。 PageNumberPagination在内部使用Django自己的Paginator

要使count更简单的查询覆盖,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

答案 2 :(得分:1)

如果您没有计数,可以使用下一个和上一个链接,可以使用以下自定义类。

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)
        ]))

答案 3 :(得分:0)

其他答案对我不起作用或仍在执行额外的COUNT(*)查询。

这将摆脱所有分页,计数查询,并仅返回JSON响应:

from rest_framework.pagination import PageNumberPagination


class NoCountPagination(PageNumberPagination):
    page_size = None

    def get_paginated_response(self, data):
        return Response({
            'results', data
        })

要使用它:

from rest_framework import viewsets
from .models import MyModel
from .serializers import MySerializer


class CustomApiViewSet(viewsets.ReadOnlyModelViewSet):
    """
    Simple viewset for viewing MyModels (as a list, or individually).
    """
    queryset = MyModel.objects.all()
    serializer_class = MySerializer
    pagination_class = NoCountPagination

当心,这将返回查询集中的所有行。在几乎所有情况下,我认为最好直接使用PageNumberPagination或使用上面@Florian的解决方案来加快速度。

答案 4 :(得分:0)

添加到 getup8 的响应中,我能够让它在不返回计数但也不返回所有行的情况下工作(假设您在站点的 REST_FRAMEWORK 设置中将 PAGE_SIZE 设置为合理的数字)。

import_file

并使用它:

from rest_framework.pagination import LimitOffsetPagination
    
class NoCountPaginator(LimitOffsetPagination):
    def get_count(self, queryset):
        return 99999999

    def get_paginated_response(self, data):
        return Response(OrderedDict([
            ('results', data)
        ]))

我将 PAGE_SIZE 设置为 25,因此 API 始终只返回前 25 行,并且不再运行计数查询。