Ajax

  • Ajax操作(重点)

    '''
    异步提交
    局部刷新
    eg:github注册
    	动态获取用户名实时的跟后端确认并实时展示前端(局部刷新)
    
    朝后端发送请求的方式
    	1 浏览器地址栏直接输入url回车				GET请求
    	2 a标签href属性							  GET请求
    	3 form表单							   GET请求/POST请求
    	4 ajax									GET请求/POST请求
    	
    AJAX不是新的编程语言,而是一种使用现有标准的新方法(比较装饰器)
    
    AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交互数据并更新部分网页内容(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
    
    Ajax只学习jQuery封装之后的版本(不学原生的 原生的复杂并且在实际项目中也一般不用),所以在前端页面使用Ajax的时候需要确保导入了jQuery
    ps:并不只有jQuery能够实现Ajax,其他的框架也可以 但是换汤不换药 原理是一样的
    '''
    

    案例

    '''
    页面上有三个input框
    	在前两个框中输入数字 点击按钮 朝后端发送ajax请求
    	后端计算出结果 在返回给前端动态展示到第三个input框中
    	(整个过程页面不准有刷新,也不能在前端计算)
    '''
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
        <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <link rel="stylesheet" href="font-awesome-4.7.0/css/font-awesome.min.css">
    </head>
    <body>
    <input type="text" id="d1">+
    <input type="text" id="d2">=
    <input type="text" id="d3">
    <p>
        <button id="btn">点我</button>
    </p>
    
    // ajax固定语法结构
    <script>
        // 先给按钮一个绑定事件
        $('#btn').click(function () {
            // 朝后端发送ajax请求
            $.ajax({
                // 1 朝哪个后端发送Ajax请求
                url:'',  // 默认朝当前地址提交
                // 2 请求方式
                type:'post', // 不写默认就是'get' 全是小写
                // 3 数据
                {#data:{'username':'jason','password':123},#}
                data:{'i1':$('#d1').val(),'i2':$('#d2').val()},
                // 4 回调函数:当后端给你返回结果的时候会自动触发 args接受后端的返回结果
                success:function (args) {
                    // alert(args)  // 通过DOM操作动态渲染到第三个input里面
                    {#$('#d3').val(args)#}
                    // console.log(typeof args)  // string
                    // 用HttpResponse打印出来的是string类型
                    // 用JsonResponse打印出来的是object对象
                    // console.log(args)  // {"code": 100, "msg": 666} 不是对象
    
                }
            })
        })
    </script>
    </body>
    </html>
    '''
    总结
    针对后端如果是用HttpResponse返回的数据 回调函数不会自动帮你反序列化
    如果后端直接用的是JsonResponse返回的数据 回调函数会自动帮你反序列化
    
    HttpResponse解决方式
    	1 在前端利用JSON.parse()
    	2 在ajax里面配置一个参数
    		dataType:'JSON'
    结论:写Ajax的时候 可以直接将dataType参数加上 以防万一 或者后端就用JsonResponse
    	$.ajax({
                // 1 朝哪个后端发送Ajax请求
                url:'',  // 默认朝当前地址提交
                // 2 请求方式
                type:'post', // 不写默认就是'get' 全是小写
                dataType:'application/j',
                // 3 数据
                {#data:{'username':'jason','password':123},#}
                data:{'i1':$('#d1').val(),'i2':$('#d2').val()},
                // 4 回调函数:当后端给你返回结果的时候会自动触发 args接受后端的返回结果
                success:function (args) {
    '''
    
  • 前后端传输数据编码格式(contentType)

    # 我们主要研究post请求数据的编码格式
    # 如何查看编码格式 
    	检查->network->Headers->contentType
    '''
    get请求数据就是直接放在url后面的
    url?username=jason&password=123
    '''
    
    # 可以朝后端发送post请求的方式
    '''
    1 form表单
    2 Ajax请求
    '''
    
    '''
    前后端传输数据的编码格式
    	urlencoded
    	
    	formdata
    	
    	json
    '''
    # 研究form表单
    	默认的数据编码格式是urlencoded
        数据格式:username=jason&password=123
        django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中
        	username=ycc&password=123&file=DAY05.md 》》》 request.POST
        
    	如果把编码格式改成enctype="mulitpart/form-data",那么针对普通的键值对还是解析到request.POST中 
        而将文件解析到request.FILES中
        
        form表单是没有办法发送json格式数据的
        
    # 研究ajax
    	默认的编码格式也是urlencoded
        数据格式:username=ycc&age=18
        django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中
        	username=ycc&age=18 》》》 request.POST
            
    
  • Ajax发送json格式数据

    '''
    前后端传输数据的时候一定要确保编码格式跟数据真正的格式是一致的 
    
    {"username":"ycc","age":18}  
    	在request.POST里面肯定找不到
    
    	django针对json格式的数据 不会做任何的处理
    
    request对象方法补充
    request.is_ajax()
    	判断当前请求是否是ajax请求 返回布尔值
    '''
    <script>
        $('#d1').click(function () {
            $.ajax({
                url:'',
                type:'post',
                data:JSON.stringify({'username':'ycc','age':18}),
                contentType:'application/json',//指定编码格式
                success:function () {
                    
                }
            })
        })
    </script>
    
    
    def index(request):
        return render(request,'index.html')
    
    import json
    def ab_json(request):
        if request.is_ajax():  # 判断当前是否是Ajax请求
            print(request.is_ajax())  # True
            print(request.body)  # b'{"username":"ycc","age":18}'
            # 针对json格式数据需要自己手动处理
            json_bytes = request.body
    
            # json_str = json_bytes.decode('utf8')
            # print(json_str,type(json_str))  # {"username":"ycc","age":18} <class 'str'>
            # json_dict = json.loads(json_str)
            # print(json_dict,type(json_dict))  # {'username': 'ycc', 'age': 18} <class 'dict'>
    
            # json.loads括号内如果传入了一个二进制格式的数据那么内部自动解码再反序列化
            json_dict = json.loads(json_bytes)  # 内部原理其实就是上面的两步先解码再反序列化
        return render(request,'ab_json.html')
    
    '''
    ajax发送json格式数据需要注意的点
    	1 contentType参数指定成:application
    	2 数据是真正的json格式数据
    	3 django后端不会帮你处理json格式数据需要你自己去request.body获取并处理
    '''
    
  • Ajax发送文件数据

    '''
    ajax发送文件需要借助于js内置对象FormData
    '''
    <body>
    <p>username:<input type="text" id="d1"></p>
    <p>password:<input type="text" id="d2"></p>
    <p><input type="file" id="d3"></p>
    <button class="btn btn-danger" id="d4">点我</button>
    <script>
        // 给按钮绑定一个点击事件
        $('#d4').click(function () {
            // 1 需要利用FormData内置对象
            let formDataObj = new FormData()
            // 2 添加普通的键值对
            formDataObj.append('username',$('#d1').val())
            formDataObj.append('password',$('#d2').val())
            // 3 添加文件对象
            formDataObj.append('files',$('#d3')[0].files[0])
            // 4 将对象基于ajax发送给后端
            $.ajax({
                url:'', //默认朝当前地址提交
                type:'post',
                data:formDataObj, //直接将对象放在data后面即可
    
                // ajax发送文件必须要指定两个参数
                contentType:false, 
                //不需要使用任便编码 django后端能自动识别formData对象并且能够将内部的普通的键值对自动解析并封装到request.POST中 文件数据自动解析并封装到request.FILES中
                processData:false, // 告诉浏览器不要对数据进行任何处理
                success:function (args) {
    
                }
    
            })
        })
    </script>
    </body>
    
    def ab_file(request):
        if request.is_ajax():
            if request.method == 'POST':
                print(request.POST)
                print(request.FILES)
        return render(request,'ab_file.html')
    
  • Ajax结合sweetalert实现删除按钮的二次确认操作

    # user_list.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
        <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <link rel="stylesheet" href="font-awesome-4.7.0/css/font-awesome.min.css">
        <style>
            div.sweet-alert h2 {
                padding-top: 10px;
            }
        </style>
        {% load static %}
         <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
        <script type="text/javascript" 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 table-striped 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.gender }}</td>
                                <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').click(function () {
            // 先将当前标签对象存储起来
            let currentBtn = $(this);
            // 二次确认弹框
            {#alert($(this).attr('delete_id'))#}
            swal({
              title: "Are you sure?",
              text: "You will not be able to recover this imaginary file!",
              type: "warning",
              showCancelButton: true,
              confirmButtonClass: "btn-danger",
              confirmButtonText: "Yes, delete it!",
              cancelButtonText: "No, cancel plx!",
              closeOnConfirm: false,
              closeOnCancel: false,
              showLoaderOnConfirm: true,
            },
            function(isConfirm) {
              if (isConfirm) {
                  // 朝后端发送ajax请求删除数据之后 再弹下面的提示框
                  $.ajax({
                      {#url:'/delete/user/' + currentBtn.attr('delete_id'),  // 传递主键方式一:直接拼接#}
                      url:'/delete/user/',  // 传递主键方式二:放在请求体里
                      type:'post',
                      data:{'delete_id':currentBtn.attr('delete_id')},
                      success:function (args) {  // args = {'code':'','msg':''}
                          // 判断响应状态码 然后做不同的处理
                          if (args) {
                              swal("删了!", args.msg, "success");
                              // 方式一:lowb版本 直接刷新当前页面 缺点:刷新完之后 如果有分页 会直接调转到第一页
                              {#window.location.reload()#}
                              // 方式二:利用DOM操作 动态刷新  页面没刷新
                              currentBtn.parent().parent().remove()
                          }else {
                              swal('完了','出现了未知的错误','info')
                          }
                      }
                  })
    
              } else {
                swal("Cancelled", "Your imaginary file is safe :)", "error");
              }
            });
        })
    </script>
    
    </body>
    </html>
                
                
    # urls.py
    # 用户展示页面
        url(r'^user/list/',views.user_list),
        url(r'^delete/user',views.delete_user)
                
    # views.py
    import time
    def delete_user(request):
        '''
        前后端再用ajax进行交互的时候 后端通常给ajax的回调函数返回一个字典格式的数据
        :param request:
        :return:
        '''
        if request.is_ajax():
            if request.method == 'POST':
                back_dic = {'code':1000,'msg':''}  # code:自定义状态码
                time.sleep(3)  # 模拟操作数据的延迟
                delete_id = request.POST.get('delete_id')
                models.User.objects.filter(pk=delete_id).delete()
                back_dic['msg'] = '数据已经删了,你赶紧跑路'
                # 我们需要告诉前端我们操作的结果
                return JsonResponse(back_dic)
    
  • django自带的序列化功能(了解 后面路飞会有更加牛逼的序列化drf)

    # 需求:在前端获取到后端用户表里面的所有数据 并要要是列表套字典
    # models.py
    from django.db import models
    
    
    # Create your models here.
    
    class User(models.Model):
        username = models.CharField(max_length=32)
        age = models.IntegerField()
        gender_choices = (
            (1, '男'),
            (2, '女'),
            (3, '其他'),
        )
        gender = models.IntegerField(choices=gender_choices)
        
        
    # views.py
    import json
    from django.http import JsonResponse
    from django.core import serializers
    def ab_ser(request):
        user_queryset = models.User.objects.all()
        # **************************************************************************************
        # 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,json_dumps_params={'ensure_ascii':False},safe=False)
        # 以上代码虽然能能实现分离 但是需要手动建很多字段,所以django再带了序列化组件
        # **************************************************************************************
        # return render(request,'ab_ser.html',locals())
    
    # [{"pk": 1, "username": "jason", "age": 18, "gender": "男"},
    # {"pk": 2, "username": "egon", "age": 19, "gender": "女"},
    #  {"pk": 3, "username": "kevin", "age": 20, "gender": "其他"},
    #  {"pk": 4, "username": "owen", "age": 21, "gender": 4}]
    #  前后端分离的项目
    #     作为后端开发的我们 只需要写代码将数据处理好
    #     能够序列化返回给前端即可
    #         再写一个接口文档 告诉前端每个字段代表的意思即可
    
        # 序列化
        res = serializers.serialize('json',user_queryset)
        '''会自动帮你将数据编程json格式的字符串 并且内部非常的全面'''
        return HttpResponse(res)
    
    '''
    [{"model": "app01.user", 
    "pk": 1, 
    "fields": {"username": "jason", "age": 18, "gender": 1}},
    
     {"model": "app01.user", 
     "pk": 2, 
     "fields": {"username": "egon", "age": 19, "gender": 2}}, 
     
     {"model": "app01.user",
      "pk": 3, 
      "fields": {"username": "kevin", "age": 20, "gender": 3}}, 
      
      {"model": "app01.user", 
      "pk": 4,
       "fields": {"username": "owen", "age": 21, "gender": 4}}]
    写接口就是利用序列化组件渲染数据然后写一个接口文档 该交代的交代一下就完事了
    '''
    
  • 批量插入

    {% for book_obj in book_queryset %}
        <p>{{ book_obj.title }}</p>
    {% endfor %}
    
    
    def ab_pl(request):
        # 先给book插入一万条数据
        # for i in range(1000):
        #     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能够大大的减少操作时间
        '''
    
  • 分页器(只需要掌握分页器的推导思路即可)

    '''
    在制作页码个数的时候 一般情况下都是技术个 符合中国人对称美的标准
    '''
    <body>
    {% for book_obj in book_queryset %}
        <p>{{ book_obj.title }}</p>
        <nav aria-label="Page navigation">
    
    </nav>
    
    {% endfor %}
    <ul class="pagination">
        <li>
          <a href="#" aria-label="Previous">
            <span aria-hidden="true">&laquo;</span>
          </a>
        </li>
        {{ page_html|safe }}
        <li>
          <a href="#" aria-label="Next">
            <span aria-hidden="true">&raquo;</span>
          </a>
        </li>
      </ul>
    
    </body>
    
    def ab_pl(request):
        # 分页
        book_list = models.Book.objects.all()
        # 想访问哪一页
        current_page = request.GET.get('page',1)  # 如果获取不到当前页面 就展示第一页
        # 数据类型转换
        try:
            current_page = int(current_page)
        except Exception:
            current_page = 1
        # 每页展示多少条
        per_page_num = 10
        # 起始位置
        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)
        if more:
            page_count += 1
    
        page_html = ''
        xxx = current_page
        if current_page < 6:
            current_page = 6
        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]  # queryset对象是直接切片操作的
        return render(request,'ab_pl.html',locals())
    
    
    """
    per_page_num = 10
    current_page                start_page                  end_page
        1                           0                           10
        2                           10                          20
        3                           20                          30
        4                           30                          40
    
    
    per_page_num = 5
    current_page                start_page                  end_page
        1                           0                           5
        2                           5                           10
        3                           10                          15
        4                           15                          20
    
    start_page = (current_page - 1) * per_page_num
    end_page = current_page * per_page_num
    """
    
    '''
    django中自带的分页器模块 但是书写起来很麻烦并且功能太简单
    所以我们自己想法设法的写自定义分页器
    
    上述推到代码无需掌握 只需知道内部逻辑即可
    
    基于上述的思路 已经封装好了我们自己的自定义分页器
    之后需要使用直接拷贝即可
    '''
    
  • 自定义分页器的拷贝与使用(简单几行代码即可 需要掌握)

    '''
    当我们需要使用到非django内置的第三方功能或者组件代码的时候
    我们一般情况下会创建一个名为utils文件夹 在该文件夹内对模块进行功能性划分
    	utils可以在全局创建 也可以在每个应用下创建 具体结合实际情况
    
    我们自定义的分页器是基于bootstrap样式来的 所以你需要提前导入bootstrap
    '''
    后端
    book_queryset = models.Book.objects.all()
    current_page = request.GET.get('page',1)
    all_count = book_queryset.count()
    # 1 实例化传值 生成对象
    page_obj = Pagination(current_page=current_page,all_count=all_count)
    # 2 直接对总数居进行切片操作
    page_queryset = book_queryset[page_obj.start:page_obj.end]
    
    前端
    {% for book_obj in page_queryset %}
        <p>{{ book_obj.title }}</p>
        <nav aria-label="Page navigation">
    </nav>
    {% endfor %}
    {#利用自定义分页器直接显示分页器样式#}
    {{ page_obj.page_html|safe }}
        
    分页器
        class Pagination(object):
        def __init__(self, current_page, all_count, per_page_num=11, 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)
    
posted @ 2021-08-30 17:06  Aisa  阅读(49)  评论(0)    收藏  举报