Django ajax的简单使用、自定义分页器

时间:2019-06-15
本文章向大家介绍Django ajax的简单使用、自定义分页器,主要包括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')
对应视图函数

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

   先在数据库中创建多条数据,然后展示在页面上,这里以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())

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

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())

原文地址:https://www.cnblogs.com/maoruqiang/p/11025979.html