Django-10:django前后端交互

16 篇文章 2 订阅

Django - Ajax

一、回顾往期知识点

本章节需要使用到往期文章中前端的知识点,所以本小结用来回顾一些比较关键的。

1.1、绑定事件

原生JS两种绑定事件的方式:

  • <!--HTML-->
    <button onclick="func1()">点我</button>
    
    <script>
        function func1() {
            alert(111)
        }
    </script>
    
  • <!--HTML-->
    <button id="d1">点我</button>
    
    <script>
        let btnEle = document.getElementById('d1');
        btnEle.onclick = function () {
            alert(222)
        }
    </script>
    

前面了解过了jQuery,所以后续代码都以jQuery为准。

  • jQuery绑定事件

    <script>
    	// 第一种
        $('#d1').click(function () {
                alert('111')
        });
      
    	// 第二种(功能更加强大,支持事件委托)
        $('#d2').on('click',function () {
                alert('111')
        })
    </script>
    

1.1 获取标签内的数据

/*通过不同选择器,获取表单内当前的数据*/
$('#d1').val()  
$('input').val() 
$('.c1').val()  


/*将数据发送给标签(设置表单内数据)*/
$('#d1').val('520')

image-20211130001327896image-20211130001416561

1.2 获取发送的文件数据

let fileEle = document.getElementById('ipt1')
fileEle.value
'''
	.value  无法获取到文件数据,只能获取到文件路径。
'''

可以利用files方法,来获取到提交的文件数据。

let fileEle = document.getElementById('ipt1')
fileEle.files[0]

'''
File {name: '测试.png', lastModified: 1638241647821, lastModifiedDate: Tue Nov 30 2021 11:07:27 GMT+0800 (中国标准时间), webkitRelativePath: '', size: 144161, …}
'''

注意:

  • files是DOM对象的方法,如果是使用的jQuery语法,获取的jQuery对象,是无法使用该方法的,需要先进行转换。

    jQuery对象 – > DOM对象:

    $('#input')[0]
    
    '''
    加上索引0即可
    '''
    

二、Ajax

Ajax简介:

AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)

AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

  • 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
  • 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求

优点:

  • AJAX使用JavaScript技术向服务器发送异步请求;
  • AJAX请求无须刷新整个页面;
  • 因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;
  • 两个关键点:1.局部刷新,2.异步请求

常见应用场景:

image-20221016005829380

一、基本语法:(使用jQuery为主)

  • $.ajax({})
    

    所需参数:

    $.ajax({
        url:''   #指定朝哪个后端发送ajax请求,不写就是朝当前地址提交
        type:'post'   #不指定默认就是get,都是小写
        data:{}   #发给后端的数据
        success:function(形参接收后端返回的结果){处理代码}  #回调函数
    })
    

二、小案例

页面上有三个input框:

  • 在前两个框中输入数字,点击按钮后,朝后端发送ajax请求
  • 后端计算出结果,再返回给前端,动态展示的到第三个input框中,整个过程页面不准有刷新,也不能在前端计算。

HTML代码如下:

<input type="text" id="num1"> +
<input type="text" id="num2"> =
<input type="text" id="num3">
<p>
    <button id="btn">计算</button>
</p>

2.1 JS代码与python代码的推导

第一步:

  • JS

    $('#btn').on('click',function () {
        $.ajax({
            url:'',
            type:'post',
            data:{'num1':$('#num1').val(),'num2':$('#num2').val()},
            success:function (args) {
    
            }
        })
    })
    
  • Python

    def ajax_test(request):
        if request.method == 'POST':
            print(request.POST)
            #输出结果 <QueryDict: {'num1': ['xxx'], 'num2': ['xxx']}>
        return render(request,'ajax_test.html')
    

代码分析:

  • JS:先给计算按钮添加绑定事件,当点击触发之后,执行函数体内的代码。

    代码内发送Ajax请求,类型为POST,发送的数据为num1表单内的数据,与num表单内的数据,随后将这些获取到的数据组成字典返回给后端(在js中,这种数据类型叫对象,而在python中叫做字典)

  • Python:打印request.POST,查看是否可以正常的获取到Ajax发来的数据,答案是可以的。

第二步:

  • 后端Python代码中,可以获取到数据后,就可以着手书写计算上传数值的合了。

    # Python
    def ajax_test(request):
        if request.method == 'POST':
            print(request.POST)
            # <QueryDict: {'num1': ['3'], 'num2': ['4']}>
            num1 = request.POST.get('num1')
            num2 = request.POST.get('num2')
            res = int(num1)+int(num2)
            return HttpResponse(res)
        
        return render(request,'ajax_test.html')
    

    这里需要注意,返回计算后的结果时,不可以直接return返回,而是要使用HttpResponse。

第三步:

  • 将后端计算好的结果,渲染到页面上

    $('#btn').on('click',function () {
        $.ajax({
            url:'',
            type:'post',
            data:{'num1':$('#num1').val(),'num2':$('#num2').val()},
            /*回调函数*/
            success:function (args) {
                $('#num3').val(args)
            }
        })
    })
    

    后端返回的函数,会被回调函数的形参所接收,所以后续只需要让表单内的值,与返回的计算结果相等即可。

补充:

  • 判断当前请求是否是ajax请求

    request.is_ajax()
    
    '''
    返回值为布尔类型
    '''
    

三、前后端交互的编码格式

本章节主要研究,前后端在传输数据的编码格式(contentType)

由于GET请求数据就是直接放在url后面的,如:url?name=liuyu&pwd=123,是可以直接看到的,所以本章节主要研究POST请求数据的编码格式。

可以朝后端发送post请求的方式:

  • form表单
  • ajax请求

前后端传输数据的编码格式:

  • urlencoded
  • formdata
  • json

下面将针对form表单以及ajax请求,在使用不同编码格式时如何交互。

3.1 研究form表单

3.1.1 urlencoded编码格式

urlencoded默认的编码格式。

  • 全称:Content-Type: application/x-www-form-urlencoded

    image-20211206174222616

结论:

  • Django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST

    username=liuyu&password=123  >>>  request.POST
    

3.2.2 formdata编码格式

formdata编码的应用场景:

  • 上传文件

    """
    当需要上传文件时,就需要form表单必须满足两个条件:
    	1.method必须指定成post
    	2.enctype必须换成formdata,否则只能接收到文件名。
    		其中,这个enctype就是设置编码为formdata格式。
    """
    <form action="" method="post" enctype="multipart/form-data">
        <p>上传图片:<input type="file" name="photo"></p>
        <p><input type="submit"></p>
    </form>
    

request.POST只能拿到普通的键值对数据,对于文件数据,则不行,需要专门利用request.FILES来获取文件数据

file_obj = request.FILES.get('file') #获取文件对象,file为前端标签的name属性值。

结论:

  • Django后端针对符合formdata编码格式的数据都会自动帮你解析封装到request.FILES

    <MultiValueDict: {'file': [<InMemoryUploadedFile: 笑哭.jpg (image/jpeg)>]}>
    

3.2.3 无法发送json格式数据

'''form表单是没有办法发送json格式数据的'''

3.2 研究Ajax请求

2.2.1 urlencoded

form表单一样,默认都是urlencoded编码格式

代码示例:

<div class="col-sm-offset-2 col-sm-10">
  <button type="submit" class="btn btn-danger " id="button1">Ajax提交</button>
</div>


<script>
    $('#button1').click(function () {
        $.ajax({
            url:'',
            type:'post',
            data:{'name':'liuyu','age':19}, 
            success:function () {}
        })
    })
</script>

通过给标签绑定事件的方式,来向后端提交数据,如图所示,默认为urlencoded

image-20211206223108997 image-20211206223314073 image-20211206223326095

结论:

  • django后端针对符合urlencoded编码格式的数据都会自动解析封装到request.POST中

3.2.2 ajax发送文件(formdata)

ajax发送文件需要借助于js内置对象FormData

<script>
    // 点击按钮朝后端发送普通键值对和文件数据
    $('#d4').on('click',function () {
        // 1 需要先利用FormData内置对象
        let formDateObj = new FormData();
        // 2 添加普通的键值对
        formDateObj.append('username',$('#d1').val());
        formDateObj.append('password',$('#d2').val());
        // 3 添加文件对象
        formDateObj.append('myfile',$('#d3')[0].files[0])
        // 4 将对象基于ajax发送给后端
        $.ajax({
            url:'',
            type:'post',
            data:formDateObj,  // 直接将对象放在data后面即可

            // ajax发送文件必须要指定的两个参数
            contentType:false,  // 不需使用任何编码 django后端能够自动识别formdata对象
            processData:false,  // 告诉你的浏览器不要对你的数据进行任何处理

            success:function (args) {
            }
        })

    })
</script>
  • contentType:falseprocessData:false,这两句话一定要附带上。

  • 借助formDate对象,将文件数据与键值对数据都利用append方法添加,随后直接返回给python后端。

  • $(‘#d3’)[0].files[0],这里的filter[0],表示取DOM对象的文件数据对象,又因为这是DOM对象的方法而不是jQuery对象,所以还需要进行转换。

总结:

  • django后端能够直接识别到formdata对象,并且能够将内部的普通键值,自动解析并封装到request.POST中,文件数据自动解析并封装到request.FILES中。

3.2.3 ajax发送json格式数据

代码示例:

  • HTML

    <div>
        <button class="btn btn-warning" id="ipt_json">点我发送JSON数据</button>
    </div>
    
  • JS

    $('#ipt_json').on('click',function () {
        $.ajax({
            url:'',
            type:'post',
            contentType:'application/json',  // 指定编码格式
            data:JSON.stringify({'username':'liuyu','age':20}),
            success:function () {
    
            }
        })
    })
    
  • Python(Django)

    def ajax_test(request):
        if request.method == 'POST':
            print(request.POST)
            print(request.body)
    
        return render(request,'ajax_test.html')
    

    打印结果如下:
    <QueryDict: {}>

    b’{“username”:“liuyu”,“age”:20}'

    可以看到,request.POST为空,而前端发送的JSON数据,被封装进了request.body里面,因为django后端不会帮我们处理json格式数据。

所以当前端返回给后端JSON格式的数据时,后端Python首先需要解码,然后再反序列化

def ajax_test(request):
    if request.method == 'POST':

        '''
        ajax发送json数据
        '''
        print(request.body)
        json_bytes = request.body
        json_str = json_bytes.decode('utf-8')
        json_dict = json.loads(json_str)
        print(json_dict['username'],json_dict['age'])

    return render(request,'ajax_test.html')

'''
打印结果:
	b'{"username":"liuyu","age":20}'
	liuyu 20
'''

python后端如何给前端发送json数据

  • 第一种,使用JsonResponse

    return JsonResponse({'code':200})
    
    $.ajax({
    	/*其他参数略*/
        success:function (args) {
    		console.log(args,typeof args)
        }
    })
    
    • 打印输出:{code: 200} ‘object’

      直接就是JS中的对象,无需再进行反序列化。

  • 第二种,使用HttpResponse,但是前端收到数据后进行反序列化

    return HttpResponse({'code':200})
    
    $.ajax({
    	/*其他参数略*/
        success:function (args) {
    		console.log(args,typeof args)
        }
    })
    /*
    打印输出:code string
    */
    

    后端直接返回字典格式的数据时,前端收到的是自动进行过序列化的字符串数据,所以需要再进行反序列化。

    success:function (args) {
        resObj = JSON.parse(args)
        console.log(resObj)
    }
    

四、django自带的序列化组件

引子:

  • 后端返回数据,前端将数据以列表的形式展示在页面上。

    def ab_ser(request):
        user_queryset = models.User.objects.all()
    	return render(request,'datalist.html',locals())
    
    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>Username</th>
                <th>Age</th>
                <th>Gender</th>
            </tr>
        </thead>
        <tbody>
            {% for user_obj in data_queryset %}
                <tr>
                    <td>{{ user_obj.pk }}</td>
                    <td>{{ user_obj.username }}</td>
                    <td>{{ user_obj.age }}</td>
                    <td>{{ user_obj.gender }}</td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
    

上述代码,是利用了模板语法来实现将后端的数据“层层剥出”,最终实现把数据渲染在页面上。

  • 实际项目中,往往都是前后端分离的,没有模板语法…

    解决方案:在返回给前端数据的时候,不丢过去Queryset对象,而是我们封装好的一个字典

user_queryset = models.User.objects.all()
# [{obj1},{obj2},{obj3},{obj4},.....]
user_list = []
for user_obj in user_queryset:
	tmp = {
		'pk':user_obj.pk,
		'username':user_obj.username,
		'age':user_obj.age,
		'gender':user_obj.get_gender_display()
         }
	user_list.append(tmp)
    
return JsonResponse(user_list,safe=False)
	# 由于这里返回的是列表,所以需要添加safe参数,值为False
  • 此时前端反序列化得到对象(python中叫字典),有了数据之后,前端可通过jsDOM操作将数据渲染到页面上,不需要模板语法。

python后端处理好的数据:

[
 {"pk": 1, "username": "张三", "age": 25, "gender": "male"}, 
 {"pk": 2, "username": "李四", "age": 31, "gender": "female"},
 {"pk": 3, "username": "王五", "age": 32, "gender": "others"}, 
 {"pk": 4, "username": "马六", "age": 40, "gender": 4}
 ]

在前后端分离的项目中,作为后端开发只需要写代码,将数据处理好,能够序列化返回给前端即可,随后再写一个接口文档 告诉前端每个字段代表的意思即可。

Django中提供了很多序列化组件,可以帮助我们省略创建列表封装数据的步骤

4.1 serializers组件

序列化组件之一,还有drf等,后续章节再介绍。

作用:

  • 直接丢入一个对象,指定编码格式,即可转换成对应编码格式。
from django.core import serializers
res = serializers.serialize('编码格式',obj)

代码示例:

import json
from django.http import JsonResponse
from django.core import serializers


def ab_ser(request):
    user_queryset = models.User.objects.all()
    
    """会自动将数据变成json格式的字符串 并且内部非常的全面"""
    res = serializers.serialize('json',user_queryset)
    
    return HttpResponse(res)  # 直接返回到页面上,查看效果。

"""返回在页面上的值
        
[
    {   "model": "app01.user", 
        "pk": 1, 
        "fields": {"username": "jason", "age": 25, "gender": 1}}, 

    {   "model": "app01.user", 
        "pk": 2, 
        "fields": {"username": "egon", "age": 31, "gender": 2}}, 

    {   "model": "app01.user", 
        "pk": 3, 
        "fields": {"username": "kevin", "age": 32, "gender": 3}},

    {   "model": "app01.user", 
        "pk": 4, 
        "fields": {"username": "tank", "age": 40, "gender": 4}}
]
"""
  • 可以看到,serializers还会标注上数据是来自那张表的,主键值是多少,字段值是什么。

    gender字段的值由于使用了choise参数,所以实际存入数据库的值为1、2、3、4,所以就需要我们后端程序员,书写文档,来告诉前端程序员每个key对应的value都是什么意思,随后至于说怎么渲染,这就是前端的工作了。

五、ajax小项目

5.1 代码

1、模型层:

  • from django.db import models
    
    class Usermsg(models.Model):
        name = models.CharField(max_length=10,verbose_name='名字')
        age = models.IntegerField()
        hobby = models.TextField()
    

2、在测试文件,给数据库添加数据:

  • import os
    
    if __name__ == "__main__":
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoProject.settings")
        import django
        django.setup()
    
        from app01 import models
        for i in range(0,10):
            models.Usermsg.objects.create(name='test',age=18,hobby='被测....试')
    

3、路由层:

  • urlpatterns = [
        url(r'^admin/', admin.site.urls),
    
        url(r'^userlist/',views.user_msg,name='userlist'),
    ]
    

4、模板层:

  • <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <!--静态文件配置,的动态解析(令牌可随意更换)-->
        {% load static %}
        <script src="{% static 'jquery-3.6.0.min.js' %}"></script>
        <script src="{% static 'bootstrap.min.js' %}"></script>
        <link rel="stylesheet" href="{% static 'bootstrap.css' %}">
    </head>
    <body>
    
    <div class="container">
        <div class="row">
            <div class="col-md-10">
                <!--面板-->
                <div class="panel panel-info">
                    <div class="panel-heading">数据列表</div>
                    <div class="panel-body">
                        <!--图书表格-->
                        <table class="table table table-striped table-bordered table-hover table-condensed">
                            <thead>
                                 <tr class="success">
                                        <th>姓名</th>
                                        <th>年龄</th>
                                        <th>擅长领域</th>
                                        <th>操作</th>
                                </tr>
                            </thead>
                            <tbody></tbody>
                        </table>
                        <!--图书表格-->
                    </div>
                </div>
                <!--面板-->
            </div>
        </div>
    </div>
    
    
    
    <script>
        //定义函数。获取图书列表
        function getList() {
            //发送get请求
            $.ajax({
                type:'GET',
                url:'userlist',
                //回调函数
                success:function (res) {
                    //因为每次刷新、添加、删除都需要调用该函数渲染
                    //所以在每次渲染之前,先把之前的数据删除掉。
                     $('tbody').empty()
    
                    //反序列化,将后端传递过来的JSON格式数据转成对象
                    res = JSON.parse(res)
    
                    //创建一个空数组,随后把每一个用户数据对象提取出数据,最后利用字符串拼接的方式,将数据拼接为html格式的字符串,从而达到数据渲染到页面的效果。
                    let rows = []
                    $.each(res,function (i,item) {
                        // 形参i为对象在列表中的索引,形参item为用户数据对象。
                        rows.push('<tr><td>'+ item.fields.name +'</td><td>'+ item.fields.age +'</td><td>'+ item.fields.hobby +'</td><td><button class="btn btn-success btn-xs"   data-id='+item.pk+'>编辑</button><button class="btn btn-danger btn-xs"    data-id='+item.pk+'>删除</button></td></tr>')
                        //这里的编辑和删除标签,添加了自定义属性data-id,值为用户数据的ID
                    })
    
                    //渲染到页面上
                    $('tbody').html(rows.join(''))
                }
            })
        }
    
        //调用刚刚定义的函数,将数据渲染到页面上
        getList()
    
    
        //删除数据
        $('tbody').on('click','.btn-danger',function () {
            //获取对应需要删除的id值
            let delId = $(this).attr('data-id')
            //发起post请求,并将需要删除的数据id返回给后端。
            $.ajax({
                url:'userlist',
                type:'post',
                contentType:'json',
                data:JSON.stringify({'type':'del','id':delId,}),
                success:function (args) {
                    //如果后端返回的状态码为200,那么就重新刷新列表数据。
                    if (args.status === 200){
                        getList()
                    }
                }
            })
        })
    </script>
    
    </body>
    </html>
    

5、视图层

  • from app01 import  models
    import json
    from django.core import serializers
    from django.http import  JsonResponse
    def user_msg(request):
        '''
        如果是ajax请求,那么就判断是那种类型的请求,是get还是post
        随后针对不同的请求方式,来进行响应。
        '''
        if request.is_ajax():
            # 当前端ajax发起GET请求时,将数据库中的数据返回
            if request.method == 'GET':
                data_queryset = models.Usermsg.objects.all()
                # 利用django自带的序列化模块,可以直接将queryset对象进行处理,无需人为手动的封装数据。
                res = serializers.serialize('json', data_queryset)
                return  JsonResponse(res,safe=False)
    
            # 当前端ajax发起POST请求时,后端获取json数据,并进行处理。
            if request.method == 'POST':
                res_bytes = request.body
                res = json.loads(res_bytes)
                if res['type'] == 'del':
                    # 执行删除操作。
                    res = models.Usermsg.objects.filter(pk=res['id']).delete()
                    # res : (1, {'app01.Usermsg': 1})
                    if res:
                        # 将处理的结果返回给前端回调函数
                        return JsonResponse({'status':200,'msg':'删除数据成功'})
                else:
                    pass
                    # 执行编辑操作,此处代码略。
    
        return  render(request,'ajax_sweetalert.html')
    
    

最终实现的效果展示:

django-ajax-5.1案例

5.2 结合sweetalert实现二次确认

在很多需要删除的场景中,往往会有弹框,提示是否确定要删除,那么我们如何利用现有的知识点,完成这个效果呢?

项目:djangoAjaxProject

路由:http://127.0.0.1:8000/userlist/

所有功能都以写好,等待与sweetalert的结合。

<script>
    $('.del').on('click',function () {
        // 先将当前标签对象存储起来
        let currentBtn = $(this);
        // 二次确认弹框
        swal({
          title: "确定要删吗?",
          text: "此操作不可逆,请谨慎操作!",
          type: "warning",
          showCancelButton: true,
          confirmButtonClass: "btn-danger",
          confirmButtonText: "我就要删!",
          cancelButtonText: "算了,冷静冷静!",
          closeOnConfirm: false,
          closeOnCancel: false,
          showLoaderOnConfirm: true
        },
        function(isConfirm) {
          if (isConfirm) {
                // 朝后端发送ajax请求删除数据之后 再弹下面的提示框
                $.ajax({
                    {#url:'/delete/user/' + currentBtn.attr('delete_id'),  // 1 传递主键值方式1#}
                    url:'/delete/user/',  // 2 放在请求体里面
                    type:'post',
                    data:{'delete_id':currentBtn.attr('delete_id')},
                    success:function (args) {  // args = {'code':'','msg':''}
                        // 判断响应状态码 然后做不同的处理
                        if(args.code === 1000){
                            swal("删了!", args.msg, "success");
                            // 1.lowb版本 直接刷新当前页面
                            {#window.location.reload()#}
                            // 2.利用DOM操作 动态刷新
                            currentBtn.parent().parent().remove()
                        }else{
                            swal('完了','出现了未知的错误','info')
                        }
                    }

                })

          } else {
            swal("已取消", "当前数据已取消删除", "error");
          }
        });
    })

</script>

六、批量插入

def ab_pl(request):
    # 先给Book插入一万条数据
    # for i in range(10000):
    #     models.Book.objects.create(title='第%s本书'%i)
    # # 再将所有的数据查询并展示到前端页面
    book_queryset = models.Book.objects.all()

    # 批量插入
    # book_list = []
    # for i in range(100000):
    #     book_obj = models.Book(title='第%s本书'%i)
    #     book_list.append(book_obj)
    # models.Book.objects.bulk_create(book_list)
    """
    当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间
    :param request: 
    :return: 
    """
    return render(request,'ab_pl.html',locals())

七、自定义分页器

为什么要用自定义分页器:

  • 上述的批量插入,可能会造成数据渲染时的一个问题,那就是页面上不能一次性展示那么多,所以需要分页展示。
  • django内置的有分页器,但是效果不太好,而且使用起来也没那么好用,所以官网又提供了模块,供我们来拷贝使用。

当我们需要使用到非django内置的第三方功能或者组件代码的时候,我们一般情况下会在项目的根目录下,创建一个名为utils文件夹,在该文件夹内对模块进行功能性,创建不同的目录进行划分。

所以自定义分页器做好在新建的utils文件夹下。

模块源码:

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)

后端:

from utils.mypage import *
def mypage(request):
    msgdata_queryset = models.Usermsg.objects.all()
    
    '''自定义分页器'''
    current_page = request.GET.get("page", 1)
    all_count = msgdata_queryset.count()  
    page_obj = Pagination(current_page=current_page, all_count=all_count, per_page_num=5)
    																	# per_page_num=5 表示每页展示5条数据	
    page_queryset = msgdata_queryset[page_obj.start:page_obj.end]
    
    return render(request,'mypage.html',locals())
  • 拷贝过来的第三方模块,放在utils目录下,名为mypage,所以在上面就直接from…import导入。
  • 变量msgdata_queryset为查询出来的数据,正常情况下,直接通过locals()方法将该数据传给模板语法,但是这里使用自定义分页器,所以需要复制粘贴下列代码,注意变量间的替换即可,后续模板语法操作的数据为page_queryset,等同于msgdata_queryset,但是内部封装了很多方法。

前端:

<div class="container">
    <div class="row">
        <div class="col-md-10">
            <!--面板-->
            <div class="panel panel-info">
                <div class="panel-heading">数据列表</div>
                <div class="panel-body">
                    <!--图书表格-->
                    <table class="table table table-striped table-bordered table-hover table-condensed">
                        <thead>
                             <tr class="success">
                                    <th>姓名</th>
                                    <th>年龄</th>
                                    <th>擅长领域</th>
                                    <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            {% for data_obj in page_queryset %}
                                <tr>
                                    <td>{{ data_obj.name }}</td>
                                    <td>{{ data_obj.age }}</td>
                                    <td>{{ data_obj.hobby }}</td>
                                    <td>
                                        <button class="btn btn-success btn-xs"   data-id='+item.pk+'>编辑</button>
                                        <button class="btn btn-danger btn-xs"    data-id='+item.pk+'>删除</button>
                                    </td>
                            {% endfor %}
                        </tbody>
                    </table>
                    <!--图书表格-->
                    
                    <!--自定义分页器-->
                 	{{ page_obj.page_html|safe }}
                    <!--自定义分页器-->
                    
                </div>
            </div>
            <!--面板-->
        </div>
    </div>
</div>
  • 上面说道,page_queryset等同于msgdata_queryset,并且内部还封装了很多属性和方法,如:控制每页展示行数等。

    所以在渲染数据时,for…in的就是page_queryset。

  • 最后直接在需要的地方书写{{ page_obj.page_html|safe }}即可,分页器就会转义之后成功渲染在页面上。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿煜酱~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值