Django作为Python Web开发框架的一哥,提供了企业级网站开发所需要的几乎所有功能,其中就包括自带分页功能。利用Django自带的Paginator类,我们可以很轻松地实现分页。Django 2.0和1.X最大的不同在于新增了get_page()方法。我们现在来具体看看有什么不同。
Django 1.X 和Django 2.0下实现分页
利用Django实现分类非常简单,我们只需要修改views.py和模板template。Django 1.X下实现分页代码如下:
# app/views.py - 基于函数的视图
1 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 2 from .models import Article 3 from django.shortcuts import render 4 5 def article_list(request): 6 article_list = Article.objects.all() 7 paginator = Paginator(article_list, 10) # 实例化一个分页对象, 每页显示10个 8 9 page = request.GET.get('page') # 从URL通过get页码,如?page=3 10 try: 11 articles = paginator.page(page) # 获取某页对应的记录 12 except PageNotAnInteger: # 如果页码不是个整数 13 articles = paginator.page(1) # 提取第一页的记录 14 except EmptyPage: # 如果页码太大,没有相应的记录 15 articles = paginator.page(paginator.num_pages) # 提取最后一页的记录 16 17 return render(request, 'article_list.html', {'articles': articles})
你注意到没有?上段代码的try和2个except非常重要,但是看上去有些冗余。Django 2.0下新增了get_page()方法,可以将代码大大简化(如下图所示)。它所实现的功能与上面是一样的。当用户提交的页码不是整数时,提取第一页记录。当用户输入的页码太大时,只提取最后一页记录。
1 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 2 from .models import Article 3 from django.shortcuts import render 4 5 def article_list(request): 6 article_list = Article.objects.all() 7 paginator = Paginator(article_list, 10) # 实例化一个分页对象, 每页显示10个 8 9 page = request.GET.get('page') # 从URL通过get页码,如?page=3 10 articles = paginator.get_page(page) # 获取某页对应的记录, 如果 11 12 return render(request, 'article_list.html', {'articles': articles})
Django 1.X和2.0下的模板是一样的。这里提供两种显示方案。
模板一: 上一页, Page 1 of 3, 下一页
#app/templates/app/article_list.html
1 {% for article in articles %} 2 {{ article.title }} 3 {% endfor %} 4 5 <div class="pagination"> 6 <span class="step-links"> 7 {% if articles.has_previous %} 8 <a href="?page=1">« first</a> 9 <a href="?page={{ articles.previous_page_number }}">previous</a> 10 {% endif %} 11 12 <span class="current"> 13 Page {{ articles.number }} of {{ articles.paginator.num_pages }}. 14 </span> 15 16 {% if articles.has_next %} 17 <a href="?page={{ articles.next_page_number }}">next</a> 18 <a href="?page={{ articles.paginator.num_pages }}">last »</a> 19 {% endif %} 20 </span> 21 </div>
模板二: Page 1, 2, 3, 4, 5, 6, 7, 8, ... (推荐)
#app/templates/app/article_list.html
1 # Pagination style 2 2 {% for article in articles %} 3 {{ article.title }} 4 {% endfor %} 5 <div class="pagination"> 6 <nav> 7 <ul class="pagination"> 8 {% if articles.has_previous %} 9 <li class=""> 10 <a href="?page={{ articles.previous_page_number }}" aria-label="Previous"> 11 <span aria_hidden="true">«</span></a> 12 </li> 13 {% endif %} 14 15 {% for page_num in articles.paginator.page_range %} 16 {% if page_num == articles.number %} 17 <li class="active"><a href="?page={{ page_num }}">{{page_num}}</a> </li> 18 {% else %} 19 <li class=""><a href="?page={{ page_num }}">{{page_num}}</a> </li> 20 {% endif %} 21 {% endfor %} 22 23 {% if articles.has_next %} 24 <li class=""> 25 <a href="?page={{ articles.next_page_number }}" aria-label="Next"> 26 <span aria_hidden="true">»</span></a> 27 </li> 28 {% endif %} 29 </ul> 30 </nav> 31 </div>
Django如何在基于类的视图里使用分页?
上述案例里我们使用了函数试图,很容易定义page对象, 并传递给模板,例如articles。但是如果我们希望使用基于类的视图,我们该如何实现分页呢?其实操作非常简单。我们只需在视图中加入paginate_by = number即可。
from. models import Article from django.views.generic import ListView class ArticleListView(ListView): model = Article # 等于 queryset = models.Article.objects.all() template_name = 'app/article_list.html' # 可选的 context_object_name = "article_list" # 默认context名字 paginate_by = 10 # 每页10项
此时模板article_list.html也需要做出相应调整,如下图所示。Django会先对是否分页is_paginated做个判断,如果有,就会自动生成个page_obj分页对象传递到模板。
1 {% if article_list %} 2 {% for article in article_list %} 3 {{ article.title }} 4 {% endfor %} 5 6 {% if is_paginated %} 7 <ul class="pagination"> 8 {% if page_obj.has_previous %} 9 <li> 10 <span><a href="?page={{ page_obj.previous_page_number }}">Previous</a></span> 11 </li> 12 {% endif %} 13 <li class=""> 14 <span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</span> 15 </li> 16 {% if page_obj.has_next %} 17 <li> 18 <span><a href="?page={{ page_obj.next_page_number }}">Next</a></span> 19 </li> 20 {% endif %} 21 </ul> 22 23 {% else %} 24 <h3>Articles</h3> 25 <p>No article yet</p> 26 {% endif %}
django自定义分页
未封装版:
- 优点:直观
- 缺点:代码乱,不易维护,可拓展性差
1 data = [] 2 3 for i in range(1, 302): 4 tmp = {"id": i, "name": "alex-{}".format(i)} 5 data.append(tmp) 6 7 print(data) 8 9 10 def user_list(request): 11 12 # user_list = data[0:10] 13 # user_list = data[10:20] 14 try: 15 current_page = int(request.GET.get("page")) 16 except Exception as e: 17 current_page = 1 18 19 per_page = 10 20 21 # 数据总条数 22 total_count = len(data) 23 # 总页码 24 total_page, more = divmod(total_count, per_page) 25 if more: 26 total_page += 1 27 28 # 页面最多显示多少个页码 29 max_show = 11 30 half_show = int((max_show-1)/2) 31 32 if current_page <= half_show: 33 show_start = 1 34 show_end = max_show 35 else: 36 if current_page + half_show >= total_page: 37 show_start = total_page - max_show 38 show_end = total_page 39 else: 40 show_start = current_page - half_show 41 show_end = current_page + half_show 42 43 # 数据库中获取数据 44 data_start = (current_page - 1) * per_page 45 data_end = current_page * per_page 46 47 user_list = data[data_start:data_end] 48 49 # 生成页面上显示的页码 50 page_html_list = [] 51 # 加首页 52 first_li = '<li><a href="/user_list/?page=1">首页</a></li>' 53 page_html_list.append(first_li) 54 # 加上一页 55 if current_page == 1: 56 prev_li = '<li><a href="#">上一页</a></li>' 57 else: 58 prev_li = '<li><a href="/user_list/?page={}">上一页</a></li>'.format(current_page - 1) 59 page_html_list.append(prev_li) 60 for i in range(show_start, show_end+1): 61 if i == current_page: 62 li_tag = '<li class="active"><a href="/user_list/?page={0}">{0}</a></li>'.format(i) 63 else: 64 li_tag = '<li><a href="/user_list/?page={0}">{0}</a></li>'.format(i) 65 page_html_list.append(li_tag) 66 67 # 加下一页 68 if current_page == total_page: 69 next_li = '<li><a href="#">下一页</a></li>' 70 else: 71 next_li = '<li><a href="/user_list/?page={}">下一页</a></li>'.format(current_page+1) 72 page_html_list.append(next_li) 73 74 # 加尾页 75 page_end_li = '<li><a href="/user_list/?page={}">尾页</a></li>'.format(total_page) 76 page_html_list.append(page_end_li) 77 78 page_html = "".join(page_html_list) 79 80 return render(request, "user_list.html", {"user_list": user_list, "page_html": page_html})
封装版:
- 优点:易维护、可拓展性强
- 缺点:逻辑相对复杂
1 """ 2 自定义分页组件 3 """ 4 class Pagination(object): 5 6 def __init__(self, data_num, current_page, url_prefix, per_page=10, max_show=11): 7 """ 8 进行初始化. 9 :param data_num: 数据总数 10 :param current_page: 当前页 11 :param url_prefix: 生成的页码的链接前缀 12 :param per_page: 每页显示多少条数据 13 :param max_show: 页面最多显示多少个页码 14 """ 15 self.data_num = data_num 16 self.per_page = per_page 17 self.max_show = max_show 18 self.url_prefix = url_prefix 19 20 # 把页码数算出来 21 self.page_num, more = divmod(data_num, per_page) 22 if more: 23 self.page_num += 1 24 25 try: 26 self.current_page = int(current_page) 27 except Exception as e: 28 self.current_page = 1 29 # 如果URL传过来的页码数是负数 30 if self.current_page <= 0: 31 self.current_page = 1 32 # 如果URL传过来的页码数超过了最大页码数 33 elif self.current_page > self.page_num: 34 self.current_page = self.page_num # 默认展示最后一页 35 36 # 页码数的一半 算出来 37 self.half_show = max_show // 2 38 39 # 页码最左边显示多少 40 if self.current_page - self.half_show <= 1: 41 self.page_start = 1 42 self.page_end = self.max_show 43 elif self.current_page + self.half_show >= self.page_num: # 如果右边越界 44 self.page_end = self.page_num 45 self.page_start = self.page_num - self.max_show 46 else: 47 self.page_start = self.current_page - self.half_show 48 # 页码最右边显示 49 self.page_end = self.current_page + self.half_show 50 51 if self.page_num < self.page_end: 52 self.page_end = self.page_num # 如果总页面小于结束页面,那么结束页面即为总页码 53 54 @property 55 def start(self): 56 # 数据从哪儿开始切 57 return (self.current_page - 1) * self.per_page 58 59 @property 60 def end(self): 61 # 数据切片切到哪儿 62 return self.current_page * self.per_page 63 64 def page_html(self): 65 # 生成页码 66 l = [] 67 # 加一个首页 68 l.append('<li><a href="{}?page=1">首页</a></li>'.format(self.url_prefix)) 69 # 加一个上一页 70 if self.current_page == 1: 71 l.append('<li class="disabled" ><a href="#">«</a></li>'.format(self.current_page)) 72 else: 73 l.append('<li><a href="{}?page={}">«</a></li>'.format(self.url_prefix, self.current_page - 1)) 74 75 76 for i in range(self.page_start, self.page_end + 1): 77 78 if i == self.current_page: 79 tmp = '<li class="active"><a href="{0}?page={1}">{1}</a></li>'.format(self.url_prefix, i) 80 else: 81 tmp = '<li><a href="{0}?page={1}">{1}</a></li>'.format(self.url_prefix, i) 82 l.append(tmp) 83 84 # 加一个下一页 85 if self.current_page == self.page_num: 86 l.append('<li class="disabled"><a href="#">»</a></li>'.format(self.current_page)) 87 else: 88 l.append('<li><a href="{}?page={}">»</a></li>'.format(self.url_prefix, self.current_page + 1)) 89 # 加一个尾页 90 l.append('<li><a href="{}?page={}">尾页</a></li>'.format(self.url_prefix, self.page_num)) 91 return "".join(l)
封装版使用步骤:
- 将上面的一段代码复制到自定义的模块(pt文件)中
- 导入自定义模块
- 在views系统的函数里写入以下代码
def user_list(request): # 实例化一个分页类的对象 obj = Pagination(blogs.count(),page,'blogs.html') # 根据传入的总博客数、页码获取当页需要展示的博客 blog_list = blogs[obj.start:obj.end] # 根据传入的博客数及页码获取生成分页的html格式的页码列表 page_html = pager.page_html() #返回指定页面 return render(request, "blog_list.html", {"blog_list": blog_list, "page_html": page_html})
- 在需要生成页面的html页面写入以下代码
<div class="topicListFooter fenye"> <ul class="pagination"> {{ page_html|safe }} </ul> </div>
注:示例中用的是bootstrap分页样式
Django中的内置分页(不能展示页码列表)
1 from django.shortcuts import render 2 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 3 4 L = [] 5 for i in range(999): 6 L.append(i) 7 8 def index(request): 9 current_page = request.GET.get('p') 10 11 paginator = Paginator(L, 10) 12 # per_page: 每页显示条目数量 13 # count: 数据总个数 14 # num_pages:总页数 15 # page_range:总页数的索引范围,如: (1,10),(1,200) 16 # page: page对象 17 try: 18 posts = paginator.page(current_page) 19 # has_next 是否有下一页 20 # next_page_number 下一页页码 21 # has_previous 是否有上一页 22 # previous_page_number 上一页页码 23 # object_list 分页之后的数据列表 24 # number 当前页 25 # paginator paginator对象 26 except PageNotAnInteger: 27 posts = paginator.page(1) 28 except EmptyPage: 29 posts = paginator.page(paginator.num_pages) 30 return render(request, 'index.html', {'posts': posts})
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <ul> 9 {% for item in posts %} 10 <li>{{ item }}</li> 11 {% endfor %} 12 </ul> 13 14 <div class="pagination"> 15 <span class="step-links"> 16 {% if posts.has_previous %} 17 <a href="?p={{ posts.previous_page_number }}">Previous</a> 18 {% endif %} 19 <span class="current"> 20 Page {{ posts.number }} of {{ posts.paginator.num_pages }}. 21 </span> 22 {% if posts.has_next %} 23 <a href="?p={{ posts.next_page_number }}">Next</a> 24 {% endif %} 25 </span> 26 27 </div> 28 </body> 29 </html>