1. 批量操作数据
在操作大批数据的时候,例如在数据库表中插入大批量的记录时,可以使用该方法减少运行时间,首先可以尝试直接插入多条数据,如下所示
urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^get_book/',views.get_book)
]
models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=64)
views.py
from django.shortcuts import render, HttpResponse, redirect
from app01 import models
def get_book(request):
# for循环插入1000条数据
for i in range(1000):
models.Book.objects.create(name='第%s本书'%i)
# 将插入的数据再查询出来
book_queryset = models.Book.objcets.all()
return render(request,'get_book.html',locals()) # 将查询出来的数据传递给html页面
template/get_book.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'dist/sweetalert.min.js' %}"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
{#对结果集进行for循环#}
{% for book in book_queryset %}
{#循环展示书名#}
<p>{{ book.title }}</p>
{% endfor %}
</div>
</div>
</div>
</body>
</html>
在插入大批量的时候,浏览器会等待很久,这是因为后端在不停的操作数据库,所以看起来想卡了,直接操作大批量的数据效率并不高,我们可以使用 bulk_create方法提升效率
bulk_create 方法
在 views.py 中使用该方法即可
from django.shortcuts import render, HttpResponse, redirect
from app01 import models
def get_book(request):
'''批量插入'''
book_list = []
for i in range(100000):
# 先用类产生一个对象
source_book_obj = models.Books(title=f'第{i}本书')
# 将对象追加到列表中
book_list.append(source_book_obj)
models.Books.objects.bulk_create(book_list) # 批量插入
book_queryset = models.Books.objects.all()
return render(request,'many_data.html',locals())
2. 分页器推导
在前面批量插入了多条数据,但是肯定不能在页面上同时展示这么多数据,就需要使用分页器的功能了。
1. 首先分页,首先肯定要有展示数据的条数、开始展示的位置、结束展示的位置、分页器页码的数量等参数。
2. 我们可以在路由后携带类似于 ?page=1 的数据来表示当前页码,并对结果集进行切片让其展示固定位置数据。
class sorter(View):
def get(self, request):
# 获取请求中的page值
page_num = request.GET.get('page')
try:
# 将其转为数字类型
page_num = int(page_num)
except Exception:
# 如果不存在将其转为1
page_num = 1
# 对结果集进行切片操作
book_queryset = models.Books.objects.all()[20:30]
3. 虽然可以展示出指定位置的数字,但是此时是写死的,我们需要动态的获得指定的位置,在此我们可以看下页码参数
(page_num)、开始的位置(start_num)和结束的位置(stop_num)以及展示的条数(show_num)之间的关系,如下表所示
页码展示条数 | 页码 | 结果集开始位置 | 结果集结束位置 |
---|---|---|---|
5 | 1 | 0 | 5 |
5 | 2 | 5 | 10 |
5 | 3 | 10 | 15 |
页码展示条数 | 页码 | 结果集开始位置 | 结果集结束位置 |
---|---|---|---|
10 | 1 | 0 | 10 |
10 | 2 | 10 | 20 |
10 | 3 | 20 | 30 |
4. 我们可以发现,结果集开始的位置是页码展示条数与页码减一的乘积,而结果集结束的位置是页码展示条数和页码的乘积
关系如下所示,此时就可以动态的切片结果集了,如下所示
# 关系展示
start_num = show_num * (page_num - 1)
stop_num = show_num * page_num
# 动态切割结果集
class sorter(View):
def get(self, request):
page_num = request.GET.get('page')
try:
page_num = int(page_num)
except Exception:
page_num = 1
# 设置最大展示的条数
show_num = 10
# 动态结果集起始的位置
start_num = (page_num-1) * show_num
# 动态结果集结束的位置
stop_num = page_num * show_num
# 对结果集动态切片
book_queryset = models.Books.objects.all()[start_num:stop_num]
return render(request, 'book.html', locals())
5. 接下来就是引入分页器的样式了,这里使用的是 Bootstrap 框架提供的分页器,修改其链接,如下
<div class="text-center">
{% for foo in book_queryset %}
<p>{{ foo.name }}</p>
{% endfor %}
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li><a href="?page=1">1</a></li>
<li><a href="?page=2">2</a></li>
<li><a href="?page=3">3</a></li>
<li><a href="?page=4">4</a></li>
<li><a href="?page=5">5</a></li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
6. 虽然实现了简易分页,但是其跳转的页码是写死的,我们可以把它放在后端编写,首先要了解页码数量和数据条数之间的
关系,如下表所示,就算数据条数只多出一条,也要一个页码储存。在后端使用的方法是 'divmod', divmod() 函数把除
数和余数运算结果结合起来,返回一个包含商和余数的元组,如下示意
# divmod 内置方法使用示意
>>> divmod(100,10)
(10, 0) # 10页
>>> divmod(101,10)
(10, 1) # 11页
>>> divmod(99,10)
(9, 9) # 10页
# 余数只要不是0就需要在第一个数字上加一
数据条数 | 页码最大展示条数 | 页码数 |
---|---|---|
99 | 10 | 10 |
100 | 10 | 10 |
101 | 10 | 11 |
7. 在后端编写html代码发生到前端,如下所示
'views.py文件'
class sorter(View):
def get(self, request):
page_num = request.GET.get('page')
# 获取当前结果集
data_queryset = models.Books.objects.all()
try:
page_num = int(page_num)
except Exception:
page_num = 1
show_num = 10
start_num = (page_num - 1) * show_num
stop_num = page_num * show_num
# 获取结果集数据的个数
all_count = data_queryset.count()
# 使用divmod方法
more, others = divmod(all_count, page_num)
# 如果有余数,页码数量加1
if others:
more += 1
# 创建空字符串用于接收html代码
html = ''
# for循环,循环生成html代码
for i in range(1, more+1):
# 添加到空字符串中
html += f'<li><a href="?page={i}">{i}</a></li>'
# 切片
book_queryset = data_queryset[start_num:stop_num]
return render(request, 'book.html', locals())
'book.html文件'
<div class="text-center">
{% for foo in book_queryset %}
<p>{{ foo.name }}</p>
{% endfor %}
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
# 使用后端传入的变量,因为是HTML代码,需要加上取消转义过滤器
{{ html|safe }}
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
8. 页码已经有了功能,但是现在是一次性将页码都展示,我们需要对其最终优化,如下
class sorter(View):
def get(self, request):
# 获取当前页码page值
page_num = request.GET.get('page')
# 获取结果集
data_queryset = models.Books.objects.all()
try:
# 将其转为数字类型
page_num = int(page_num)
except Exception:
# 异常时赋值为1
page_num = 1
# 定义一个页码展示的最大条数
show_num = 10
# 动态获取结果集切片开始的位置
start_num = (page_num - 1) * show_num
# 动态获取结果集切片结束的位置
stop_num = page_num * show_num
# 获取结果集数据的总条数
all_count = data_queryset.count()
# 获取整除数和余数,整除的数量就是页码的总数量,余数是多出来的数据条数
more, others = divmod(all_count, show_num)
if others:
# 如果有余数,需要在添加一页存放数据
more += 1
# 创建一个变量指向当前页码
more_page = page_num
# 若当前页码小于等于6,range(1, 12)。
if more_page < 6:
more_page = 6
# 假设最大页码数是27,若当前页码大于22时,例如为23,则range(17,28)。页码为25也为range(17,28)
if more_page > (more - 5):
more_page = more - 5
# 创建空字符串
html = ''
# 这里设定只展示11个页码,不小于6和不大于more - 5的页码按照其所在位置展示前后5个页码
for i in range(more_page - 5, more_page + 6):
# 将当前页码添加高亮
if i == page_num:
html += f'<li class="active"><a href="?page={i}">{i}</a></li>'
else:
html += f'<li><a href="?page={i}">{i}</a></li>'
# 动态切片结果集
book_queryset = data_queryset[start_num:stop_num]
return render(request, 'book.html', locals())
到这里简单的分页器功能就实现了,实际使用可以使用下面的模板
分页器最终版本
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param pager_count: 最多显示的页码个数
"""
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_num = per_page_num
# 总页码
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager
self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2)
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
def end(self):
return self.current_page * self.per_page_num
def page_html(self):
# 如果总页码 < 11个:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 总页码 > 11
else:
# 当前页如果<=页面上最多显示11/2个页码
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
# 当前页大于5
else:
# 页码翻到最后
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1
page_html_list = []
# 添加前面的nav和ul标签
page_html_list.append('''
<nav aria-label='Page navigation>'
<ul class='pagination'>
''')
first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
else:
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一页</a></li>'
else:
next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加标签
page_html_list.append('''
</nav>
</ul>
''')
return ''.join(page_html_list)
最终版本使用
由于分页器会多次使用,我们可以创建一个utils文件夹用于存放分页器,将上面的类放到utils文件夹下的py文件中
后端
def get_book(request):
# 产生结果集对象
book_queryset = models.Book.objects.all()
# 获取page值
current_page = request.GET.get("page",1)
# 统计结果集个数
all_count = book_queryset.count()
# 实例化类,其中 page值、结果集个数是必传的参数,其他参数有设置默认值
page_obj = Pagination(current_page=current_page,all_count=all_count,per_page_num=10)
# 动态切片结果集
page_queryset = book_queryset[page_obj.start:page_obj.end]
return render(request,'book.html',locals())
前端
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
{% for book in page_queryset %}
<p>{{ book.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}
</div>
</div>
</div>