11class PaginatedListViewMixin :
2+ """
3+ A mixin that adds pagination functionality to ListView-based views.
4+ """
5+
26 paginate_by = 20
37 max_page_size = 100
48
@@ -9,6 +13,9 @@ class PaginatedListViewMixin:
913 ]
1014
1115 def get_paginate_by (self , queryset = None ):
16+ """
17+ Get the number of items to paginate by from the request.
18+ """
1219 try :
1320 page_size = int (self .request .GET .get ("page_size" , self .paginate_by ))
1421 if page_size <= 0 :
@@ -17,42 +24,64 @@ def get_paginate_by(self, queryset=None):
1724 except (ValueError , TypeError ):
1825 return self .paginate_by
1926
20- def get_context_data (self , ** kwargs ):
21- context = super ().get_context_data (** kwargs )
27+ def get_pagination_context (self , paginator , page_obj ):
28+ """
29+ Generate pagination-related context data, preserving filters.
30+ """
31+ if not paginator or not page_obj :
32+ return {}
2233
2334 current_page_size = self .get_paginate_by ()
24- total_count = context ["paginator" ].count
25-
26- context .update (
27- {
28- "current_page_size" : current_page_size ,
29- "page_size_choices" : self .PAGE_SIZE_CHOICES ,
30- "total_count" : total_count ,
31- "page_range" : self ._get_page_range (
32- context ["paginator" ], context ["page_obj" ].number
33- ),
34- "search" : self .request .GET .get ("search" , "" ),
35- }
36- )
35+ total_count = paginator .count
3736
38- return context
37+ query_params = self .request .GET .copy ()
38+ query_params .pop ("page" , None )
39+
40+ base_query_string = query_params .urlencode ()
41+ base_url = f"?{ base_query_string } " if base_query_string else "?"
3942
40- def _get_page_range ( self , paginator , current_page , window = 2 ):
43+ pages = []
4144 if paginator .num_pages <= 5 :
42- return range (1 , paginator .num_pages + 1 )
45+ pages = [str (i ) for i in range (1 , paginator .num_pages + 1 )]
46+ else :
47+ pages .append ("1" )
4348
44- pages = [1 ]
45- if current_page > 3 :
46- pages .append ("..." )
49+ if page_obj .number > 3 :
50+ pages .append ("..." )
4751
48- start_page = max (2 , current_page - window )
49- end_page = min (paginator .num_pages - 1 , current_page + window )
50- pages .extend (range (start_page , end_page + 1 ))
52+ start_page = max (2 , page_obj . number - 2 )
53+ end_page = min (paginator .num_pages - 1 , page_obj . number + 2 )
54+ pages .extend (str ( i ) for i in range (start_page , end_page + 1 ))
5155
52- if current_page < paginator .num_pages - 2 :
53- pages .append ("..." )
56+ if page_obj . number < paginator .num_pages - 2 :
57+ pages .append ("..." )
5458
55- if paginator .num_pages not in pages :
56- pages .append (paginator .num_pages )
59+ if str ( paginator .num_pages ) not in pages :
60+ pages .append (str ( paginator .num_pages ) )
5761
58- return pages
62+ return {
63+ "current_page_size" : current_page_size ,
64+ "page_size_choices" : self .PAGE_SIZE_CHOICES ,
65+ "total_count" : total_count ,
66+ "page_range" : pages ,
67+ "search" : self .request .GET .get ("search" , "" ),
68+ "base_url" : base_url ,
69+ "previous_page_url" : f"{ base_url } &page={ page_obj .previous_page_number } "
70+ if page_obj .has_previous ()
71+ else None ,
72+ "next_page_url" : f"{ base_url } &page={ page_obj .next_page_number } "
73+ if page_obj .has_next ()
74+ else None ,
75+ }
76+
77+ def get_context_data (self , ** kwargs ):
78+ """
79+ Add pagination context to the existing context data.
80+ """
81+ context = super ().get_context_data (** kwargs )
82+ paginator = context .get ("paginator" )
83+ page_obj = context .get ("page_obj" )
84+ pagination_context = self .get_pagination_context (paginator , page_obj )
85+ context .update (pagination_context )
86+
87+ return context
0 commit comments