django:分页功能源码与应用实现
2020-08-27 16:24
1176 查看
分页功能作为网页中的必要功能之一,django提供的内置Paginator实现了如下功能:
- 当前页面是否存在前后页。
- 每页的数据量。
- 访问的页数是否超范围。
一,django分页源码
1,分页由
Paginator类实现,源码如下:
class Paginator: def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True): self.object_list = object_list self._check_object_list_is_ordered() self.per_page = int(per_page) self.orphans = int(orphans) self.allow_empty_first_page = allow_empty_first_page def validate_number(self, number): """Validate the given 1-based page number.""" try: if isinstance(number, float) and not number.is_integer(): raise ValueError number = int(number) except (TypeError, ValueError): raise PageNotAnInteger(_('That page number is not an integer')) if number < 1: raise EmptyPage(_('That page number is less than 1')) if number > self.num_pages: if number == 1 and self.allow_empty_first_page: pass else: raise EmptyPage(_('That page contains no results')) return number def get_page(self, number): """ Return a valid page, even if the page argument isn't a number or isn't in range. """ try: number = self.validate_number(number) except PageNotAnInteger: number = 1 except EmptyPage: number = self.num_pages return self.page(number) def page(self, number): """Return a Page object for the given 1-based page number.""" number = self.validate_number(number) bottom = (number - 1) * self.per_page top = bottom + self.per_page if top + self.orphans >= self.count: top = self.count return self._get_page(self.object_list[bottom:top], number, self) def _get_page(self, *args, **kwargs): """ Return an instance of a single page. This hook can be used by subclasses to use an alternative to the standard :cls:`Page` object. """ return Page(*args, **kwargs) @cached_property def count(self): """Return the total number of objects, across all pages.""" c = getattr(self.object_list, 'count', None) if callable(c) and not inspect.isbuiltin(c) and method_has_no_args(c): return c() return len(self.object_list) @cached_property def num_pages(self): """Return the total number of pages.""" if self.count == 0 and not self.allow_empty_first_page: return 0 hits = max(1, self.count - self.orphans) return ceil(hits / self.per_page) @property def page_range(self): """ Return a 1-based range of pages for iterating through within a template for loop. """ return range(1, self.num_pages + 1) def _check_object_list_is_ordered(self): """ Warn if self.object_list is unordered (typically a QuerySet). """ ordered = getattr(self.object_list, 'ordered', None) if ordered is not None and not ordered: obj_list_repr = ( '{} {}'.format(self.object_list.model, self.object_list.__class__.__name__) if hasattr(self.object_list, 'model') else '{!r}'.format(self.object_list) ) warnings.warn( 'Pagination may yield inconsistent results with an unordered ' 'object_list: {}.'.format(obj_list_repr), UnorderedObjectListWarning, stacklevel=3 )
其中的初始化参数如下:
object_list
,需分页的数据对象,为必需的。使用count()或len()方法的列表、元组、查询集或其他可分割对象。为了实现一致的分页,应该对查询集进行排序,例如使用order by()子句,或者使用模型上的默认排序。这里要注意"对大型查询集进行分页的性能问题".per_page
,必需的。页面上要包括的最大项数,不包括“遗留项”。orphans
,可选的。当你不想最后一页条目很少的时候使用这个。如果最后一页通常有许多小于或等于遗留的项,那么这些项将被添加到前一页(即最后一页),而不是将这些项单独留在页面上。allow_empty_first_pag
,可选的。第一页是否允许为空。如果False且对象列表为空,则会引发一个EmptyPage错误。
其中的方法如下:
- validate_number,验证当前分页数是否大于等于1。
get_page
,返回给定的基于1索引的Page对象,同时还处理超出范围和无效的页码。如果页面不是一个数字,它返回第一个页面。如果页号为负数或大于页数,则返回最后一页。page
,据当前页数对object_list进行切片,返回给定的基于1索引的Page对象。如果给定的页码不存在,则引发InvalidPage。_get_page
,将当前页数及页面对应的数据传给Page
类,创建当前页数据对象。count
,返回object_list长度。num_pages
,获取分页后页面总数。page_range
,将页面总数变为可循环对象。_check_object_list_is_ordered
,对未排序的ORM object_list发出警告。
2,关于_get_page返回的数据:Page`类:
class Page(collections.abc.Sequence): def __init__(self, object_list, number, paginator): self.object_list = object_list self.number = number self.paginator = paginator def __repr__(self): return '<Page %s of %s>' % (self.number, self.paginator.num_pages) def __len__(self): return len(self.object_list) def __getitem__(self, index): if not isinstance(index, (int, slice)): raise TypeError # The object_list is converted to a list so that if it was a QuerySet # it won't be a database hit per __getitem__. if not isinstance(self.object_list, list): self.object_list = list(self.object_list) return self.object_list[index] def has_next(self): return self.number < self.paginator.num_pages def has_previous(self): return self.number > 1 def has_other_pages(self): return self.has_previous() or self.has_next() def next_page_number(self): return self.paginator.validate_number(self.number + 1) def previous_page_number(self): return self.paginator.validate_number(self.number - 1) def start_index(self): """ Return the 1-based index of the first object on this page, relative to total objects in the paginator. """ # Special case, return zero if no items. if self.paginator.count == 0: return 0 return (self.paginator.per_page * (self.number - 1)) + 1 def end_index(self): """ Return the 1-based index of the last object on this page, relative to total objects found (hits). """ # Special case for the last page because there can be orphans. if self.number == self.paginator.num_pages: return self.paginator.count return self.number * self.paginator.per_page
其中的初始化参数如下:
object_list
,必选,此页上已被切片的对象列表。number
,必选,用户传递的页数。paginator
,必选,Paginator的实例化对象。
其中的方法如下:
- has_next,Returns True if there’s a next page.
- has_previous,Returns True if there’s a previous page.
- has_other_pages,Returns True if there’s a next or previous page.
- next_page_number,Returns the next page number. Raises InvalidPage if next page doesn’t exist.
- previous_page_number,Returns the previous page number. Raises InvalidPage if previous page doesn’t exist.
- start_index,输出当前页面的第一行数据在整个数据中的位置。
- end_index,输出当前页面的最后一行数据在整个数据中的位置。
3,关于InvalidPage exceptions:
InvalidPage
,如果所请求的页面无效(即不是整数)或不包含任何对象,则Paginator.page()方法会引发该异常。当然也可以使用下面的异常实现更细的粒度。PageNotAnInteger
,当page()被赋予非整数值时引发。EmptyPage
,当给page()一个有效值但该页上不存在对象时引发。
二,基本操作
通过以上部分源码分析,django以提供众多的属性与方法实现分页相关操作,所以分页操作比较固定。
>>> from django.core.paginator import Paginator >>> objects = ['john', 'paul', 'george', 'ringo'] >>> p = Paginator(objects, 1) >>> p.object_list ['john', 'paul', 'george', 'ringo'] >>> p.count 4 >>> p.num_pages 4 >>> type(p.page_range) <class 'range'> >>> p.page_range range(1, 5) >>> page1 = p.page(1) >>> page1.object_list ['john'] >>> page2 = p.page(2) >>> page2.object_list ['paul'] >>> page2.has_next() True >>> page2.has_previous() True >>> page2.has_other_pages() True >>> page2.next_page_number() 3 >>> page1.has_previous() False >>> page2.start_index() 2 >>> p.page(0) Traceback (most recent call last): File "<console>", line 1, in <module> File "F:\PythonProjects\web\django-yingyongkaifashizhan\MyDjango\venv\lib\site-packages\django\core\pag inator.py", line 70, in page number = self.validate_number(number) File "F:\PythonProjects\web\django-yingyongkaifashizhan\MyDjango\venv\lib\site-packages\django\core\pag inator.py", line 47, in validate_number raise EmptyPage(_('That page number is less than 1')) django.core.paginator.EmptyPage: That page number is less than 1 >>> p.page(1) <Page 1 of 4>
三,案例实现
1,创建模型并添加数据
from django.db import models class PersonInfo(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=20) age = models.IntegerField()
2,添加路由
proj/urls.py: from django.urls import path, include urlpatterns = [ path('', include(('index.urls', 'index'), namespace='index')), ] app/urls.py: from django.urls import path from .views import * urlpatterns = [ path('<page>/', index, name='index'), ]
3,功能视图
from django.shortcuts import render from django.core.paginator import Paginator from django.core.paginator import EmptyPage from django.core.paginator import PageNotAnInteger from .models import PersonInfo def index(request, page): person = PersonInfo.objects.all().order_by('-age') # 设置每一页的数据量为2 p = Paginator(person, 2) try: pages = p.get_page(page) except PageNotAnInteger: # 如果参数page的数据类型不是整型,就返回第一页数据 pages = p.get_page(1) except EmptyPage: # 若用户访问的页数大于实际页数,则返回最后一页的数据 pages = p.get_page(p.num_pages) return render(request, 'index.html', locals())
4,模板文件
<!DOCTYPE html> <html lang="zh-hans"> <head> {% load static %} <title>分页功能</title> <link rel="stylesheet" href="{% static "css/base.css" %}"/> <link rel="stylesheet" href="{% static "css/lists.css" %}"> </head> <body class="app-route model-hkrouteinfo change-list"> <div id="container"> <div id="content" class="flex"> <h1>分页功能</h1> <div id="content-main"> <div class="module filtered" id="changelist"> <form id="changelist-form" method="post"> <div class="results"> <table id="result_list"> <thead> <tr> <th class="action-checkbox-column"> <div class="text"> <span><input type="checkbox"/></span> </div> </th> <th><div class="text">姓名</div></th> <th><div class="text">年龄</div></th> </tr> </thead> <tbody> {% for p in pages %} <tr> <td class="action-checkbox"> <input type="checkbox" class="action-select"> </td> <td>{{ p.name }}</td> <td>{{ p.age }}</td> </tr> {% endfor %} </tbody> </table> </div> <p class="paginator"> {# 上一页的路由地址 #} {% if pages.has_previous %} <a href="{% url 'index:index' pages.previous_page_number %}">上一页</a> {% endif %} {# 列出所有的路由地址 #} {% for n in pages.paginator.page_range %} {% if n == pages.number %} <span class="this-page">{{ pages.number }}</span> {% else %} <a href="{% url 'index:index' n %}">{{ n }}</a> {% endif %} {% endfor %} {# 下一页的路由地址 #} {% if pages.has_next %} <a href="{% url 'index:index' pages.next_page_number %}">下一页</a> {% endif %} </p> </form> </div> </div> </div> </div> </body> </html>
相关文章推荐
- Wifi温控器实际应用效果,源码以及功能实现,可实现互联网访问
- 【hbase】Hbase分页功能的实现及源码
- django 实现分页功能
- django-pure-pagination分页功能的实现
- 使用Django实现分页功能
- django-pure-pagination分页功能的实现
- Django处理URL过程与网站分页功能实现
- Django分页功能的实现代码详解
- Django实现网页分页功能
- 在django中使用自定义标签实现分页功能
- django-pure-pagination分页功能的实现
- 【Django插件使用】django-pagination实现页面分页功能
- django-pure-pagination分页功能的实现
- 构建NetCore应用框架之实战篇(五):BitAdminCore框架1.0登录功能设计实现及源码
- django-pure-pagination分页功能的实现
- 基于SSM框架的web应用分页功能实现的开发笔记
- 通过 Django Pagination 实现简单分页功能
- 【小试身手】几个自定义控件的组合应用,实现简单的“增删改查”功能(有源码)
- Zend Framework实现留言本分页功能(附demo源码下载)
- ListView实现分页功能【附Demo源码】