当我们需要大批量创建数据的时候,如果一条一条的去创建或许需要猴年马月 我们可以先试一试for循环试试
我们首先建立一个模型类来创建一个表
models.py;建议连接到MySQL数据库下进行实验
class Book(models.Model):
name = models.CharField(max_length=32)
'记得创建好模型类后,执行数据库迁移命令:makemigrations、migrate'
配置路由文件urls.py
from django.conf.urls import url
from django.contrib import admin
from app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^data/', views.data),
]
'html代码'
<div class="col-md-8 col-md-offset-2">
{% for book_obj in book_query %}
<p class="text-center" style="font-size:20px;">{{ book_obj.name }}</p>
{% endfor %}
</div>
'views.py'
from app import models
def data(request):
for i in range(10000):
models.Book.objects.create(name=f"第{i}本书")
book_query = models.Book.objects.all()
return render(request,'data.html',locals())
'''
浏览器访问一个django路由,立刻创建1万条数据并展示到前端页面
涉及到大批量数据的创建,直接使用create可能会造成数据库崩溃
所以django就有一个专门来创建的参数就是 dulk_create还有dulk_update 效率极高
'''
从最后批量插入数据的结果来看,效率太慢了。
原因就是每一次插入一条数据时,都需要和数据库做链接,因此创建效率就特别慢。
所以我们就用到了django专门用来批量创建数据的参数dulk_create
from app import models
def data(request):
book_list = []
for i in range(10000):
book_obj = models.Book(name=f"第{i}本书")
book_list.append(book_obj)
'''上述代码可以简化为一行'''
[models.Book(name=f"第{i}本书") for i in range(10000)]
models.Book.objects.bulk_create(book_list) # 批量创建
# models.Book.objects.bulk_update(book_list) # 批量更新
book_query = models.Book.objects.all()
return render(request,'data.html',locals())
从浏览器展示中可以看到,使用了
bulk_create
批量创建插入数据时,极快的创建完成后并在同一个页面展示出来了。但是这样展示出来的效果不够美观,所以我们需要把展示的数据进行分页展示
这里我们将利用Django提供给我们的一个分页模块来帮助我们的页面达到一个分页的效果
1.get请求也是可以携带参数的,所以我们在朝后端发送查看数据的同时可以携带一个参数告诉后端我们想看第几页的数据
current_page = request.GET.get('page',1) # 获取用户想访问的页码,如果没有,默认展示第一页
try: # 由于后端接收到的前端数据是字符串类型所以我们这里做类型转换处理加异常捕捉
current_page = int(current_page)
except Exception as f:
current_page = 1
# 还需要定义页面到底展示几条数据
per_page_num = 10 # 一页展示10条数据
# 需要对总数据进行切片操作,需要确定切片起始位置和终止位置
start_page = (current_page-1)*per_page_num
end_page = current_page*per_page_num
'''
下面需要研究current_page、per_page_num、start_page、end_page四个参数之间的数据关系
per_page_num = 10
current_page start_page end_page
1 0 10
2 10 20
3 20 30
4 30 40
per_page_num = 5
current_page start_page end_page
1 0 5
2 5 10
3 10 15
4 15 20
可以很明显的看出规律
start_page = (current_page-1)*per_page_num
end_page = current_page*per_page_num
'''
2.数据总页面获取
内置方法之divmod
divmod(100,10)
#(10,0) 10页
divmod(101,10)
#(10,1) 11页
divmod(99,10)
#(9,9) 10页
'''余数只要不是0就需要在第一个数字上加一'''
总共需要多少页数
info_queryset = models.UserInfo.objects.all()
all_count = info_queryset.count() # 数据总条数
all_pager,more = divmod(all_count,per_page_num)
if more: # 有余数则总页数加一
all_pager += 1
3.利用start_page和end_page对总数据进行切片取值再传入前端页面就能够实现分页展示
后端
info_list = models.UesrInfo.objects.all()[start_page:end_page]
return render(request,'info.html',locals())
前端
{% for info in info_list %}
<p>{{ info.name }}</p>
{% endfor %}
4.由于模版语法没有range功能,但是我们需要循环产生很多分页标签,所以考虑后端生成传递给前端页面
后端
html_srt = ''
xxx = current_page
if xxx<6:
xxx = 6
for i in range(xxx - 5,xxx+6)
if current_page == i:
html_str += ' %s ' %(i,i)
else:
html_str += 'href="?page=%s">%s ' % (i,i)
前端
{{ html_str }}
分页器主要是处理逻辑,代码最后很简单
推导流程:
1.queryset支持切片操作(正数)
2.分页样式添加
3.页码展示
如果根据总数据和每页展示的数据得出总页码
divmod()
4.如何渲染出所有的页码标签
前端模版语法不支持range 但是后端支持,我们可以在后端创建好html标签然后传递给html页面使用
5.如何限制住展示的页面标签个数
页码推荐使用奇数位(对称美),利用当前页前后固定位置来限制
6.首尾页码展示范围问题
上述是分页器组件的推导流程,我们无需真正编写
django自带一个分页器组件,但是不是很好用,所以我们也可以自己自定义一个分页器
实验环境搭建
我们首先建立一个模型类来存储数据,因为要实现分页需要一定的数据量。
models.py;建议连接到MySQL数据库下进行实验
class UserInfo(models.Model):
name = models.CharField(max_length=32)
age = models.CharField(max_length=32)
address = models.CharField(max_length=32)
'记得创建好模型类后,执行数据库迁移命令:makemigrations、migrate'
配置路由文件urls.py
from django.conf.urls import url
from django.contrib import admin
from app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^info/', views.info),
]
tests.py 制作我们待会进行分页的数据,并将其写入数据库内
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "nine.settings")
import django
django.setup()
from app import models
from faker import Faker # 生成加的数据
import random # 用来创建随机年龄
obj_list = []
for i in range(300): # 制作300条虚拟数据
fake = Faker(locale='zh-CN') # 选择地区
name = fake.name() # 生成随机名称
age = random.randint(18,55) # 生成随机年龄
address = fake.address() # 生成随机地址
info_obj = models.UserInfo(name=name,age=age,address=address) # 生成一个个用户对象
obj_list.append(info_obj)
models.UserInfo.objects.bulk_create(obj_list) # 将一个个对象写入数据库内保存,一个对象对应一条记录
这里用到了一个Python第三方模块
faker
,主要是生成各种各样的伪数据。具体使用自行搜索引擎查找。
views.py:先简单的搭建出模型,在web能看到数据效果
def info(request):
info_list = models.UserInfo.objects.all() # 获取所有的数据
all_count = info_list.count() # 通过计数获取有多少条数据
per_page_num = 10 # 自定义每一页展示的数据条数,这个时候就可以动态计算了
all_page_num,more = divmod(all_count,per_page_num) # divmod计算需要多少页来展示
if more:
all_page_num += 1 # 这样就获取到了所有的页码的数量
current_page = request.GET.get('page',1) #获取用户指定的page,如果没有则默认展示第一页
try:
current_page = int(current_page) # 因为返回给前端的是字符串要转换成整型
except ValueError: # 抛出异常类型错误
current_page = 1
html_str = ''
xxx = current_page
if current_page < 6: # 一用户访问的页码小于6就等于6
xxx = 6
for i in range(xxx-5,xxx+6):
if current_page == i:
html_str += '%s ' % (i,i)
else:
html_str += '%s ' % (i,i)
start_page = (current_page-1)*per_page_num # 定义出切片的起始位置
end_page = current_page*per_page_num # 定义出切片的终止位置
info_query = models.UserInfo.objects.all()[start_page:end_page]
'''
all返回的是QuerySet,可以看成列表套对象,也就是说支持索引取值
现在的问题总可能让客户用源码修改页数吧?
'''
return render(request, 'info.html', locals())
info.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table table-striped">
<thead>
<tr>
<th class="text-center">ID</th>
<th class="text-center">姓名</th>
<th class="text-center">年龄</th>
<th class="text-center">住址</th>
</tr>
</thead>
<tbody>
{% for info in info_query %}
<tr class="text-center">
<td>{{ info.id }}</td>
<td>{{ info.name }}</td>
<td>{{ info.age }}</td>
<td>{{ info.address }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<nav aria-label="Page navigation" class="text-center">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{{ html_str | safe }}
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</body>
</html>
是我们自己写好的分页器代码
一般是存在应用层.utils.mypage文件下的,我们需要的时候导入就行
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('''
)
first_page = '首页 ' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '上一页 '
else:
prev_page = '上一页 ' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '%s ' % (i, i,)
else:
temp = '%s ' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '下一页 '
else:
next_page = '下一页 ' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '尾页 ' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加标签
page_html_list.append('''
''')
return ''.join(page_html_list)
自定义分页器的使用
django自带分页器模块但是使用起来很麻烦 所以我们自己封装了一个
只需要掌握使用方式即可
后端
def info(request):
info_list = models.UserInfo.objects.all()
from app.utils.my_page import Pagination # 导入
current_page = request.GET.get('page') # 获取当前页
page_obj = Pagination(current_page=current_page,all_count=info_list.count(),per_page_num=10) # 生成对象
info_query = info_list[page_obj.start:page_obj.end] # 切片
html = page_obj.page_html
return render(request,'info.html',locals())
前端
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table table-striped">
<thead>
<tr>
<th class="text-center">ID</th>
<th class="text-center">姓名</th>
<th class="text-center">年龄</th>
<th class="text-center">住址</th>
</tr>
</thead>
<tbody>
{% for info in info_query %}
<tr class="text-center">
<td>{{ info.id }}</td>
<td>{{ info.name }}</td>
<td>{{ info.age }}</td>
<td>{{ info.address }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ html | safe }}
</div>
</div>
</div>