django 分页

终于有一天你恍然大悟,或许成长的一部分就是这样,你不断跟熟悉的人告别,跟熟悉的地方告别,然后走上一个陌生的舞台,见陌生的人,听陌生的歌,看陌生的风景,最后把陌生变为熟悉。在生活的锤炼下,你看到谁和谁分开都不会太奇怪,不管这个世界发生什么,你都会有勇气面对新的一天。

学习分页器时候有很多选择,有插件形式,很简单就能处理;但是在学习 django ,自带的分页器还挺好用,但是在做用户展现时也碰到了很多的问题,整理出来如下

django 分页器

1.1 paginator 分页

代码位于 django/core/paginator.py 里,这里直接看到文档直接上手

这里没有用户,选在数据库中生成300个测试用户来做展示测试

# python manage.py shell
In [1]from django.contrib.auth.models import User
In [2]: for i in range(1,300):
... User.objects.create_user("testuser{}".format(i), "test{}@test.com".format(i), '123456')

下面开始进行分页展示

In [2]: from django.core.paginator import Paginator

In [3]: userlist = User.objects.all()

#每10个对象分为1页
In [4]: pages = Paginator(userlist, 10)

#得到里面总共有多少个模型对象
In [5]: print pages.count
300

#总页数
In [6]: print pages.num_pages
30

#页面列表,可以拿来遍历得到全部页码
In [7]: print pages.page_range
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]

#得到第1页包含的模型对象,该对象集合可以用于遍历得到里面的模型对象
In [8]: user_page = pages.page(1)

#得到该对象集合当前是哪一页
In [9]: print user_page.number
1

In [10]: print user_page.has_previous
<bound method Page.has_previous of <Page 1 of 30>>

#判断是否有下一页
In [11]: print user_page.has_next()
True

#判断是否有前一页
In [12]: print user_page.has_previous()
False

上面很直观的看到了一些 paginator 的用法,常规的值需要了解以下几个方法即可

  • Paginator 对象方法
  • page 对象方法

1.2 分页进阶

貌似看分页是处理好了,能正常显示了,但有个问题,当展示内容越来越多的时候,分页导航栏也会越来越多,所以完善的一个分页应该包含如下

  • 用户在哪一页,则当前页号高亮以提示用户所在位置,比如上图显示用户正处在第二页。
  • 当用户所处的位置还有上一页时,显示上一页按钮;当还有下一页时,显示下一页按钮,否则不显示。
  • 当分页较多时,总是显示当前页及其前几页和后几页的页码,其他页码不显示。

所以处理逻辑变成了如下

  • 首页、末页、上一页,下一页都循环最原始从数据库中取出的 list 来循环获取数据
  • 而最终展示在用户面前的这个循环列表是处理过的,长度是指定长度的 list,这样就能完成我们要的分页展示

最终代码成了如下,只列出了分页的代码

APP/views.py

class UserListView(ListView):

template_name = "user/userlist.html"
model = User
paginate_by = 10 # 每页展示多少对象
before_index = 4 # 当前页往前几页
after_index = 3 # 当前页往后几页
# context_object_name = "userlist"

def get_context_data(self, **kwargs):
context = super(UserListView, self).get_context_data(**kwargs)

# 重写父类方法,并覆盖父类的 context 属性
context['page_range'] = self.get_page_range(context['page_obj'])
return context

def get_page_range(self, page_obj):
"""分页处理逻辑"""

# 开始序列号 = 当前页码号 - 后置序列号
start_index = page_obj.number - self.before_index
# 应对开始序列异常的情况,最小不能为 0
if start_index < 0:
start_index = 0

# 最终在页面上供展示的循环的 list
page_range = page_obj.paginator.page_range[start_index: page_obj.number + self.after_index]
return page_range

HTML 只贴出分页部分

    <div class="panel-default">
<center>
<ul class="pagination">
{% if page_obj %}
<li><a href="/user/list/?page=1">首页</a></li>
{% endif %}
{# 如果有上一页则显示上一页标签 #}
{% if page_obj.has_previous %}
<li><a href="/user/list/?page={{ page_obj.previous_page_number }}">上一页</a></li>
{% endif %}

{# {% for p in page_obj.paginator.page_range %}#}
{% for p in page_range %}
<li {% if page_obj.number == p %} class="active" {% endif %}><a href="/user/list/?page={{ p }}">{{ p }}</a></li>
{% endfor %}

{# 如果有下一页则显示下一页标签 #}
{% if page_obj.has_next %}
<li><a href="/user/list/?page={{ page_obj.next_page_number }}">下一页</a></li>
{% endif %}

{% if page_obj %}
<li><a href="/user/list/?page={{ page_obj.paginator.num_pages }}">末页</a></li>
{% endif %}
</ul>
</center>
</div>

这里注意两个对象 page_objpage_range

  • page_obj 是原始的从 User.object.all() 取出的全部 list 数据
  • page_range 是上面的分页逻辑处理后给页面进行循环的 list 对象

如果还需要更多的处理逻辑都可以在 get_page_range 方法里进行添加,这里只实现了我需要的基本分页逻辑。结果就不上图了,效果如上面的需求。

  

参考阅读