15-Django批量插入数据库与分页管理
批量插入数据
关键词: bulk_create(可迭代对象)
- models.模型名去创建数据
- 用一个变量去接接收1的返回值,并加入列表
- xx.objects.bulk_create(数据列表)
from django.shortcuts import render
from faker import Faker
from book import models
# 生成虚假数据
def faker_data() -> dict:
faker = Faker('zh_CN')
data = {}
useful: list = ['name', 'md5', 'company', 'province']
for _ in dir(faker):
# 廖欣 2d59bd0d346e6f8b371a9a17f54e1d7c 数字100传媒有限公司 青海省
# 海象运算符 + 反射
if _ in useful and (content := getattr(faker, _)()):
data[_] = content
else:
return data
def demo(request):
# 插入数据 常规 耗时8分钟
for index in range(1000):
company, password, name, province = faker_data().values()
models.FakerData.objects.create(name=name, password=password, company=company, province=province)
# 插入数据 批量 耗时4分钟(因为还要基于网络请求,后续实测8000不需要包的数据只需要0.32秒
faker_list = []
for index in range(1000):
company, password, name, province = faker_data().values()
faker_obj = models.FakerData(name=name, password=password, company=company, province=province)
faker_list.append(faker_obj)
# 关键的一步,批量插入
models.FakerData.objects.bulk_create(faker_list)
# 读取数据渲染页面
data = models.FakerData.objects.all()
return render(request, 'book/faker.html', locals())
def home(request):
return render(request, 'book/home.html')
分页器
楔子
通过刚刚,我们生成了1000条随机的虚假数据,不过这些数据在前端页面都显示到一页了,用户体验非常不好,所以便来学习分页器的使用。
# 导入模块
from django.core.paginator import Paginator
from django.core.paginator import Paginator
obj = models.FakerData.objects.all()
page = Paginator(obj, 20) # 每页显示多少条, 这里的20指的是显示20条
# <django.core.paginator.Paginator object at 0x0000028534921DB0>
obj_num = page.count # 对象个数,即共有多少数据需要分页 1000
total_num = page.num_pages # 总共几页 50
foo = page.page_range # 分页范围 range(1, 51)
page1 = page.page(1) # 获取第几页 获取第1页数据 <Page 1 of 50>
page1_obj = page1.object_list # 获取第1页的对象 QuerySet 里面的数量就是你上面Paginator(obj, 20)指定的20
has_next = page1.has_next() # 判断是否有下一页,这里需要用page获得的对象去操作 返回布尔值 当前1/50 True
has_previous = page1.has_previous() # 是否有上一页 返回布尔值 当前1/50 False
has_other_pages = page1.has_other_pages() # 是否有其它页 True
next_page_number = page1.next_page_number() # 获取下一页的页码 2
previous_page_number = page1.previous_page_number() # 获取上一页的页面 Traceback 当前就是第一页 所以报错
start_index = page1.start_index() # 从1开始计数的当前页面的第1个对象 1
end_index = page1.end_index() # 从1开始计数的当前页面的最后1个对象 20
p2 = page.page(51) # 访问不存在的页面 报错 Traceback
如何使用
- 获取要展示的对象列表QuerySet
- 将对象列表和每页展示数量传递给Paginator,返回一个分页对象
- 调用该对象的各种方法,获取各种分页信息
- 在前端HTML模板中,使用上面的分页信息构建分页栏
函数使用方式
# 导入必要的模块
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
# 视图层
def demo(request):
queryset = models.FakerData.objects.all()
paginator = Paginator(queryset, 30) # 实例化一个分页对象,每页显示10个
page = request.GET.get('page') # 从URL通过get页面,比如?page=3
try:
page_obj = paginator.page(page)
except PageNotAnInteger:
page_obj = paginator.page(1) # 如果传入page参数不是整数,默认第一页
except EmptyPage:
page_obj = paginator.page(paginator.num_pages)
is_paginated = True if paginator.num_pages > 1 else False # 如果页数小于1不使用分页
context = {'page_obj': page_obj, 'is_paginated': is_paginated}
return render(request, 'book/faker.html', context)
<!-- 前端页面 -->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>虚假数据库</title>
<!-- 引入自定义样式表 -->
<link rel="stylesheet" href="{% static 'book/css/book-style.css' %}">
</head>
<body>
<!-- 外层容器 -->
<div class="outer">
<!-- 数据表格 -->
<table>
<!-- 表头 -->
<tr class="title">
<th>姓名</th>
<th>密码</th>
<th>公司名称</th>
<th>籍贯</th>
</tr>
<!-- 数据行循环 -->
{% for foo in page_obj %}
<tr>
<td>{{ foo.name }}</td>
<td>{{ foo.password }}</td>
<td>{{ foo.company }}</td>
<td>{{ foo.province }}</td>
</tr>
{% endfor %}
</table>
<!-- 分页链接 -->
<div class="a-tag">
<!-- 如果有多页 -->
{% if is_paginated %}
<!-- 上一页链接 -->
{% if page_obj.has_previous %}
<a href="?page=1">« first</a>
<a href="?page={{ page_obj.previous_page_number }}">上一页</a>
{% endif %}
<!-- 当前页信息 -->
<span>
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
<!-- 下一页链接 -->
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">下一页</a>
<a href="?page={{ page_obj.paginator.num_pages }}">last »</a>
{% endif %}
{% endif %}
</div>
</div>
</body>
</html>
/* 全局样式重置 */
* {
padding: 0;
margin: 0;
}
/* 外层容器样式 */
.outer {
width: 80%;
margin: 0 auto;
}
/* 表头样式,使用 sticky 让表头固定在页面顶部 */
tr.title {
width: 100%;
margin: 0 auto;
position: sticky;
top: 0;
background-color: linen;
color: maroon;
}
/* 表格样式,设定边框折叠 */
.outer > table {
border-collapse: collapse;
}
/* 表格行样式,添加边框 */
.outer > table tr {
border: 1px solid black;
}
/* 表格行悬停样式,鼠标悬停时变色 */
.outer > table tr:hover {
background-color: #80bdff;
color: #fff;
}
/* 表格单元格样式,设定内边距 */
.outer > table td, th {
padding: 10px;
}
/* 分页链接容器样式 */
.a-tag {
margin: 10px auto;
}
/* 超链接样式,设定颜色和去除下划线 */
a {
color: blue;
text-decoration: none;
}
/* 超链接悬停样式,鼠标悬停时变色并显示下划线 */
a:hover {
color: red;
text-decoration: underline;
}
类的使用方式(推荐)
在基于类的视图
ListView
中使用分页,只需设置paginate_by
这个参数即可。它同样会向模板传递page_obj
和is_paginated
这2个参数。
# 导入模块
from django.views.generic import ListView
from django.views.generic import ListView
from .models import FakerData
class TableListView(ListView):
# 定义查询集,获取所有的 FakerData 对象
queryset = models.FakerData.objects.all()
# 定义模板变量的名称,将分页数据传递给模板的变量名设置为 'page_obj'
context_object_name = 'page_obj'
# 定义模板文件的路径
template_name = 'book/faker.html'
# 定义每页显示的数据条数
paginate_by = 10 # 每页10条
分页器模板(前端)
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
{% if is_paginated %}
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1" aria-label="First">第一页</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">‹</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if num == page_obj.number %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'2' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">›</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}" aria-label="Last">最后一页</a>
</li>
{% endif %}
{% endif %}
</ul>
</nav>
分页器模板(后端)
# libs/CommonPagination.py
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=5):
"""
封装分页相关数据
: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)
# views.py
def home(request):
# 获取所有的文章
article_data = models.Article.objects.all()
# 获取到当前的页数
current_page = request.GET.get('page', 1)
# 获取到当前数据的总数
all_count = article_data.count()
# 生成分页器对象 当前页数 全部数据 每页几条数据
page_obj = CommonPagination.Pagination(current_page=current_page, all_count=all_count, per_page_num=10)
# 对数据源进行切割
page_queryset = article_data[page_obj.start:page_obj.end]
return render(request, 'home.html', locals())
<!-- 前端 -->
{% for artic_obj in page_queryset %}
{% comment %}
中间是一些逻辑处理
分页器要放在endfor下面
可以居中显示,这里
{% endcomment %}
{% endfor %}
{# 分页器开始 #}
<div class="text-center">
{{ page_obj.page_html|safe }}
</div>
{# 分页器结束 #}
参考博文
本文作者:小满三岁啦
本文链接:https://www.cnblogs.com/ccsvip/p/18090679
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。