Added the navigation links for pagination.

This commit is contained in:
2020-07-07 22:18:40 +08:00
parent 3bce775729
commit 19ac9d3200
4 changed files with 192 additions and 11 deletions

View File

@ -21,6 +21,8 @@
import urllib.parse
from django.utils.translation import pgettext
class UrlBuilder:
"""The URL builder.
@ -91,6 +93,17 @@ class UrlBuilder:
"""
return self.del_param(name).add_param(name, value)
def clone(self):
"""Returns a copy of this URL builder.
Returns:
UrlBuilder: A copy of this URL builder.
"""
another = UrlBuilder(self.base_path)
another.params = [
self.Param(x.name, x.value) for x in self.params]
return another
def __str__(self):
if len(self.params) == 0:
return self.base_path
@ -134,42 +147,170 @@ class Pagination:
"""The pagination.
Args:
request (HttpRequest): The request
records (list[Model]): All the records
page_no (int): The specified page number
page_size (int): The specified number of records per page
is_reverse (bool): Whether we should display the last
page first
is_reversed (bool): Whether we should display the last
page first
Raises:
PageNoOutOfRangeError: if the specified page number is out
of range or is redundant.
Attributes:
page_no (int): The current page number
page_size (int): The page size
records (list[Model]): The records in the current page
is_reversed (bool): Whether we should display the last
page first
page_size (int): The page size.
total_pages (int): The total number of pages available.
is_paged (bool): Whether there are more than one page.
page_no (int): The current page number.
records (list[Model]): The records in the current page.
links (list[Link]): The navigation links in the pagination
bar.
"""
page_no = None
is_reversed = False
page_size = None
total_pages = None
is_paged = None
page_no = None
records = None
links = None
DEFAULT_PAGE_SIZE = 10
def __init__(self, records, page_no, page_size, is_reverse=False):
def __init__(self, request, records, page_no,
page_size, is_reversed=False):
self.is_reversed = is_reversed
self.page_size = page_size \
if page_size is not None \
else self.DEFAULT_PAGE_SIZE
total_pages = int((len(records) - 1) / self.page_size) + 1
default_page = 1 if not is_reverse else total_pages
self.total_pages = int(
(len(records) - 1) / self.page_size) + 1
self.is_paged = self.total_pages > 1
if not self.is_paged:
self.page_no = 1
self.records = records
self.links = []
return
default_page = 1 if not is_reversed else self.total_pages
if page_no == default_page:
raise PageNoOutOfRangeException()
self.page_no = page_no \
if page_no is not None \
else default_page
if self.page_no > total_pages:
if self.page_no > self.total_pages:
raise PageNoOutOfRangeException()
start_no = self.page_size * (self.page_no - 1)
self.records = records[start_no:start_no + self.page_size]
self.create_pagination_bar(request)
def create_pagination_bar(self, request):
"""Creates the pagination bar.
Args:
request (HttpRequest): The request
"""
base_url = UrlBuilder(
request.get_full_path()).del_param("page")
self.links = []
# The previous page
link = self.Link()
link.title = pgettext("Pagination|", "Previous")
if self.page_no > 1:
if self.page_no - 1 == 1:
if not self.is_reversed:
link.url = str(base_url)
else:
link.url = str(base_url.clone().add_param(
"page", "1"))
else:
link.url = str(base_url.clone().add_param(
"page", str(self.page_no - 1)))
link.is_small_screen = True
self.links.append(link)
# The first page
link = self.Link()
link.title = "1"
if not self.is_reversed:
link.url = str(base_url)
else:
link.url = str(base_url.clone().add_param("page", "1"))
if self.page_no == 1:
link.is_active = True
self.links.append(link)
# The previous ellipsis
if self.page_no > 4:
link = self.Link()
if self.page_no > 5:
link.title = pgettext("Pagination|", "...")
else:
link.title = "2"
link.url = str(base_url.clone().add_param(
"page", "2"))
self.links.append(link)
# The nearby pages
for no in range(self.page_no - 2, self.page_no + 3):
if no <= 1 or no >= self.total_pages:
continue
link = self.Link()
link.title = str(no)
link.url = str(base_url.clone().add_param(
"page", str(no)))
if no == self.page_no:
link.is_active = True
self.links.append(link)
# The next ellipsis
if self.page_no + 3 < self.total_pages:
link = self.Link()
if self.page_no + 4 < self.total_pages:
link.title = pgettext("Pagination|", "...")
else:
link.title = str(self.total_pages - 1)
link.url = str(base_url.clone().add_param(
"page", str(self.total_pages - 1)))
self.links.append(link)
# The last page
link = self.Link()
link.title = str(self.total_pages)
if self.is_reversed:
link.url = str(base_url)
else:
link.url = str(base_url.clone().add_param(
"page", str(self.total_pages)))
if self.page_no == self.total_pages:
link.is_active = True
self.links.append(link)
# The next page
link = self.Link()
link.title = pgettext("Pagination|", "Next")
if self.page_no < self.total_pages:
if self.page_no + 1 == self.total_pages:
if self.is_reversed:
link.url = str(base_url)
else:
link.url = str(base_url.clone().add_param(
"page", str(self.total_pages)))
else:
link.url = str(base_url.clone().add_param(
"page", str(self.page_no + 1)))
link.is_small_screen = True
self.links.append(link)
class Link:
"""A navigation link in the pagination bar.
Attributes:
url (str): The link URL, or for a non-link slot.
title (str): The title of the link.
is_active (bool): Whether this link is currently active.
is_small_screen (bool): Whether this link is for small
screens
"""
url = None
title = None
is_active = False
is_small_screen = False
class PageNoOutOfRangeException(Exception):