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

@ -34,6 +34,8 @@ First written: 2020/7/1
<p>{{ request.resolver_match.app_name }}</p> <p>{{ request.resolver_match.app_name }}</p>
{% if records %} {% if records %}
{% include "mia_core/pagination.html" %}
{# The table for large screens #} {# The table for large screens #}
<table class="table table-striped table-hover d-none d-md-table general-journal-table"> <table class="table table-striped table-hover d-none d-md-table general-journal-table">
<thead> <thead>

View File

@ -201,5 +201,5 @@ ORDER BY
self.kwargs["subject_code"] + "%", self.kwargs["subject_code"] + "%",
self.kwargs["subject_code"] + "%"]) self.kwargs["subject_code"] + "%"])
self.pagination = Pagination( 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 return self.pagination.records

View File

@ -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 %}
<ul class="pagination">
{% for link in pagination.links %}
{% if link.url is not None %}
<li class="page-item {% if link.is_active %} active {% endif %}{% if not link.is_small_screen %} d-none d-md-inline {% endif %}">
<a class="page-link" href="{{ link.url }}">{{ link.title }}</a>
</li>
{% else %}
<li class="page-item disabled {% if link.is_active %} active {% endif %}{% if not link.is_small_screen %} d-none d-md-inline {% endif %}">
<span class="page-link">{{ link.title }}</span>
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}

View File

@ -21,6 +21,8 @@
import urllib.parse import urllib.parse
from django.utils.translation import pgettext
class UrlBuilder: class UrlBuilder:
"""The URL builder. """The URL builder.
@ -91,6 +93,17 @@ class UrlBuilder:
""" """
return self.del_param(name).add_param(name, value) 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): def __str__(self):
if len(self.params) == 0: if len(self.params) == 0:
return self.base_path return self.base_path
@ -134,10 +147,11 @@ class Pagination:
"""The pagination. """The pagination.
Args: Args:
request (HttpRequest): The request
records (list[Model]): All the records records (list[Model]): All the records
page_no (int): The specified page number page_no (int): The specified page number
page_size (int): The specified number of records per page page_size (int): The specified number of records per page
is_reverse (bool): Whether we should display the last is_reversed (bool): Whether we should display the last
page first page first
Raises: Raises:
@ -145,31 +159,158 @@ class Pagination:
of range or is redundant. of range or is redundant.
Attributes: Attributes:
page_no (int): The current page number is_reversed (bool): Whether we should display the last
page_size (int): The page size page first
records (list[Model]): The records in the current page 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 page_size = None
total_pages = None
is_paged = None
page_no = None
records = None records = None
links = None
DEFAULT_PAGE_SIZE = 10 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 \ self.page_size = page_size \
if page_size is not None \ if page_size is not None \
else self.DEFAULT_PAGE_SIZE else self.DEFAULT_PAGE_SIZE
total_pages = int((len(records) - 1) / self.page_size) + 1 self.total_pages = int(
default_page = 1 if not is_reverse else total_pages (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: if page_no == default_page:
raise PageNoOutOfRangeException() raise PageNoOutOfRangeException()
self.page_no = page_no \ self.page_no = page_no \
if page_no is not None \ if page_no is not None \
else default_page else default_page
if self.page_no > total_pages: if self.page_no > self.total_pages:
raise PageNoOutOfRangeException() raise PageNoOutOfRangeException()
start_no = self.page_size * (self.page_no - 1) start_no = self.page_size * (self.page_no - 1)
self.records = records[start_no:start_no + self.page_size] 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): class PageNoOutOfRangeException(Exception):