Django分页的实现原理
本文主要介绍Django中的分页是如何实现的,首先介绍分页的使用方法,然后介绍其实现原理。
1、使用方法:
VIew视图:
class CategoryArticleListView(BaseMixin,ListView):
template_name = 'category.html'
paginate_by = NUM_PER_PAGE
context_object_name = 'article_list'
def get_queryset(self):
article_list = []
alias = self.kwargs.get('alias')
try:
self.category = Category.objects.get(alias=alias)
except Category.DoesNotExist:
logger.error('no this category')
article_list = self.category.article_set.all()
return article_list
def get_context_data(self,*args,**kwargs):
kwargs['category_name'] = self.category.name
return super(CategoryArticleListView,self).get_context_data(**kwargs)
上面这个视图就实现了,分页。 在html模板中定义:
<ul class="pagination pagination-centered pagination-medium">
<li><a href="?page=1">首页 </a></li>
{% if page_obj.has_previous %}
<li class="abled">
<a href="?page={{page_obj.previous_page_number}}"> 上一页</a>
</li>
{% else %}
<li class="disabled">
<a>上一页</a>
</li>
{% endif %}
{% for page in paginator.page_range %}
{% if page != page_obj.number %}
<li><a href="?page={{page}}">{{page}} </a></li>
{% else %}
<li class="active"><a>{{page}}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next%}
<li class="abled">
<a href="?page={{page_obj.next_page_number}}">下一页</a>
</li>
{% else %}
<li class="disabled">
<a>下一页 </a>
</li>
{% endif %}
<li><a href="?page={{paginator.num_pages}}">末页</a></li>
</ul>
在基于类的视图中,关于分页有一个属性最关键:paginate_by。通过定义该属性,就可以得到Paginator,page等上下文,在模板中就可以自由使用这些变量了;如果不定义该属性,就得不到这些变量。关于分页类paginator,page等类的使用方法可以参考Django官方文档的相关部分。
2、实现原理
看到这,可能想问了,为什么定义了一个paginator_by属性就可以得到Paginator等变量呢?现在就要看下Django源码了。
视图中CategoryArticleListView继承ListView,Listview的父类为BastListView,而BaseListView的一个父类是MultipleObjectMixin。现在拿出该类的源码,代码较多,只粘贴一部分。
class MultipleObjectMixin(ContextMixin):
paginate_by = None
paginator_class = Paginator
page_kwarg = 'page'
def get_paginate_by(self, queryset):
"""
Get the number of items to paginate by, or ``None`` for no pagination.
"""
return self.paginate_by
def get_paginator(self, queryset, per_page, orphans=0,
allow_empty_first_page=True, **kwargs):
"""
Return an instance of the paginator for this view.
"""
return self.paginator_class(
queryset, per_page, orphans=orphans,
allow_empty_first_page=allow_empty_first_page, **kwargs)
def get_paginate_orphans(self):
"""
Returns the maximum number of orphans extend the last page by when
paginating.
"""
return self.paginate_orphans
def get_allow_empty(self):
"""
Returns ``True`` if the view should display empty lists, and ``False``
if a 404 should be raised instead.
"""
return self.allow_empty
def get_context_object_name(self, object_list):
"""
Get the name of the item to be used in the context.
"""
if self.context_object_name:
return self.context_object_name
elif hasattr(object_list, 'model'):
return '%s_list' % object_list.model._meta.model_name
else:
return None
def get_context_data(self, **kwargs):
"""
Get the context for this view.
"""
queryset = kwargs.pop('object_list', self.object_list)
page_size = self.get_paginate_by(queryset)
context_object_name = self.get_context_object_name(queryset)
if page_size:
paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size)
context = {
'paginator': paginator,
'page_obj': page,
'is_paginated': is_paginated,
'object_list': queryset
}
else:
context = {
'paginator': None,
'page_obj': None,
'is_paginated': False,
'object_list': queryset
}
if context_object_name is not None:
context[context_object_name] = queryset
context.update(kwargs)
return super(MultipleObjectMixin, self).get_context_data(**context)
看该类的get_context_data,因为在子类CategoryArticleListView中调用了父类的get_context_data,因此这个方法是关键。 看这句:
page_size = self.get_paginate_by(queryset)
调用 get_paginate_by方法。
def get_paginate_by(self, queryset):
"""
Get the number of items to paginate by, or ``None`` for no pagination.
"""
return self.paginate_by
当我们定义了paginate_by的数值,page-size就是一个非None值,就会执行下面;
paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size)
context = {
'paginator': paginator,
'page_obj': page,
'is_paginated': is_paginated,
'object_list': queryset
}
上下文添加了'paginator', 'page_obj'等变量。
相反,如果不设置paginate_by的值,也就是paginate_by为默认值None,上下文context只有一个object_list,也就是一个查询集。
其实Django的分页实现原理还是比较简单的。
说完了Django,再简单说下Flask。Flask的分页和Django的实现原理是完全不一样的。
Flask也是得到一个关于分页器的一个类, 但它是直接通过Flask-SQlalchemy得到的。
pagination = Article.query.newest().paginate(page,Article.PER_PAGE)
然后在模板中使用这个变量。
{% macro paginate(pagination, page_url) %}
<div class=pagination>
{%- for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}
<a href="/latest/{{ page }}">{{ page }}</a>
{% else %}
<strong>{{ page }}</strong>
{% endif %}
{% else %}
<span class=ellipsis>…</span>
{% endif %}
{%- endfor %}
</div>
{% endmacro %}
微信分享/微信扫码阅读