Django Rest Framework разбиение на страницы чрезвычайно медленное количество

Я включил разбиение на страницы в Django Rest framework, и это кажется невероятно медленным. Count выглядит как виновник и занимает сотни миллисекунд, чтобы вернуться каждый раз из-за миллионов строк в таблицах.

Я использую PostgreSQL в качестве базы данных. Есть ли способ не считать строки и по-прежнему использовать разбиение на страницы? Производительность была прекрасной, прежде чем это было включено, если я вручную отфильтровал queryset.

3 ответов


переопределить 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)
        ]))

затем в settings.py, set DEFAULT_PAGINATION_CLASS к вашему новому классу разбиения на страницы.

DEFAULT_PAGINATION_CLASS = 'path.to.PageNumberPaginationWithoutCount'

этот подход используется в пример в документах разбиения на страницы.

Edit: из комментариев ниже это звучит так: недостаточно, чтобы предотвратить медленный sql-запрос, поэтому вам может потребоваться переопределить paginate_queryset как хорошо.


Если вы в порядке без подсчета, следующие и предыдущие ссылки, можно использовать следующий пользовательский класс.

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

проблема в том, что запрос, используемый для подсчета, является тем же потенциально сложным, что и для извлечения данных. Это расточительно. PageNumberPagination использует собственный Django Paginator внутренне.

чтобы сделать запрос для count проще переопределить класс paginator 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