Skip to content

Commit 3b21946

Browse files
committed
Resuable Pagination Code - SCIO+VCIO
Signed-off-by: Rishi Garg <rishigarg2503@gmail.com>
1 parent 2e33c5b commit 3b21946

3 files changed

Lines changed: 110 additions & 29 deletions

File tree

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
class 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

vulnerabilities/templates/includes/pagination.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,52 @@
1212
</select>
1313
</div>
1414
</div>
15+
<<<<<<< Updated upstream
1516

1617
{% if page_obj.paginator.num_pages > 1 %}
1718
<nav class="pagination is-centered" role="navigation" aria-label="pagination">
1819
{% if page_obj.has_previous %}
1920
<a href="?page={{ page_obj.previous_page_number }}&search={{ search|urlencode }}&page_size={{ current_page_size }}"
21+
=======
22+
23+
{% if page_obj.paginator.num_pages > 1 %}
24+
<nav class="pagination is-centered" role="navigation" aria-label="pagination">
25+
{% if page_obj.has_previous %}
26+
<a href="{{ base_url }}&page={{ page_obj.previous_page_number }}"
27+
>>>>>>> Stashed changes
2028
class="pagination-previous">Previous</a>
2129
{% else %}
2230
<button class="pagination-previous" disabled>Previous</button>
2331
{% endif %}
32+
<<<<<<< Updated upstream
2433

2534
{% if page_obj.has_next %}
2635
<a href="?page={{ page_obj.next_page_number }}&search={{ search|urlencode }}&page_size={{ current_page_size }}"
36+
=======
37+
38+
{% if page_obj.has_next %}
39+
<a href="{{ base_url }}&page={{ page_obj.next_page_number }}"
40+
>>>>>>> Stashed changes
2741
class="pagination-next">Next</a>
2842
{% else %}
2943
<button class="pagination-next" disabled>Next</button>
3044
{% endif %}
45+
<<<<<<< Updated upstream
3146

47+
=======
48+
49+
>>>>>>> Stashed changes
3250
<ul class="pagination-list">
3351
{% for page_num in page_range %}
3452
{% if page_num == '...' %}
3553
<li><span class="pagination-ellipsis">&hellip;</span></li>
3654
{% else %}
3755
<li>
56+
<<<<<<< Updated upstream
3857
<a href="?page={{ page_num }}&search={{ search|urlencode }}&page_size={{ current_page_size }}"
58+
=======
59+
<a href="{{ base_url }}&page={{ page_num }}"
60+
>>>>>>> Stashed changes
3961
class="pagination-link {% if page_num == page_obj.number %}is-current{% endif %}"
4062
aria-label="Go to page {{ page_num }}"
4163
{% if page_num == page_obj.number %}aria-current="page"{% endif %}>
@@ -47,6 +69,7 @@
4769
</ul>
4870
</nav>
4971
{% endif %}
72+
<<<<<<< Updated upstream
5073
</div>
5174

5275
<style>
@@ -75,3 +98,26 @@
7598
window.location.href = newUrl;
7699
}
77100
</script>
101+
=======
102+
</div>
103+
104+
<style>
105+
.select.is-disabled {
106+
opacity: 0.7;
107+
cursor: not-allowed;
108+
}
109+
.select.is-disabled select {
110+
cursor: not-allowed;
111+
}
112+
</style>
113+
114+
<script>
115+
function handlePageSizeChange(value) {
116+
const url = new URL(window.location.href);
117+
const params = new URLSearchParams(url.search);
118+
params.set('page_size', value);
119+
params.delete('page');
120+
window.location.href = `${url.pathname}?${params.toString()}`;
121+
}
122+
</script>
123+
>>>>>>> Stashed changes

vulnerabilities/views.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,13 @@ def get_purl_version_class(purl: models.Package):
6565

6666

6767
class PackageSearch(PaginatedListViewMixin, ListView):
68+
<<<<<<< Updated upstream
6869
"""
6970
View for searching and displaying packages with pagination.
7071
"""
7172

73+
=======
74+
>>>>>>> Stashed changes
7275
model = models.Package
7376
template_name = "packages.html"
7477
ordering = ["type", "namespace", "name", "version"]
@@ -89,10 +92,13 @@ def get_queryset(self, query=None):
8992

9093

9194
class VulnerabilitySearch(PaginatedListViewMixin, ListView):
95+
<<<<<<< Updated upstream
9296
"""
9397
View for searching and displaying vulnerabilities with pagination.
9498
"""
9599

100+
=======
101+
>>>>>>> Stashed changes
96102
model = models.Vulnerability
97103
template_name = "vulnerabilities.html"
98104
ordering = ["vulnerability_id"]

0 commit comments

Comments
 (0)