zoukankan      html  css  js  c++  java
  • Django ajax的简单使用、自定义分页器

    一. ajax初识

    1. 前后端传输数据编码格式contentType

      使用form表单向后端提交数据时,必须将form表单的method由默认的get改为post,如果提交的数据中包含文件,还要将form表单的enctype由默认的"application/x-www-form-urlencoded"修改为"multipart/form-data"。

      我们可以通过谷歌浏览器-》检查 中的Network查看网络请求的详细信息。

      以form表单为例,其中代码如下(用Bootstrap装饰了一下):

      输入用户名密码,然后随便选一个文件点提交:

      点击view source查看原生数据:

      随后发现后端能拿到文件,不过只是文件名而已:

      随后修改将enctype修改为"multipart/form-data",然后再次提交该文件:

      

      此时原生数据中file看不到了,不过后端可以看到request.FILES中收到了真实的文件,使用GET可以拿到对应的文件对象。

    2. 小结

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

      1. application/x-www-form-urlencoded

    • 对应的数据格式:name=jason&password=666&myfile=test.py
    • 后端获取数据:request.POST

      2. multipart/form-data

    • 对应的数据格式:name=jason&password=666
    • 后端获取文件格式数据:request.FILES
    • 后端获取普通键值对数据:request.POST

       注意:django会将urlencoded编码的数据解析自动放到request.POST,即使修改了编码格式,只要有其中有普通的键值对,都能通过request.POST取到数据。

    二. ajax的简单使用

      前端朝后端发送请求的方式有四种:

    1. 浏览器窗口手动输入网址(get请求)
    2. a标签的href属性(get请求)
    3. form表单(get、post请求,默认是get请求)
    4. ajax(get、post请求)

      前面三种我们都已经接触过了,接下来来看看第四种ajax。

    • AJAXAsynchronous Javascript And XML)翻译成中文就是异步的JavascriptXML”。即使用Javascript语言与服务器进行异步交互(客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。),传输的数据为XML(当然,传输的数据不只是XML)。
    • AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
    • AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
    • AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

    2.1 ajax的基本语法

      ajax主要由四个部分组成:

    $('#d1').click(function () {
            $.ajax({
                // 提交的地址,不写默认提交至当前页面,同form表单的action
                url:'/index/',
                // 提交的方式
                type:'post',
                // 提交的数据,一般以键值对的形式出现
                data:{'name':'jason','password':'123'},
                // 回调函数
                success:function (data) {  // data接收的就是异步提交返回的结果
                    alert(data)
                }
            })
        })

       注意:ajax传输数据的默认编码格式也是urlencoded。前后端传输数据时,数据格式与编码要一一对应,比如传输文件就要将编码改为formdata。

    2.2 ajax实现前端数字相加

      假设有三个input框,需求是前两个input框输入数字,点击提交按钮后将结果显示在第三个input中,中途页面不刷新且加法运算要通过后端实现。

      大致思路如下:

    1. 拿到input框中输入的数字
    2. 将数字传至后端进行运算
    3. 后端将运算的结果返回给前端
    4. 前端将结果展示在第三个input框中

      首先假设用户输入的均为数字,所以我们不做任何检验。然后ajax是异步JavaScript和XML,JQuery内部封装了JavaScript,我们这里使用JQuery的语法来获取input框中输入内容(JQuery_obj.val()),然后使用ajax的回调函数seccess来将加法运算的结果显示在第三个input框中(JQuery_obj.val(result))。

      代码如下(前端未做任何装饰,可能有点丑):

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>test1</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <input type="text" id="s1">+<input type="text" id="s2">=<input type="text" id="s3">
    <button id="b1">提交</button>
    
    <script>
        $('#b1').on('click', function () {
            $.ajax({
                url: '/test1/', //不写默认提交至当前页面
                type: 'post',
                data:{'s1':$('#s1').val(),'s2':$('#s2').val()},
                success:function (data) {
                    $('#s3').val(data)
                }
            })
        })
    </script>
    </body>
    </html>
    html代码
    def test1(request):
        if request.method == 'POST':
            print(request.POST)
            s1 = request.POST.get('s1')
            s2 = request.POST.get('s2')
            # 拿到的前端数据均为字符串格式,所以需要类型转换
            res = int(s1) + int(s2)
            return HttpResponse(res)
        return render(request, 'test1.html')
    相应的视图函数

    2.2 ajax传输JSON数据

      各编程语言与前端数据传输通常使用json格式,因为json支持多种语言,各编程语言都有相应的json语法。在python中是使用json.dumps()和json.loads()分别实现对象的序列化和反序列化。而JavaScript中是使用JSON.stringify()和JSON.parse()分别实现对象的序列化和反序列化。

      向后端传输JSON数据时,需要修改编码类型,不然会出现以下情况:

      以上情况的出现是因为编码与数据格式不匹配造成的,你传输的是JSON格式字符串,而编码urlencode却让后端拿到的是字典,所以就直接把你的JSON字符串当初字典的key,value拿个空来自己造一个字典出来了。

       为了统一编码和数据格式,需要将编码设置为‘application/json’。

      代码如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>test1</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <button id="b1">提交</button>
    <script>
        $('#b1').on('click', function () {
            $.ajax({
                url:'',
                type:'post',
                data:JSON.stringify({'name': 'json', 'password': '123'}),
                contentType:'application/json',
                success:function (data) {
                    alert(data)
                }
            })
        })
    </script>
    </body>
    </html>
    html代码
    def test1(request):
        if request.method == 'POST':
            import json
            # 编码改成application/json之后,传输过来的是二进制数据,存在request.body中
            dic = json.loads(request.body.decode('utf-8'))
            print(dic, type(dic))
            return HttpResponse('get it')
        return render(request, 'test1.html')
    视图函数

     2.3 ajax传输文件

      传输文件相比传输json字符串要复杂一些,首先要想办法把用户上传的文件取出来,这需要用到JavaScript中的FormData对象的方法,其次还是要统一数据格式与编码,将编码改为false(因为formdata内部有自带一个编码)。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>test1</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    file:<input type="file" name="myfile" id="f1">
    <button id="b1">提交</button>
    <script>
      $('#b1').on('click', function () {
          let formdata = new FormData();//生成一个FormData对象
          //$('#f1')[0]得到的是JQuery对象中的第一个JS对象,JS_obj.files拿到所有的文件,通过索引取第一个文件
          formdata.append('file', $('#f1')[0].files[0]);
          $.ajax({
              url:'',
              type:'post',
              contentType:false,//用FormData自带的编码,所以不需要设置contentType
              processData:false,//告诉浏览器不要处理数据
              data: formdata,//直接将formdata提交至后端即可
              success:function (data) {
                  alert(data)
              }
          })
      })
    </script>
    </body>
    </html>
    html代码
    def test1(request):
        if request.method == 'POST':
            # 没有普通键值对,所以request.POST是空
            print(request.POST)
            print(request.FILES)
            # request.FIlES.get('file')拿出来的是文件对象
            file_obj = request.FILES.get('file')
            # 保存文件至本地
            with open(file_obj.name, 'wb') as f:
                for line in file_obj:
                    f.write(line)
            return HttpResponse('get it')
        return render(request, 'test1.html')
    对应视图函数

     2.4 ajax小试验

      初次接触ajax,对ajax理解只是一点点皮毛,今天受一个朋友影响,对ajax的理解加深了一点点,在此感谢CC teacher。

      接下来说的方法可能有点鸡肋,可是又有点意思。拿图书管理系统举例,点击添加作者,直接在当前页面局部刷新来实现,不用标签的隐藏等各种方法,就只用ajax来实现(就是那么头铁)。

      大致思路:

    1. 前端页面展示作者数据的区域用一个div标签包裹
    2. 点击添加按钮,利用click事件和ajax,从后端获取一个用render渲染过的HTML页面(该页面需要啥就写啥,不需要body和head标签,同模板导入与自定义inclusion_tag一样)。
    3. 将获取的页面data通过JQ语法.html(data)显示在div中(这时原本显示作者数据的区域数据就会全被获取的页面覆盖了)。
    {% extends 'home.html' %}
    {% block content %}
    
        <div id="d1">
            <button id="i1" class="btn btn-info">添加</button>
            <table class="table table-hover table-striped table-bordered">
                <thead>
                <tr>
                    <th>id</th>
                    <th>name</th>
                    <th>age</th>
                    <th>gender</th>
                    <th>phone</th>
                    <th>addr</th>
                    <th>action</th>
                </tr>
                </thead>
                <tbody>
    
                {% for author in author_list %}
                    <tr>
                        <td>{{ author.pk }}</td>
                        <td>{{ author.name }}</td>
                        <td>{{ author.age }}</td>
                        <td>{{ author.get_gender_display }}</td>
                        <td>{{ author.authordetail.phone }}</td>
                        <td>{{ author.authordetail.addr }}</td>
                        <td>
                            <a href="{% url 'author_edit' author.pk %}" class="btn btn-success">编辑</a>
                            <a href="{% url 'author_delete' author.pk %}" class="btn btn-danger">删除</a>
                        </td>
                    </tr>
                {% endfor %}
    
                </tbody>
            </table>
        </div>
    {% endblock %}
    
    {% block js %}
        <script>
            $('#i1').click(function () {
                $.ajax({
                    url: '{% url "author_add" %}',
                    type: 'post',
                    data: {'type': '333'},
                    success: function (data) {
                        //将id为d1的div标签中的html换成data变量中存的数据
                        $('#d1').html(data)
                    }
                })
            })
        </script>
    {% endblock %}
    大致代码

      form表单和ajax是可以一起使用的,不过这样感觉没什么意义,试验了一下,并没有出现网上说的错误。ajax绑定form表单中的button按钮及submit按钮,两者效果一样,后端先获取到ajax提交的数据,然后再获取到from表单中的数据,有兴趣可以自行试验。

      注意:虽然ajax是异步提交(GitHub注册示例)、局部刷新,但是并不是所有ajax使用的越多越好,因为ajax异步的回调函数会向后端询问执行的结果,当同时有很多该请求时,服务端会有很大的负担。

    三. 批量插入,分页器的简单实现

       先在数据库中创建多条数据,然后展示在页面上,这里以1000条为例。

    def test1(request):
        for i in range(100):
            models.Book2.objects.create(name='第%s本' % i)
        book_list = models.Book2.objects.all()
        return render(request, 'test1.html', locals())

      以上这么增加数据时,发现要在页面等一段时间才会有数据显示,因为写入数据库要时间,前端只能等待数据写入数据库结束。这种情况需要用到批量插入bulk_create:

    def test1(request):
        # 定义一个列表
        book_list = []
        for i in range(100):
            # 实例化出Book2的对象,并将其加入列表
            book_list.append(models.Book2(name='第%s本' % i))
        # 这就是批量导入的精髓,相当于异步,程序执行无需等待该代码执行完毕,可直接去执行后续代码
        models.Book2.objects.bulk_create(book_list)
        # 随后前端直接可以使用book_list中的书籍对象点属性去展示内容,不需要等数据库中数据写入完毕
        return render(request, 'test1.html', locals())

      上述使用的是列表,但是当列表中数据很多时,会占用很多的内存,可以采用生成器的方式来进行优化:g = (models.book(name = '第%s本' % i for i in range(10000)))。

      分页器是需要考虑一共有几页的,这需要依据数据总条数来定,其次分页器每一次只显示几个按钮,这就意味着我们是没法在前端来完成这个动态的过程的,只能在后端完成,这时候需要用到后端或者前端的取消转义语法。

    html = ''
    for i in range(1,pager_nums+1):
    html += '<li><a href="?page=%s">%s</a></li>'%(i,i)

      最终代码如下:

    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:  最多显示的页码个数
    
            用法:
            queryset = model.objects.all()
            page_obj = Pagination(current_page,all_count)
            page_data = queryset[page_obj.start:page_obj.end]
            获取数据用page_data而不再使用原始的queryset
            获取前端分页样式用page_obj.page_html
            """
            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的文件夹,里面创建一个.py文件,将该代码拷贝进去保存。然后将该py文件导入到views.py中。

    from django.shortcuts import render,HttpResponse
    from app01 import models
    # 导入拷贝了上述分页器代码的py文件
    from app01.utils import my_page
    
    def booklist(request):
        book_list = models.Book2.objects.all()
        # 拿到数据总条数
        all_count = book_list.count()
        # 得到当前页面,前端的分页器被点击时返回page,标明用户点击的页编号
        current_page = request.GET.get('page',1)
        page_obj = my_page.Pagination(current_page=current_page,all_count=all_count)
        # 将总数据按用户点击的页编号切片
        page_queryset = book_list[page_obj.start:page_obj.end]
        return render(request,'booklist.html',locals())
  • 相关阅读:
    jQuery笔记(1)
    [bzoj 1878][SDOI2009]HH的项链
    [bzoj 1968][Ahoi2005]COMMON 约数研究
    [bzoj 1899][ZJOI2004]lunch 午餐
    [bzoj 1090][SCOI2003]字符串折叠
    CodeForces 1029E div3
    [bzoj 1270][BeijingWc2008]雷涛的小猫
    [bzoj 1260][CQOI 2007]涂色paint
    [AtCoder ARC101D/ABC107D] Median of Medians
    [luogu 1070]道路游戏(NOIP2009T4)
  • 原文地址:https://www.cnblogs.com/maoruqiang/p/11025979.html
Copyright © 2011-2022 走看看