day67 前后端数据交互

一、前后端传输数据的编码格式(contentType)

由于get请求的数据都是直接跟在url后面的:url?username=jason&password=123

所以这里主要研究post请求数据的编码格式

前端可以朝后端发送post请求的方式:form表单,ajax请求

前后端传输数据有三种编码格式:urlencoded,formdata,json

1 form表单

通过实验,我们可以在前端network里找到请求头内:Content-Type:application/x-www-form-urlencoded,这个表示form表单默认的数据编码格式是urlencoded

数据连接的方式:username=123&password=123&file=timg.jpg

在django中会把所有的数据以键值对的形式都传给request.POST中

当我们修改这个默认请求数据格式后

<form action="" method="post" enctype="multipart/form-data">

在后端会发现,formdata的数据会把普通数据以键值对的形式传给POST.get

把文件数据传给request.FILES中

2 ajax请求

默认编码格式:urlencoded,也会把数据以键值对的形式传给request.POST中

二、ajax发送json格式数据

前提:在规定了传输数据以json格式之后就要真的传json格式的数据!!!

前提2:request方法补充:

request.is_ajax() 判断当前请求是否是ajax请求,返回布尔值

django不会对前端发送来json格式数据进行处理,所以得我们手动获取

前端

<script>
    $('#d1').click(function () {
        $.ajax({
            url:'',
            type:'post',
            data:JSON.stringify({'username':'jason','age':25}),
            contentType:'application/json',  // 指定编码格式
            success:function () {

            }
        })
    })
</script>

后端

def ab_json(request):
    if request.is_ajax():
        print(request.is_ajax()) # 判断是否是ajax请求
        print(request.POST)
        print(request.FILES)
        # body是获取前端发送的二进制数据
        print(request.body)  
        # b'{"username":"jason","age":25}'
        # 针对json格式数据需要你自己手动处理
        json_bytes = request.body
        # json_str = json_bytes.decode('utf-8')
        # json_dict = json.loads(json_str)
        # json.loads括号内如果传入了一个二进制格式的数据那么内部自动解码再反序列化
        json_dict = json.loads(json_bytes)  # {'username': 'jason', 'age': 25} <class 'dict'>
        print(json_dict,type(json_dict))  # {'username': 'jason', 'age': 25} <class 'dict'>
    return render(request,'ab_json.html')

ajax发送json格式数据的要点

  • contentType参数必须指定成:application/json
  • 前端发送的必须是真的json格式数据
  • django后端不会帮我们处理数据,需要我们自己去request.body中获取并处理数据

三、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>

后端

def ab_file(request):
    if request.is_ajax():
        if request.method == 'POST':
            print(request.POST)
            print(request.FILES)
    return render(request,'ab_file.html')

总结

  • 传输文件数据需要用到formdata对象
    • 添加普通键值对:formDateObj.append('username',$('#d1').val());
    • 添加文件对象: formDateObj.append('myfile',$('#d3')[0].files[0])
  • 需要制定两个关键性的参数
    • contentType:false 不需要指定任何编码,django可以识别formdata对象
    • processdata:false 告诉浏览器不要对这个数据进行处理
  • django会识别formdata对象并将内部的普通键值对自动解析并封装到request.POST中,文件对象解析到request.FILES中

四、django自带的序列化组件(drf做铺垫)

当我们后端要给前端穿一个json数据的时候

from django.http import JsonResponse
def ab_ser(request):
    user_queryset = models.User.objects.all()
    # [{},{},{},{},{}] 这是我们要传送的数据格式
    user_list = []
    for user_obj in user_queryset:
        # 通过一个for循环把每一个对象的值全都转换成一个字典的形式,添加到我们要传输的列表当中
        tmp = {
            'pk':user_obj.pk,
            'username':user_obj.username,
            'age':user_obj.age,
            'gender':user_obj.get_gender_display()
        }
        user_list.append(tmp)
        # JsonResponse会帮我们把要传送到前端的数据转化成json格式,但是默认只能传字典,如果要传其他格式就要写safe=False
    return JsonResponse(user_list,safe=False)
    return render(request,'ab_ser.html',locals())

'''
[
 {"pk": 1, "username": "jason", "age": 25, "gender": "male"}, 
 {"pk": 2, "username": "egon", "age": 31, "gender": "female"},
 {"pk": 3, "username": "kevin", "age": 32, "gender": "others"}, 
 {"pk": 4, "username": "tank", "age": 40, "gender": 4}
 ]
'''

上述是lowb方法,实际上django有自带的序列化组件:serializers

from django.core import serializers
def ab_ser(request):
    user_queryset = models.User.objects.all()
    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}}
]

'''

总结:在前后端分离的项目中,我们只需要把数据处理好,能够序列化返回给前端即可,在写一个接口文档,告诉前端每个字段代表的意思即可

五、ajax结合sweetalert

百度搜索sweetalert,找到中文官网,然后在页面中导入CDN或者链接静态文件(必须先导入jQuery)。接着去文档中找一个自己喜欢的swal弹框

// 一个最简单的弹框
 swal("一级标题内容", "内容", "信息格式(success/info...)");

然后结合这个弹框实现我们的二次确认删除操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <style>
        div.sweet-alert h2 {
            padding-top: 10px;
        }
    </style>
    {% load static %}
    <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
    <script src="{% static 'dist/sweetalert.min.js' %}"></script>
    {#    关于静态文件查找 其实还有一个方法 代码发布再透露   #}
</head>
<body>
<div class="container-fluid">
    <h1 class="text-center">数据展示</h1>
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <table class="table-striped table table-hover">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>username</th>
                        <th>age</th>
                        <th>gender</th>
                        <th>actions</th>
                    </tr>
                </thead>
                <tbody>
                    {% for user_obj in user_queryset %}
                        <tr>
                            <td>{{ user_obj.pk }}</td>
                            <td>{{ user_obj.username }}</td>
                            <td>{{ user_obj.age }}</td>
                            <td>{{ user_obj.get_gender_display }}</td> <-- 显示gender的对应关系-->
                            <td>
                                <button class="btn btn-primary btn-xs">编辑</button>
                                <button class="btn btn-danger btn-xs del" delete_id="{{ user_obj.pk }}">删除</button>
                            </td>
                        </tr>
                    {% endfor %}

                </tbody>
            </table>
        </div>
    </div>
</div>

<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>
</body>
</html>

六、批量插入

def ab_pl(request):
    # 先给Book插入一万条数据
    # 方式一、直接for循环创建,这种方式相当于每循环一次都创建一个数据存到数据库中
    # for i in range(10000):
    #     models.Book.objects.create(title='第%s本书'%i)
    # # 再将所有的数据查询并展示到前端页面
    book_queryset = models.Book.objects.all()

    # 批量插入
    # 方式二、for循环先把数据对象放到列表内,然后用bulk_create创建方式一次性把列表中的所有数据都创建起来
    # 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())

七、分页器

分页器在写之前先思考,我们大概需要哪些参数,一页展示多少数据,总共有多少数据,每页的起始数据,结尾数据

  # 先获取所有数据对象
    book_list = models.Book.objects.all()

    # 获取get请求提供的参数数据,如果是直接访问还没有点按钮则访问的是主页,此时显示第一页
    current_page = request.GET.get('page',1)  # 如果获取不到当前页码 就展示第一页
    # 数据类型转换
    try:
        # 因为前段传来的数据是字符形式
        current_page = int(current_page)
    except Exception:
        # 如果传来的是非数字就转换成1
        current_page = 1
    # 每页展示多少条
    per_page_num = 10
    # 起始位置 比如第一页是 (1-1)*10 =0 索引0的数据开始
    start_page = (current_page - 1) * per_page_num
    # 终止位置
    end_page = current_page * per_page_num

    # 计算出到底需要多少页
    all_count = book_list.count()
    # 内置函数的使用,传两个值,一个是总数,一个是单页展示数,得到的结果一个是总页数,一个是余数
    page_count, more = divmod(all_count, per_page_num)
    # 如果有余数就需要额外一页去展示,总页数+1
    if more:
        page_count += 1
    # 设置一个变量用来存放我们要书写的html代码
    page_html = ''
    xxx = current_page
    # 为了防止出现负数页码,这里区分如果小于6就不做运算
    if current_page < 6:
        current_page = 6
    # 每页展示11个分液器
    for i in range(current_page-5,current_page+6):
        if xxx == i:
            # 将分液器的展示页的标签做成活动态
            page_html += '<li class="active"><a href="?page=%s">%s</a></li>'%(i,i)
        else:
            page_html += '<li><a href="?page=%s">%s</a></li>'%(i,i)


    # 传书的对象的时候也要限制
    book_queryset =  book_list[start_page:end_page]
    return render(request,'ab_pl.html',locals())
posted @ 2020-06-03 21:09  lxttt521  阅读(197)  评论(0编辑  收藏  举报