1 分页器的原理和使用
2 Django自身提供了一些类来实现管理分页,数据被分在不同的页面中,并带有“上一页/下一页”标签。这个类叫做Pagination,其定义位于 django/core/paginator.py 中。
3 源码分析
class Paginator:
def __init__(self, object_list, per_page, orphans=0,
allow_empty_first_page=True):
# object_listt:可以是列表,元组,查询集或其他含有 count() 或 __len__()方法的可切片对 象。对于连续的分页,查询集应该有序,例如有order_by()项或默认ordering参数。
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 # 如果它是浮点数或者不是整数就会抛出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):# 这里要注意,当调用这个page时,会返回self._get_page(),当调用
self._get_page()时,会返回Page(*args, **kwargs),,这样也就意味着page(p是小写)的返回值是一个Page对象(P是大写),就能调用该对象的相应的方法
"""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."""
try:
return self.object_list.count()
except (AttributeError, TypeError):
# AttributeError if object_list has no count() method.
# TypeError if object_list.count() requires arguments
# (i.e. is of type list).
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
)
QuerySetPaginator = Paginator # For backwards-compatibility.
class Page(collections.abc.Sequence): 这个类同样很重要
它继承了collections.abc.Sequence这个东西,这个东西我还不太理解,不过不妨碍我对源码的理解
def __init__(self, object_list, number, paginator):
self.object_list = object_list
self.number = number
self.paginator = 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): # 如果有下一页,则返回True
return self.number < self.paginator.num_pages 如果还有下一页就肯定小于总的啊,就一定会返回True
def has_previous(self): #如果·有上一页,就一定大于1啊,也就是要返回True
return self.number > 1
def has_other_pages(self):#如果有上一页或者下一页,就返回True
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
总结一下:
Paginator类
object_list:可以是列表,元组,查询集或其他含有 count() 或 _len_()方法的可切片对象。对于连续的分页,查询集应该有序,例如有order_by()项或默认ordering参数。
per_page:每一页中包含条目数目的最大值,不包括独立成页的那页。(见下面 orphans参数解释)
orphans=0:当你使用此参数时说明你不希望最后一页只有很少的条目。如果最后一页的条目数少于等于orphans值,则这些条目会被归并到上一页中(此时的上一页变为最后一页)。例如有23项条目, per_page=10,orphans=0,则有3页,分别为10,10,3.如果orphans>=3,则为2页,分别为10,13。**
allow_empty_first_page=True: 默认允许第一页为空。
.类方法:
* Paginator.page(number):根据参数number返回一个Page对象。(number为1的倍数)*
.类属型:
Paginator.count:所有页面对象总数,即统计object_list中item数目。当计算object_list所含对象的数量时, Paginator会首先尝试调用object_list.count()。如果object_list没有 count() 方法,Paginator 接着会回退使用len(object_list)。
Pagnator.num_pages:页面总数。
pagiator.page_range:页面范围,从1开始,例如[1,2,3,4]。
Page类:
Page.has_next() 如果有下一页,则返回True。
Page.has_previous() 如果有上一页,返回 True。
Page.has_other_pages() 如果有上一页或下一页,返回True。
Page.next_page_number() 返回下一页的页码。如果下一页不存在,抛出InvlidPage异常。
Page.previous_page_number() 返回上一页的页码。如果上一页不存在,抛出InvalidPage异常。
Page.start_index() 返回当前页上的第一个对象,相对于分页列表的所有对象的序号,从1开始。比如,将五个对象的列表分为每页两个对象,第二页的start_index()会返回3。
Page.end_index() 返回当前页上的最后一个对象,相对于分页列表的所有对象的序号,从1开始。 比如,将五个对象的列表分为每页两个对象,第二页的end_index() 会返回 4。
2.类属型
Page.object_list 当前页上所有对象的列表。
Page.number 当前页的序号,从1开始。
Page.paginator 相关的Paginator对象。
异常处理:
InvalidPage(Exception): 异常的基类,当paginator传入一个无效的页码时抛出。
Paginator.page()放回在所请求的页面无效(比如不是一个整数)时,或者不包含任何对象时抛出异常。通常,捕获****InvalidPage异常就够了,但是如果你想更加精细一些,可以捕获以下两个异常之一:
exception PageNotAnInteger,当向page()提供一个不是整数的值时抛出。
exception EmptyPage,当向page()提供一个有效值,但是那个页面上没有任何对象时抛出。
这两个异常都是InalidPage的子类,所以可以通过简单的except InvalidPage来处理它们。
不足之处,欢迎批评指正,thanks!