django基础之分页器
一:分页器
1.1 分页器基本使用
class BooksView(View):
def get(self, request):
# book_list = []
# for i in range(1,101):
# book_obj = Book(title = 'book_%s' % i, price = random.randint(30, 200))
# book_list.append(book_obj)
# Book.objects.bulk_create(book_list)
book_list = Book.objects.all()
paginator = Paginator(book_list, 10) # 实例化分页器对象,参数10为每页显示多少条数据
print('count: ', paginator.count) # 数据总数
print('num_pages: ', paginator.num_pages) # 分页的总页数
print('page_range: ', paginator.page_range) # 页数的范围列表
print('first page content: ', paginator.get_page(1)) # 第一页的所有数据,参数为2即为第二页的所有数据
page2 = paginator.get_page(2)
print(page2.has_next()) # 是否有下一页
print(page2.next_page_number()) # 下一页的页码
print(page2.has_previous()) # 是否有上一页
print(page2.previous_page_number()) # 上一页的页码
return render(request, 'books.html', {'book_list': book_list})
1.2 分页导航实现
类视图:
class BooksView(View):
def get(self, request):
book_list = Book.objects.all()
paginator = Paginator(book_list, 5)
current_page = int(request.GET.get('page', 1))
page = paginator.get_page(current_page)
if paginator.num_pages > 11: # 假设页面最多显示11个页码
if current_page -5 < 1:
page_range = range(1, 12)
elif current_page + 5 > paginator.num_pages:
page_range = range(paginator.num_pages - 10, paginator.num_pages)
else:
page_range = range(current_page - 5, current_page + 6)
else:
page_range = paginator.page_range
return render(request, 'books.html', {
# 'book_list': book_list
'page': page,
'paginator': paginator,
'current_page': current_page,
'page_range': page_range
})
HTML页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1>Books List</h1>
<ul>
{% for book in page %}
<li>{{ book.title }} -- {{ book.price }}</li>
{% endfor %}
</ul>
<nav aria-label="Page navigation">
<ul class="pagination">
<li><a href="?page=1">首页</a></li>
{% if page.has_previous %}
<li>
<a href="?page={{ page.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% else %}
<li class="disabled">
<a href="javascript:void(0);" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
{% for num in page_range %}
{% if num == current_page %}
<li class="active"><a href="?page={{ num }}">{{ num }}</a></li>
{% else %}
<li><a href="?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %}
{% if page.has_next %}
<li>
<a href="?page={{ page.next_page_number }}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% else %}
<li class="disabled">
<a href="javascript:void(0);" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
<li><a href="?page={{ paginator.num_pages }}">尾页</a></li>
</ul>
</nav>
</body>
</html>
显示效果:
1.3 自定义分页器类
class Paginator:
def __init__(self, request, current_page, all_count, per_page=10, max_page_num=13):
"""
封装分页相关数据
:param current_page: 当前页码
:param all_count: 数据库中的数据总条数
:param per_page: 每个页面显示的数据条数
:param max_page_num: 最多显示的页码个数
:param num_pages: 通过总条数/每个页面显示的条数,求出总页数
"""
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page
self.all_count = all_count
self.per_page = per_page
# 计算总页数
num_pages, temp = divmod(all_count, per_page)
if temp:
num_pages += 1
self.num_pages = num_pages
self.max_page_num = max_page_num # 11
self.page_count_half = int((self.max_page_num - 1) / 2) # 5
import copy
self.url_args = copy.deepcopy(request.GET)
print(self.url_args.urlencode())
"""
self.num_pages=100
per_page=8
current_page =1 [0:8]
current_page =2 [8:16]
current_page =3 [16:24]
[(current_page-1)*per_page:current_page*per_page ]
"""
@property
def start(self):
return (self.current_page - 1) * self.per_page
@property
def end(self):
return self.current_page * self.per_page
def page_html(self):
# 如果总页数小于self.max_page_num(最多显示的页码个数)
if self.num_pages <= self.max_page_num:
page_start = 1
page_end = self.num_pages + 1
else:
# 如果当前页码<=页面上最多显示11/2个页码时
if self.current_page <= self.page_count_half:
page_start = 1
page_end = self.max_page_num + 1
# 如果当前页码+最多显示11/2 大于 总页数时
elif self.current_page + self.page_count_half > self.num_pages:
page_start = self.num_pages - self.max_page_num + 1
page_end = self.num_pages + 1
else:
page_start = self.current_page - self.page_count_half
page_end = self.current_page + self.page_count_half + 1
page_html_list = []
# 首页
self.url_args['page'] = 1
first_page = '<nav aria-label="Page navigation"><ul class="pagination"><li><a href="?%s">首页</a></li>' % (self.url_args.urlencode())
page_html_list.append(first_page)
# 上一页
if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="javascript:void(0);">上一页</a></li>'
else:
self.url_args['page'] = self.current_page - 1
prev_page = '<li><a href="?%s">上一页</a></li>' % (self.url_args.urlencode(), )
page_html_list.append(prev_page)
# 显示页码
for i in range(page_start, page_end):
self.url_args['page'] = i
if self.current_page == i:
temp = '<li class="active"><a href="?%s">%s</a></li>' % (self.url_args.urlencode(), i)
else:
temp = '<li><a href="?%s">%s</a></li>' % (self.url_args.urlencode(), i)
page_html_list.append(temp)
# 下一页
if self.current_page >= self.num_pages:
next_page = '<li class="disabled"><a href="javascript:void(0);">下一页</a></li>'
else:
self.url_args['page'] = self.current_page + 1
next_page = '<li><a href="?%s">下一页</a></li>' % (self.url_args.urlencode(), )
page_html_list.append(next_page)
# 尾页
self.url_args['page'] = self.num_pages
last_page = '<li><a href="?%s">尾页</a></li></ul></nav>' % self.url_args.urlencode()
page_html_list.append(last_page)
return "".join(page_html_list)
视图层使用:
class Index(View):
def get(self, request):
book_list = models.Book.objects.all()
current_page = request.GET.get('page')
paginator = Paginator(request, current_page, book_list.count(), 6, 11)
book_list = book_list[paginator.start: paginator.end]
return render(request, "index2.html", {"book_list": book_list, "paginator": paginator, "current_page": current_page})
二:ListView视图
网站开发中,经常会出现需要列出某个列表中的数据作为列表展示出来。在Django中可以使用内置的ListView来快速实现分页的需求
# models.py
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
create_time = models.DateTimeField(auto_now_add = True)
批量生测试数据视图函数:
def articles(request):
article_list = []
for x in range(102):
article = Article(title = 'title:%s' %x, content = 'content: %s' %x)
article_list.append(article)
Article.objects.bulk_create(article_list) # 效率比单独的for循环要高
return HttpResponse('OK')
数据展示视图类:
class ArticleListView(ListView):
model = Article # 指定这个列表是给哪个模型类
template_name = 'article_list.html' # 指定使用的模板文件
context_object_name = 'articles' # 模型类展示到模板中上下文的名字
paginate_by = 10 # 每页显示多少条数据
ordering = 'create_time' # 按模型类中的哪个字段进行排序
page_kwarg = 'p' # 分页请求参数,默认为:page
# 以下两个方法为重写父类的方法
def get_context_data(self, **kwargs):
'''用于给模板传递上下文 '''
context = super(ArticleListView, self).get_context_data(**kwargs)
context['username'] = 'ginvip'
print(context)
return context
def get_queryset(self):
'''默认展示模型类中的所有数据,如需对展示的数据进行二次处理,在该方法中实现'''
return Article.objects.filter(id__lte = 12) # 这里只展示id小于等于12的数据
数据展示效果如下图:
三:Paginator和Page类
Paginator和Page类都是用来做分页的。他们在Django中的路径为:django.core.paginator.Paginator与django.core.paginator.Page
扫描二维码关注公众号,回复:
9151453 查看本文章
3.1 Paginator常用属性和方法
count:总共有多少条数据
num_pages:总共有多少页
page_range:页面的区间,如有三页数据,那么就是range(1,4)
3.2 Page常用属性和方法
has_next:是否还有下一页
has_previous:是否还有上一页
next_page_number:下一页的页码
privious_page_number:上一页的页码
number:当前页
start_index:当前这一页的第一条数据的索引值
end_index:当前这一页的最后一条数据的索引值
可以在get_context_data方法中打印输出上面两个小节的各个参数
def get_context_data(self, **kwargs):
'''用于给模板传递上下文'''
context = super(ArticleListView, self).get_context_data(**kwargs)
paginator = context.get('paginator')
page = context.get('page_obj')
print(paginator.__dict__)
print(page.number)
return context
四:分页导航的实现
这里使用bootstrap来实现分页导航
分页导航样式见:https://v3.bootcss.com/components/#pagination
模板文件中引入下面一行代码即可:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
分页视图类:
class ArticleListView(ListView):
model = Article # 指定这个列表是给哪个模型类
template_name = 'article_list.html' # 指定使用的模板文件
context_object_name = 'articles' # 模型类展示到模板中上下文的名字
paginate_by = 10 # 每页显示多少条数据
ordering = 'create_time' # 按模型类中的哪个字段进行排序
page_kwarg = 'p' # 分页请求参数,默认为:page
def get_context_data(self, **kwargs):
'''用于给模板传递上下文'''
context = super(ArticleListView, self).get_context_data(**kwargs)
return context
# 为了实现分页效果,这里展示所有数据,所以注释掉了get_queryset(self)方法
视图模板文件内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
{% for foo in articles %}
<li>{{ foo.title }}</li>
{% endfor %}
<ul class="pagination">
{# 判断是否有上一页 #}
{% if page_obj.has_previous %}
<li><a href="{% url 'book:list' %}?p={{ page_obj.previous_page_number }}">上一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">上一页</a></li>
{% endif %}
{# 中间的页码 #}
{% for page in paginator.page_range %}
{% if page == page_obj.number %}
<li class="active"><a href="javascript:void(0);">{{ page }}</a></li>
{% else %}
<li><a href="{% url 'book:list' %}?p={{ page }}">{{ page }}</a></li>
{% endif %}
{% endfor %}
{# 下一页 #}
{% if page_obj.has_next %}
<li><a href="{% url 'book:list' %}?p={{ page_obj.next_page_number }}">下一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">下一页</a></li>
{% endif %}
</ul>
</ul>
</body>
</html>
分页导航展示效果如下:
基本实现了分页导航的效果,但是有一个问题,就是分页区间的展示是展示了所有的页码
分页视图类:
class ArticleListView(ListView):
model = Article # 指定这个列表是给哪个模型类
template_name = 'pagination.html' # 指定使用的模板文件
context_object_name = 'articles' # 模型类展示到模板中上下文的名字
paginate_by = 10 # 每页显示多少条数据
ordering = 'create_time' # 按模型类中的哪个字段进行排序
page_kwarg = 'p' # 分页请求参数,默认为:page
def get_context_data(self, **kwargs):
'''用于给模板传递上下文'''
context = super(ArticleListView, self).get_context_data(**kwargs)
paginator = context.get('paginator')
page_obj = context.get('page_obj')
pagination_data = self.get_pagination_data(paginator, page_obj, 2)
context.update(pagination_data)
return context
def get_pagination_data(self, paginator, page_obj, around_count = 2):
current_page = page_obj.number
total_pages = paginator.num_pages
left_has_more = False
right_has_more = False
if current_page <= around_count + 2:
left_pages = range(1,current_page)
else:
left_has_more = True
left_pages = range(current_page - around_count, current_page)
if current_page >= total_pages - around_count - 1:
right_pages = range(current_page + 1, total_pages + 1)
else:
right_has_more = True
right_pages = range(current_page + 1, current_page + around_count + 1)
return {
'left_pages': left_pages,
'right_pages': right_pages,
'current_page': current_page,
'left_has_more': left_has_more,
'right_has_more': right_has_more,
'total_pages': total_pages
}
模板文件内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
{% for foo in articles %}
<li>{{ foo.title }}</li>
{% endfor %}
<ul class="pagination">
{# 判断是否有上一页 #}
{% if page_obj.has_previous %}
<li><a href="{% url 'book:list' %}?p={{ page_obj.previous_page_number }}">上一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">上一页</a></li>
{% endif %}
{% if left_has_more %}
<li><a href="{% url 'book:list' %}?p=1">1</a></li>
<li><a href="javascript:void(0);">...</a></li>
{% endif %}
{# 左边的页码 #}
{% for left_page in left_pages %}
<li><a href="{% url 'book:list' %}?p={{ left_page }}">{{ left_page }}</a></li>
{% endfor %}
{# 当前的页码 #}
<li class="active"><a href="{% url 'book:list' %}?p={{ current_page }}">{{ current_page }}</a></li>
{# 右边的页码 #}
{% for right_page in right_pages %}
<li><a href="{% url 'book:list' %}?p={{ right_page }}">{{ right_page }}</a></li>
{% endfor %}
{% if right_has_more %}
<li><a href="javascript:void(0);">...</a></li>
<li><a href="{% url 'book:list' %}?p={{ total_pages }}">{{ total_pages }}</a></li>
{% endif %}
{# 下一页 #}
{% if page_obj.has_next %}
<li><a href="{% url 'book:list' %}?p={{ page_obj.next_page_number }}">下一页</a></li>
{% else %}
<li class="disabled"><a href="javascript:void(0);">下一页</a></li>
{% endif %}
</ul>
</ul>
</body>
</html>
最终效果: