Django-Ajax
一、Ajax基本用法
1.简介
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
a.同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
b.异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
AJAX除了异步的特点外,还有一个就是:浏览器页面局部刷新;(这一特点给用户的感受是在不知不觉中完成请求和响应过程
2.示例
html页面内容:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% csrf_token %} 用户名:<input type="text" id="i1" name="usename"> <input type="button" value="提交" id="but"> <span class="s1"></span> </body> <script src="{% static "jquery-3.4.1.js" %}"></script> <script> $("#but").click(function () { var name = $("#i1").val(); var csrf_data = $('[name=csrfmiddlewaretoken]').val(); $.ajax( { url:"{% url 'login' %}", type:"post", data:{ name:name, csrfmiddlewaretoken:csrf_data, }, success:function (data) { if (data==="123"){ alert("登陆成功") }else { $(".s1").text("用户名或密码错误!!!") } } } ) }) </script> </html>
views内容:
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from app01 import models def index(request): return render(request,"index.html") def login(request): print(request.POST) data = request.POST.get("name") print(data) # <QueryDict: {'name': ['123'], 'csrfmiddlewaretoken': ['8lbAt5pr33TaApmK1I8UVUNgiY62uAp4qJoBjdH8oSt5XNe2eMd9XUd3daFKV8A9']}> return HttpResponse("123")
urls内容:
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index/', views.index,name="index"), url(r'^login/', views.login,name="login"), ]
3.AJAX常见应用情景
搜索引擎根据用户输入的关键字,自动提示检索关键字。
还有一个很重要的应用场景就是注册时候的用户名的查重。
其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。
a.整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
b.当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!
当输入用户名后,把光标移动到其他表单项上时,浏览器会使用AJAX技术向服务器发出请求,服务器会查询名为lemontree7777777的用户是否存在,最终服务器返回true表示名为lemontree7777777的用户已经存在了,浏览器在得到结果后显示“用户名已被注册!”。
a.整个过程中页面没有刷新,只是局部刷新了;
b.在请求发出后,浏览器不用等待服务器响应结果就可以进行其他操作;
二、上传文件
1、基于from表单上传文件
URLS:
from app01 import views urlpatterns = [ url(r'^upload/', views.upload,name="upload"), ]
HTML:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="{% url "upload" %}" method="post" enctype="multipart/form-data"> {% csrf_token %} 用户名:<input type="text" name="username"> 头像:<input type="file" name="file"> <input type="submit" value="提交" id="sub"> </form> </body> <script src="{% static "jquery-3.4.1.js" %}"></script> </html>
views:
from django.shortcuts import render,HttpResponse,redirect # Create your views here. def upload(request): if request.method=="GET": return render(request,"upload.html") else: username = request.POST.get("username") # 获取用户输入的内容 file_obj = request.FILES.get("file") # 获得文件数据对象 print(username,file_obj.name) # 获取文件名字 file_name = file_obj.name with open(file_name,"wb") as f: # for data in file_obj: #读取数据 # f.write(data) #每次读取的data不是固定长度的,和读取其他文件一样,每次读一行,识别符为\r \n \r\n,遇到这几个符号就算是读了一行 for data in file_obj.chunks(): #chunks()默认一次返回大小为经测试为65536B,也就是64KB,最大为2.5M,是一个生成器 f.write(data) return HttpResponse("ok")
2、基于Ajax的文件上传
HTML:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <!--只是一个盒子,用不用都行--> {% csrf_token %} 用户名:<input type="text" name="username"> 头像:<input type="file" name="file"> <input type="button" value="提交" id="sub"> </div> </body> <script src="{% static "jquery-3.4.1.js" %}"></script> <script> $("#sub").click(function () { // ajax上传文件的时候,需要这个类型,它会将添加给它的键值对加工成formdata的类型 var formdata=new FormData(); // 添加键值的方法是append,注意写法,键和值之间是逗号 formdata.append("name",$("[type=text]").val()); // 转换成DOM对象 formdata.append("file",$("[type=file]")[0].files[0]); // 别忘了csrf_token formdata.append("csrfmiddlewaretoken",$("[name=csrfmiddlewaretoken]").val()); $.ajax({ url:{% url "upload" %}, type:"post", data:formdata, // 将添加好数据的formdata放到data这里 processData: false , // 不处理数据 contentType: false, // 不设置内容类型 success:function (response) { console.log(response) } } ) }) </script> </html>
其他代码同上:
注意:由于 FormData 是 XMLHttpRequest Level 2 新增的接口,现在 低于IE10 的IE浏览器不支持 FormData。
三、请求头Content Type
ContentType指的是请求体的编码类型,常见的类型共有3种:
1、第一种请求方式:application/x-www-form-urlencoded
enctype
属性,那么最终就会以 默认格式application/x-www-form-urlencoded 方式提交数据,ajax默认也是这个。请求类似于下面这样数据是以&符连接的:上面这种contenttype规定的数据格式,后端对应这个格式来解析获取数据,不管是get方法还是post方法,都是这样拼接数据,大家公认的一种数据格式,但是如果你contenttype指定的是urlencoded类型,但是post请求体里面的数据是下面那种json的格式,那么就出错了,服务端没法解开数据。
2、第二种请求方式:multipart/form-data
这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 <form> 表单的 enctype
等于 multipart/form-data,form表单不支持发json类型的contenttype格式的数据,而ajax什么格式都可以发,也是ajax应用广泛的一个原因。直接来看一个请求示例:(了解)
这个例子稍微复杂点。首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary
开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary--
标示结束。关于 multipart/form-data 的详细定义,请前往 rfc1867 查看。
这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。
上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 <form> 表单也只支持这两种方式(通过 <form> 元素的 enctype
属性指定,默认为 application/x-www-form-urlencoded
。其实 enctype
还支持 text/plain
,不过用得非常少)。
随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax 进行数据交互之后,我们完全可以定义新的数据提交方式,给开发带来更多便利。
3、第三种请求方式:application/json
客户端如果指定以json作为请求体的编码类型,那么服务端在接收后也要以对应的数据格式进行解码:{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> 用户名:<input type="text" name="username"> 密码:<input type="password" name="password"> <input type="button" value="提交" id="btn"> </div> </body> <script src="{% static "jquery-3.4.1.js" %}"></script> <script> $("#btn").click(function () { $.ajax({ url:{% url "index" %}, type:"post", headers:{'Content-Type':'application/json'}, // 指定数据编码格式 data:JSON.stringify({ // 对数据进行序列化 name:$("[name=usename]").val(), pwd:$("[name=password]").val(), }), success:function (response) { console.log(typeof response); // python序列化后的json字符串 var dic = JSON.parse(response); // 需要进行反序列化才可以用 console.log(dic.info) // 直接可以调用对应的属性 } }) }) </script> </html>
视图函数:
import json def index(request): if request.method=="GET": return render(request,"index.html") else: html_json = request.body # 取出原数据,在对数据进行反序列化 py_str = json.loads(html_json) # 反序列化成字典,在处理 print(py_str) # {'pwd': '123'} res_dic = {"status":0,"info":"响应信息"} str_dic = json.dumps(res_dic) # 将字典序列化成json字符串 return HttpResponse(str_dic) # 发送给ajax的response
import json def index(request): if request.method=="GET": return render(request,"index.html") else: html_json = request.body # 取出原数据,在对数据进行反序列化 py_str = json.loads(html_json) # 反序列化成字典,在处理 print(py_str) # {'pwd': '123'} res_dic = {"status":0,"info":"响应信息"} str_dic = json.dumps(res_dic) # 指定数据编码格式相当于告诉浏览器按照这个格式进行解码 return HttpResponse(str_dic,content_type="application/json")
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> 用户名:<input type="text" name="username"> 密码:<input type="password" name="password"> <input type="button" value="提交" id="btn"> </div> </body> <script src="{% static "jquery-3.4.1.js" %}"></script> <script> $("#btn").click(function () { $.ajax({ url:{% url "index" %}, type:"post", headers:{'Content-Type':'application/json'}, // 指定数据编码格式 data:JSON.stringify({ // 对数据进行序列化 name:$("[name=usename]").val(), pwd:$("[name=password]").val(), }), success:function (response) { console.log(response.info); //拿到json数据类型的数据,根据服务器指定的数据编码格式调用内部的解析器进行解码 }) }) </script> </html>
注意JsonResponse默认只处理字典类型的数据,如果想要返回其他数据类型:
总结:
form表单不支持json数据格式的传输 django不支持json数据格式的解析,需要我们自定义解析器
import json dic={"chao":"123"} dic_en = json.dumps(dic) print(dic_en,type(dic_en)) aa = dic_en.encode("utf-8") print(aa) bb = json.loads(aa) print(bb,type(bb)) """ {"chao": "123"} <class 'str'> b'{"chao": "123"}' {'chao': '123'} <class 'dict'> """ # bytes类型的字典,可以直接loads成字典,不需要decode
四、序列化models对象:
from django.urls import reverse book_objs = models.Book.objects.all().values('title','price') # django内部序列化方法 res = serializers.serialize('json', book_objs) # 建议用下面这种方式序列化,强转数据类型 books = list(book_objs)
通过json序列化时间日期格式数据的时候需要注意,不能直接序列化:
import json from datetime import datetime from datetime import date #对含有日期格式数据的json数据进行转换 class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field,datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field,date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self,field) d1 = datetime.now() dd = json.dumps(d1,cls=JsonCustomEncoder) print(dd)
五、Ajax通过csrf_token的几种方式
方式1
通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。
$.ajax({ url: "/cookie_ajax/", type: "POST", data: { "username": "chao", "password": 123456, "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val() // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中 }, success: function (data) { console.log(data); } })
方式2
$.ajaxSetup({ data: {csrfmiddlewaretoken: '{{ csrf_token }}' }, });
方式3
通过获取返回的cookie中的字符串 放置在请求头中发送。
注意:需要引入一个jquery.cookie.js插件。
<script src="{% static 'js/jquery.cookie.js' %}"></script> $.ajax({ headers:{"X-CSRFToken":$.cookie('csrftoken')}, #其实在ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,我们发contenttype类型数据的时候,csrf_token就可以这样加 })
注意:如果我们发送的是json数据格式的数据,那么csrf_token就不能直接写在data里面了,没有效果,必须通过csrf的方式3的形式来写,写在hearders(请求头,可以写一些自定制的请求头)里面,注意,其实contentType也是headers里面的一部分,写在里面外面都可以:
六、Ajax验证登录
HTML:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% csrf_token %} 用户名:<input type="text" id="i1" name="username"> 密码:<input type="text" id="i1" name="password"> <input type="button" value="提交" id="but"> <span class="s1"></span> </body> <script src="{% static "jquery-3.4.1.js" %}"></script> <script> $("#but").click(function () { var username = $("[name=username]").val(); var password = $("[name=password]").val(); var csrf_data = $('[name=csrfmiddlewaretoken]').val(); $.ajax( { url:"{% url 'login' %}", type:"post", data:{ name:username, pwd:password, csrfmiddlewaretoken:csrf_data, }, success:function (data) { if (data==="123"){ alert("登陆成功"); location.href="{% url "index" %}" }else { $(".s1").text("用户名或密码错误!!!") } } } ) }) </script> </html>
视图函数:
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from app01 import models def index(request): return render(request,"index.html") def login(request): if request.method=="GET": return render(request, "login.html") else: name = request.POST.get("name") pwd = request.POST.get("pwd") obj = models.Userinfo.objects.all() # 判断数据库的用户名和用户输入的的是否一致 for info in obj: if name==info.username and pwd==info.password: return HttpResponse("123") else: return render(request,"login.html")
URLS:
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index/', views.index,name="index"), url(r'^login/', views.login,name="login"), ]
数据库用户信息表: