mirror of
https://github.com/ansible/awx.git
synced 2026-05-10 02:47:36 -02:30
Merge pull request #6084 from cchurch/paginate_this
Add custom paginator to avoid separate COUNT(*) query
This commit is contained in:
@@ -1,16 +1,78 @@
|
|||||||
# Copyright (c) 2015 Ansible, Inc.
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
|
||||||
|
# Django
|
||||||
|
from django.core.paginator import Paginator as DjangoPaginator
|
||||||
|
from django.core.paginator import PageNotAnInteger, EmptyPage
|
||||||
|
from django.db import connections
|
||||||
|
|
||||||
# Django REST Framework
|
# Django REST Framework
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from rest_framework import pagination
|
from rest_framework import pagination
|
||||||
from rest_framework.utils.urls import replace_query_param
|
from rest_framework.utils.urls import replace_query_param
|
||||||
|
|
||||||
|
|
||||||
|
class Paginator(DjangoPaginator):
|
||||||
|
|
||||||
|
def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
|
||||||
|
self.count_field = None
|
||||||
|
# Based on http://stackoverflow.com/questions/156114/best-way-to-get-result-count-before-limit-was-applied
|
||||||
|
# With PostgreSQL, we can use a window function to include the total
|
||||||
|
# count of results (before limit and offset are applied) as an extra
|
||||||
|
# column and avoid having to issue a separate COUNT(*) query.
|
||||||
|
if hasattr(object_list, 'extra'):
|
||||||
|
if connections[getattr(object_list, 'db', None) or 'default'].vendor == 'postgresql':
|
||||||
|
object_list = object_list.extra(select=dict(__count='COUNT(*) OVER()'))
|
||||||
|
self.count_field = '__count'
|
||||||
|
super(Paginator, self).__init__(object_list, per_page, orphans, allow_empty_first_page)
|
||||||
|
assert self.orphans == 0
|
||||||
|
|
||||||
|
def validate_number(self, number, check_num_pages=True):
|
||||||
|
"""
|
||||||
|
Validates the given 1-based page number.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
number = int(number)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
raise PageNotAnInteger('That page number is not an integer')
|
||||||
|
if number < 1:
|
||||||
|
raise EmptyPage('That page number is less than 1')
|
||||||
|
# Optionally skip checking num_pages, since that will result in a
|
||||||
|
# COUNT(*) query.
|
||||||
|
if check_num_pages and number > self.num_pages:
|
||||||
|
if number == 1 and self.allow_empty_first_page:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise EmptyPage('That page contains no results')
|
||||||
|
return number
|
||||||
|
|
||||||
|
def page(self, number):
|
||||||
|
"""
|
||||||
|
Returns a Page object for the given 1-based page number.
|
||||||
|
"""
|
||||||
|
number = self.validate_number(number, check_num_pages=bool(self.count_field is None))
|
||||||
|
bottom = (number - 1) * self.per_page
|
||||||
|
top = bottom + self.per_page
|
||||||
|
sub_list = self.object_list[bottom:top]
|
||||||
|
if self.count_field and self._count is None:
|
||||||
|
# Execute one query to fetch all results.
|
||||||
|
sub_list = list(sub_list)
|
||||||
|
try:
|
||||||
|
# Get the total count from the first result.
|
||||||
|
self._count = getattr(sub_list[0], self.count_field)
|
||||||
|
except IndexError:
|
||||||
|
# If no results were returned, we still don't know the total
|
||||||
|
# count, but do know that we've reached an empty page.
|
||||||
|
if number > 1 or not self.allow_empty_first_page:
|
||||||
|
raise EmptyPage('That page contains no results')
|
||||||
|
return self._get_page(sub_list, number, self)
|
||||||
|
|
||||||
|
|
||||||
class Pagination(pagination.PageNumberPagination):
|
class Pagination(pagination.PageNumberPagination):
|
||||||
|
|
||||||
page_size_query_param = 'page_size'
|
page_size_query_param = 'page_size'
|
||||||
max_page_size = settings.MAX_PAGE_SIZE
|
max_page_size = settings.MAX_PAGE_SIZE
|
||||||
|
django_paginator_class = Paginator
|
||||||
|
|
||||||
def get_next_link(self):
|
def get_next_link(self):
|
||||||
if not self.page.has_next():
|
if not self.page.has_next():
|
||||||
|
|||||||
Reference in New Issue
Block a user