Skip to content

Commit ad70f59

Browse files
committed
Fix pagination
Existing approaches to "no count" pagination do not seem to include support for pagination in the Django admin, so: - Subclass Django's core Paginator - override count - Subclass Django's contrib admin view ChangeList.get_results - Use len instead of count - Ruff fixes for try/except - Subclass Django's contrib ModelAdmin - override get_paginator - override get_changelist
1 parent 9f9507d commit ad70f59

File tree

1 file changed

+42
-18
lines changed

1 file changed

+42
-18
lines changed

django_mongodb_backend/admin.py

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,56 @@
11
from django.contrib import admin
2+
from django.contrib.admin.options import IncorrectLookupParameters
23
from django.contrib.admin.views.main import ChangeList
4+
from django.core.paginator import InvalidPage, Paginator
5+
from django.utils.functional import cached_property
36

47

5-
class EncryptedPaginator:
6-
# TODO: Implement pagination for encrypted data. This paginator
7-
# currently returns all results in a single page.
8-
def __init__(self, queryset, per_page):
9-
self.queryset = queryset
10-
self.per_page = per_page
11-
12-
def page(self, number):
13-
results = list(self.queryset)
14-
has_next = False
15-
return results, has_next
8+
class EncryptedPaginator(Paginator):
9+
@cached_property
10+
def count(self):
11+
return len(self.object_list)
1612

1713

1814
class EncryptedChangeList(ChangeList):
1915
def get_results(self, request):
20-
paginator = EncryptedPaginator(self.queryset, self.list_per_page)
21-
self.result_list, _ = paginator.page(self.page_num + 1)
16+
"""
17+
This is django.contrib.admin.views.main.ChangeList.get_results with
18+
a single modification to avoid COUNT queries.
19+
"""
20+
paginator = self.model_admin.get_paginator(request, self.queryset, self.list_per_page)
21+
result_count = paginator.count
22+
if self.model_admin.show_full_result_count:
23+
# Modification: avoid COUNT query by using len() on the root queryset
24+
full_result_count = len(self.root_queryset)
25+
else:
26+
full_result_count = None
27+
can_show_all = result_count <= self.list_max_show_all
28+
multi_page = result_count > self.list_per_page
29+
if (self.show_all and can_show_all) or not multi_page:
30+
result_list = self.queryset._clone()
31+
else:
32+
try:
33+
result_list = paginator.page(self.page_num).object_list
34+
except InvalidPage as err:
35+
raise IncorrectLookupParameters from err
36+
self.result_count = result_count
37+
self.show_full_result_count = self.model_admin.show_full_result_count
38+
self.show_admin_actions = not self.show_full_result_count or bool(full_result_count)
39+
self.full_result_count = full_result_count
40+
self.result_list = result_list
41+
self.can_show_all = can_show_all
42+
self.multi_page = multi_page
43+
self.paginator = paginator
2244

23-
self.result_count = len(self.result_list)
24-
self.full_result_count = self.result_count
2545

26-
self.can_show_all = True
27-
self.multi_page = False
46+
class EncryptedModelAdmin(admin.ModelAdmin):
47+
"""
48+
A ModelAdmin that uses EncryptedPaginator and EncryptedChangeList
49+
to avoid COUNT queries in the admin changelist.
50+
"""
2851

52+
def get_paginator(self, request, queryset, per_page):
53+
return EncryptedPaginator(queryset, per_page)
2954

30-
class EncryptedModelAdmin(admin.ModelAdmin):
3155
def get_changelist(self, request, **kwargs):
3256
return EncryptedChangeList

0 commit comments

Comments
 (0)