Django 批量操作数据以及分页器

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)之间的关系,如下表所示
页码展示条数页码结果集开始位置结果集结束位置
5105
52510
531015
页码展示条数页码结果集开始位置结果集结束位置
101010
1021020
1032030
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">&laquo;</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">&raquo;</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就需要在第一个数字上加一
	
数据条数页码最大展示条数页码数
991010
1001010
1011011
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">&laquo;</span>
	                </a>
	            </li>
	            	# 使用后端传入的变量,因为是HTML代码,需要加上取消转义过滤器
	                {{ html|safe }}
	            <li>
	                <a href="#" aria-label="Next">
	                    <span aria-hidden="true">&raquo;</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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值