From 19ac9d32009c98cfc89bddb444e2786ae5f5cb3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Tue, 7 Jul 2020 22:18:40 +0800 Subject: [PATCH] Added the navigation links for pagination. --- accounting/templates/accounting/cash.html | 2 + accounting/views.py | 2 +- mia_core/templates/mia_core/pagination.html | 38 +++++ mia_core/utils.py | 161 ++++++++++++++++++-- 4 files changed, 192 insertions(+), 11 deletions(-) create mode 100644 mia_core/templates/mia_core/pagination.html diff --git a/accounting/templates/accounting/cash.html b/accounting/templates/accounting/cash.html index 9139867..eb6c620 100644 --- a/accounting/templates/accounting/cash.html +++ b/accounting/templates/accounting/cash.html @@ -34,6 +34,8 @@ First written: 2020/7/1

{{ request.resolver_match.app_name }}

{% if records %} + {% include "mia_core/pagination.html" %} + {# The table for large screens #} diff --git a/accounting/views.py b/accounting/views.py index 043a7fa..ecf65b8 100644 --- a/accounting/views.py +++ b/accounting/views.py @@ -201,5 +201,5 @@ ORDER BY self.kwargs["subject_code"] + "%", self.kwargs["subject_code"] + "%"]) self.pagination = Pagination( - records, self.page_no, self.page_size, True) + self.request, records, self.page_no, self.page_size, True) return self.pagination.records diff --git a/mia_core/templates/mia_core/pagination.html b/mia_core/templates/mia_core/pagination.html new file mode 100644 index 0000000..c564980 --- /dev/null +++ b/mia_core/templates/mia_core/pagination.html @@ -0,0 +1,38 @@ +{% comment %} +The Mia Website +base.html: The side-wide layout template + + Copyright (c) 2020 imacat. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Author: imacat@mail.imacat.idv.tw (imacat) +First written: 2020/7/1 +{% endcomment %} + +{# The pagination, if any #} +{% if pagination.is_paged %} + +{% endif %} \ No newline at end of file diff --git a/mia_core/utils.py b/mia_core/utils.py index 2152658..5a3d322 100644 --- a/mia_core/utils.py +++ b/mia_core/utils.py @@ -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):