一、分页器简介
分页器主要是为了解决大规模数据展示的问题。试想一下,如果数据库中有成千上万条数据,一下子全部在页面上展示出来的话,对于用户的体验肯定是不好的。我们可以将数据分批展示,例如每一页就展示十条数据,让用户通过页码去控制。Django 中也专门提供了这种分页的机制。
二、分页器的简单使用
**第一步:**创建 Book 表,用于存放书籍名字和价格等信息。
1 2 3 4 5 6 7 8 9 10
|
from django.db import models
class Book(models.Model): bid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) price = models.CharField(max_length=32)
|
**第二步:**随机插入多条数据
1 2 3 4 5 6 7 8 9 10
| from app01.models import Book
def insert_data(): book_list = list() for i in range(100): book_obj = Book(title=f"book_{i+1}", price=str((i+1)**2)) book_list.append(book_obj)
Book.objects.bulk_create(book_list)
|
这里不推荐使用 for 循环的方式去插入,那样每插入一条都需要连接和断开数据库,比较消耗性能。建议先将数据全都生成好再批量的插入。
**第三步:**使用分页器
1 2 3 4 5 6 7 8
|
from django.conf.urls import url from app01 import views
urlpatterns = [ url(r'^index/', views.index), ]
|
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
|
from django.shortcuts import render, HttpResponse from app01.models import Book from django.core.paginator import EmptyPage, Paginator
def index(request): book_list = Book.objects.all()
paginator = Paginator(book_list, 10) print(paginator.count) print(paginator.num_pages) print(paginator.page_range)
page_content = paginator.page(1)
content_list = page_content.object_list print(content_list) for i in page_content: print(i) return HttpResponse("ok")
|
三、配合前端显示数据
3.1 初始版本
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
|
from django.shortcuts import render, HttpResponse from app01.models import Book from django.core.paginator import EmptyPage, Paginator
def index(request): current_page_num = int(request.GET.get("page", 1))
book_list = Book.objects.all()
paginator = Paginator(book_list, 10) print(paginator.count) print(paginator.num_pages) print(paginator.page_range)
page_content = paginator.page(current_page_num)
return render(request, "index.html", locals())
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for i in page_content %} <li>{{ i.title }}:{{ i.price }}</li> {% endfor %} </ul>
</body> </html>
|
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
| 浏览器访问 http://127.0.0.1:8000/index/ book_1:1 book_2:4 book_3:9 book_4:16 book_5:25 book_6:36 book_7:49 book_8:64 book_9:81 book_10:100
浏览器访问 http://127.0.0.1:8000/index/?page=2 book_11:121 book_12:144 book_13:169 book_14:196 book_15:225 book_16:256 book_17:289 book_18:324 book_19:361 book_20:400
|
但是当访问的page的值为 -1 或者超过页码的最大值时,会出现错误,我们还需要对后台逻辑进行一个异常的处理。
3.2 处理页码不在合理范围内的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
from django.shortcuts import render, HttpResponse from app01.models import Book from django.core.paginator import EmptyPage, Paginator
def index(request): current_page_num = int(request.GET.get("page", 1))
book_list = Book.objects.all()
paginator = Paginator(book_list, 10) print(paginator.count) print(paginator.num_pages) print(paginator.page_range)
try: page_content = paginator.page(current_page_num) except EmptyPage as e: page_content = paginator.page(1)
return render(request, "index.html", locals())
|
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
|
浏览器访问 http://127.0.0.1:8000/index/?page=-1 book_1:1 book_2:4 book_3:9 book_4:16 book_5:25 book_6:36 book_7:49 book_8:64 book_9:81 book_10:100
浏览器访问 http://127.0.0.1:8000/index/?page=11 book_1:1 book_2:4 book_3:9 book_4:16 book_5:25 book_6:36 book_7:49 book_8:64 book_9:81 book_10:100
|
3.3 配合bootstrap在页面展示页码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
from django.shortcuts import render, HttpResponse from app01.models import Book from django.core.paginator import EmptyPage, Paginator
def index(request): current_page_num = int(request.GET.get("page", 1))
book_list = Book.objects.all()
paginator = Paginator(book_list, 10) print(paginator.count) print(paginator.num_pages) print(paginator.page_range)
try: page_content = paginator.page(current_page_num) except EmptyPage as e: page_content = paginator.page(1)
return render(request, "index.html", locals())
|
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <ul> {% for i in page_content %} <li>{{ i.title }}:{{ i.price }}</li> {% endfor %} </ul>
<nav aria-label="Page navigation"> <ul class="pagination"> <li> <a href="#" aria-label="Previous"> <span aria-hidden="true">上一页</span> </a> </li> {% for item in paginator.page_range %} {% if current_page_num == item %} <li class="active"><a href="?page={{ item }}">{{ item }}</a></li> {% else %} <li><a href="?page={{ item }}">{{ item }}</a></li> {% endif %}
{% endfor %}
<li> <a href="#" aria-label="Next"> <span aria-hidden="true">下一页</span> </a> </li> </ul> </nav>
</body> </html>
|
测试结果展示
当我们每页展示的数据比较少或者数据足够大的时候,就会导致页码变得特别的多,这样就不会特别美观,例如:
因此针对分页的设计,我们还需要进一步处理。
3.4 优化分页设计
3.4.1 实现上一页和下一页的功能
所谓的上一页和下一页,就是在当前页码=的基础上进行加一或者减一的操作,因此我们可以在模板中这么改
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <ul> {% for i in page_content %} <li>{{ i.title }}:{{ i.price }}</li> {% endfor %} </ul>
<nav aria-label="Page navigation"> <ul class="pagination"> <li> <a href="?page={{current_page_num|add:-1 }}" aria-label="Previous"> <span aria-hidden="true">上一页</span> </a> </li> {% for item in paginator.page_range %} {% if current_page_num == item %} <li class="active"><a href="?page={{ item }}">{{ item }}</a></li> {% else %} <li><a href="?page={{ item }}">{{ item }}</a></li> {% endif %}
{% endfor %}
<li> <a href="?page={{ current_page_num|add:1 }}" aria-label="Next"> <span aria-hidden="true">下一页</span> </a> </li> </ul> </nav>
</body> </html>
|
虽然实现了我们想要的功能,但是访问第一页和最后一页时,再点击上一页或者下一页时,url中的page的值就不在合理的范围内了。分页器中还提供了我们查看当前页的上一页或者下一页的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
from django.shortcuts import render, HttpResponse from app01.models import Book from django.core.paginator import EmptyPage, Paginator
def index_test(request): current_page_num = int(request.GET.get("page", 1))
book_list = Book.objects.all()
paginator = Paginator(book_list, 5)
test_page = paginator.page(2)
print(test_page.has_next()) print(test_page.has_previous()) print(test_page.next_page_number()) print(test_page.previous_page_number())
return HttpResponse("OK")
|
我们可以根据这四种方法去对上一页和下一页的功能进行完善。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <ul> {% for i in page_content %} <li>{{ i.title }}:{{ i.price }}</li> {% endfor %} </ul>
<nav aria-label="Page navigation"> <ul class="pagination"> {% if page_content.has_previous %} <li> <a href="?page={{ page_content.previous_page_number }}" aria-label="Previous"> <span aria-hidden="true">上一页</span> </a> </li> {% else %} <li class="disabled"> <a href="" aria-label="Previous"> <span aria-hidden="true">上一页</span> </a> </li> {% endif %}
{% for item in paginator.page_range %} {% if current_page_num == item %} <li class="active"><a href="?page={{ item }}">{{ item }}</a></li> {% else %} <li><a href="?page={{ item }}">{{ item }}</a></li> {% endif %}
{% endfor %}
{% if page_content.has_next %} <li> <a href="?page={{ page_content.next_page_number }}" aria-label="Next"> <span aria-hidden="true">下一页</span> </a> </li> {% else %} <li class="disabled"> <a href="" aria-label="Next"> <span aria-hidden="true">下一页</span> </a> </li> {% endif %}
</ul> </nav>
</body> </html>
|
3.4.2 解决页码显示过多的问题
我们可以规定页面上最多只能出现11个页码,被选中的页码在最中间,左右各有5个相邻的页码,对于其他页码都隐藏
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 31 32
| from django.shortcuts import render, HttpResponse from app01.models import Book from django.core.paginator import EmptyPage, Paginator
def index(request): current_page_num = int(request.GET.get("page", 1)) book_list = Book.objects.all() paginator = Paginator(book_list, 5) if current_page_num - 5 < 1: page_range = range(1, 12) elif current_page_num + 5 > paginator.num_pages: page_range = range(paginator.num_pages - 10, paginator.num_pages + 1) else: page_range = range(current_page_num - 5, current_page_num + 6)
try: page_content = paginator.page(current_page_num) except EmptyPage as e: page_content = paginator.page(1)
return render(request, "index.html", locals())
|
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <ul> {% for i in page_content %} <li>{{ i.title }}:{{ i.price }}</li> {% endfor %} </ul>
<nav aria-label="Page navigation"> <ul class="pagination"> {% if page_content.has_previous %} <li> <a href="?page={{ page_content.previous_page_number }}" aria-label="Previous"> <span aria-hidden="true">上一页</span> </a> </li> {% else %} <li class="disabled"> <a href="" aria-label="Previous"> <span aria-hidden="true">上一页</span> </a> </li> {% endif %} {% for item in page_range %} {% if current_page_num == item %} <li class="active"><a href="?page={{ item }}">{{ item }}</a></li> {% else %} <li><a href="?page={{ item }}">{{ item }}</a></li> {% endif %}
{% endfor %}
{% if page_content.has_next %} <li> <a href="?page={{ page_content.next_page_number }}" aria-label="Next"> <span aria-hidden="true">下一页</span> </a> </li> {% else %} <li class="disabled"> <a href="" aria-label="Next"> <span aria-hidden="true">下一页</span> </a> </li> {% endif %}
</ul> </nav>
</body> </html>
|