我在Django Rest框架中进行了分页,看起来速度非常慢。 Count看起来像罪魁祸首,由于表中有数百万行,每次返回都要花费数百毫秒。
我使用postgresql作为数据库。有没有办法不计算行数仍然使用分页?如果我手动过滤了查询集,则在启用此功能之前性能很好。
答案 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 行,并且不再运行计数查询。