15-Django批量插入数据库与分页管理

批量插入数据

关键词: bulk_create(可迭代对象)

  1. models.模型名去创建数据
  2. 用一个变量去接接收1的返回值,并加入列表
  3. 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')

image-20240307163316771

分页器

楔子

通过刚刚,我们生成了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

如何使用

  1. 获取要展示的对象列表QuerySet
  2. 将对象列表和每页展示数量传递给Paginator,返回一个分页对象
  3. 调用该对象的各种方法,获取各种分页信息
  4. 在前端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">&laquo; 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 &raquo;</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;
}

image-20240307192845834

类的使用方式(推荐)

在基于类的视图ListView中使用分页,只需设置paginate_by这个参数即可。它同样会向模板传递page_objis_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">&lsaquo;</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">&rsaquo;</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://pythondjango.cn/django/basics/14-pagination/

https://www.liujiangblog.com/course/django/173

posted @ 2024-03-23 00:49  小满三岁啦  阅读(15)  评论(0编辑  收藏  举报