使用form表单向后端提交数据时,必须将form表单的method由默认的get改成post,若是提交的数据中包含文件,还要将form表单的enctype由默认的"application/x-www-form-urlencoded"修改成"multipart/form-data"。css
咱们能够经过谷歌浏览器-》检查 中的Network查看网络请求的详细信息。html
以form表单为例,其中代码以下(用Bootstrap装饰了一下):前端
输入用户名密码,而后随便选一个文件点提交:python
点击view source查看原生数据:jquery
随后发现后端能拿到文件,不过只是文件名而已:ajax
随后修改将enctype修改成"multipart/form-data",而后再次提交该文件:数据库
此时原生数据中file看不到了,不事后端能够看到request.FILES中收到了真实的文件,使用GET能够拿到对应的文件对象。django
先后端传输数据编码格式contentType:编程
1. application/x-www-form-urlencodedjson
2. multipart/form-data
注意:django会将urlencoded编码的数据解析自动放到request.POST,即便修改了编码格式,只要有其中有普通的键值对,都能经过request.POST取到数据。
前端朝后端发送请求的方式有四种:
前面三种咱们都已经接触过了,接下来来看看第四种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。
假设有三个input框,需求是前两个input框输入数字,点击提交按钮后将结果显示在第三个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>
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')
各编程语言与前端数据传输一般使用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>
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')
传输文件相比传输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>
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')
初次接触ajax,对ajax理解只是一点点皮毛,今天受一个朋友影响,对ajax的理解加深了一点点,在此感谢CC teacher。
接下来讲的方法可能有点鸡肋,但是又有点意思。拿图书管理系统举例,点击添加做者,直接在当前页面局部刷新来实现,不用标签的隐藏等各类方法,就只用ajax来实现(就是那么头铁)。
大体思路:
{% 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())