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>
{% if records %}
{% include "mia_core/pagination.html" %}
{# The table for large screens #}
<table class="table table-striped table-hover d-none d-md-table general-journal-table">
<thead>

View File

@ -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

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
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):