Django学习笔记一十六——AJAX初识
我们在前面的所有的案例中,实现的都是对页面的全部请求,也就是每次请求的时候浏览器从服务器上拿到字符串以后按照协议渲染出来,但是在有些时候页面是在进行一些操作的时候会有部分的响应:比方我们在博客园上注册一个新用户
在我们输入信息的时候,会实时的对输入的信息进行判定。在这个判定的是不是整个页面进行刷新的,这个时候就要用到一个新的概念——AJAX。
通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
在很早以前的一篇文章里大概讲了一下JSON序列化的使用, 我们在全栈的学习中已经重点学习了两种语言:前端的JavaScript和后台的Python,要实现前端和后台的数据交互,我们需要对要交互的数据进行JSON序列化。
什么是JSON
JSON指的是JavaScript对象表示法(JavaScript Object Notation),有以下特性:
- JSON是轻量级的文本交互数据格式
- JSON独立于语言
- JSON具有自我描述性,更易于理解
这其中可以说说的是第二条,虽然JSON使用JS的语法来描述数据对象,但是JSON仍然独立与语言和平台。JSON解析器和JSON库支持许多不同的编程语言。
而在Python和JS中,JSON的关系是下面这样的
JS中的序列化和反序列化
var a = {"name":"aaa","age":18} undefined a {name: "aaa", age: 18} var a_json = JSON.stringify(a) undefined a_json "{"name":"aaa","age":18}" var b = JSON.parse(a_json) undefined b {name: "aaa", age: 18}
就是两行代码:序列化
JSON.stringfy(obj)
反序列化
JSON.parse(json_obj)
记住这两行代码就能完成大部分的数据交互是的操作。
Python中的序列化和反序列化
s = '{"name":"Jack","age":20}' import json s_json = json.dumps(s) #序列化 s_loads = json.loads(s) #反序列化
这里要有个小地方要注意一下:如果s是个JSON序列化以后的数据,内容一定要用双引号,否则会报错!
不合格的JSON对象
虽然JSON序列化常常用于数据的交互,但是还是有许多的数据类型不适合被JSON序列化,比如:
- 未使用双引号的属性名(前面才讲过)
- 十六进制的数据
- underfined类型的数据
- 函数或者日期对象
- 等等
JSON和XML的区别
在JSON被广泛使用之前,XML常常被用于数据的存储、但是JSON的两个显著的特点很快的被大众所接受:书写简单易读,可以有解释引擎直接处理,不用添加额外的代码。
XML和JSON都是采用了结构话的方法来标记数据。我们可以看看同样的一组数据分别使用XML和JSON来表示的区别
<?xml version="1.0" encoding="utf-8"?> <country> <name>中国</name> <province> <name>黑龙江</name> <cities> <city>哈尔滨</city> <city>大庆</city> </cities> </province> <province> <name>广东</name> <cities> <city>广州</city> <city>深圳</city> <city>珠海</city> </cities> </province> <province> <name>台湾</name> <cities> <city>台北</city> <city>高雄</city> </cities> </province> <province> <name>新疆</name> <cities> <city>乌鲁木齐</city> </cities> </province> </country>
{ "name": "中国", "province": [{ "name": "黑龙江", "cities": { "city": ["哈尔滨", "大庆"] } }, { "name": "广东", "cities": { "city": ["广州", "深圳", "珠海"] } }, { "name": "台湾", "cities": { "city": ["台北", "高雄"] } }, { "name": "新疆", "cities": { "city": ["乌鲁木齐"] } }] }
对比两者可以发现,JSON的瑜伽格式和层次结构更加清晰,并且在数据交互的时候,JSON所使用的字符数量明显比XML要少的多,可以节约数据所占用的带宽。
发送请求的方法
我们在前面已经学过了发送请求的集中方式:
- 在地址栏输入URL——GET请求
- 通过a标签——GET请求
- 通过FORM表单——GET或POST请求
上面几种方式都是实现了数据的同步的交互,下面就是今天要讲的第四种发送请求的方法:AJAX。AJAX有下面两个特点:
- 异步
- 浏览器局部刷新(本章一开始举的例子)
就以我们一开始的那个注册的页面来举例,用户输入完用户名,在input标签失去焦点以后,浏览器就会使用AJAX技术向服务器发送一个请求,服务器会查询输入的用户名是否已经存在,返回True表名已经存在了,浏览器就会系那是“用户名已被注册”在这整个过程中,页面是没有刷新的(否则已经输入的内容就需要重新输入),而在请求发出后,浏览器不必等待服务器的响应结果,可以继续进行别的操作(异步请求)。由于服务器响应的不再是整个页面,而是页面中的一部分,所以AJAX的性能要高一些。
AJAX的初识
我们用下面一个案例来演示一下AJAX的效果
views.py def index(request): return render(request,'index.html') def ajax_add(request): i1 = request.GET.get('i1') i2 = request.GET.get('i2') i1= int(i1) i2 = int(i2) ret = i1+i2 return HttpResponse(ret)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>AJAX局部刷新实例</title> </head> <body> <input type="text" id="i1">+ <input type="text" id="i2">= <input type="text" id="i3"> <input type="button" value="AJAX提交" id="b1"> <script src="/static/jquery-3.2.1.min.js"></script> <script> $("#b1").on("click", function () { $.ajax({ url:"/ajax_add/", type:"GET", data:{"i1":$("#i1").val(),"i2":$("#i2").val()}, success:function (data) { $("#i3").val(data); } }) }) </script> </body> </html>
运行以后的效果
仔细看一下,整个页面是没有用FORM表单的,并且点击按钮以后,页面是不会整个刷新的,但是还是能计算出来结果的,先不考虑AJAX是如何工作的,这里先看看效果!
我们先看一看模板中那段JS的代码
<script> $("#b1").on("click", function () { $.ajax({ url:"/ajax_add/", type:"GET", data:{"i1":$("#i1").val(),"i2":$("#i2").val()}, success:function (data) { $("#i3").val(data); } }) }) </script>
整体的过程就是给按钮绑定了一个点击事件,而这个事件就是一个AJAX请求,
我们做一个简单的看一看AJAX请求来看看他的的格式
$.ajax({ url:'/ajax_add/', type:'get', data:{name:"aaa",age:22}, success:function(arg){ alart(arg); } })
url是请求的地址,后端会拿着这个URL去路由里面找对应的视图函数。
比方我们后台的视图是下面这样的(ajax_add在路由中映射的函数)
def ajax_add(request): print(request.GET) return HttpResponse(123)
type就是请求的方法,可以是POST或者是GET
data是请求时候带的参数,后台可以直接拿到这个参数(request.GET)
在视图执行完毕返回值的时候,success就会拿到这个返回值(arg),然后执行后面的函数。
上面就是AJAX的局部刷新的过程
在上面的说明中,我们用的是GET的方法,可是如果是POST请求,就涉及到一个跨站请求的处理,如果我们在中间件的设置中没有注释掉CSRF那一行的话就会报错
说明在POST请求的时候发生了CSRF缺失或不正确的情况。
方法1
首先,我们可以在模板中加获取到页面的CSRF
{%csrf_token%}
CSRF会以一个隐藏的input的形式被加载到页面上
想一下jQuery中怎么通过name来获得标签里的值?
$("[name='csrfmiddlewaretoken']").val()
最后在通过AJAX提交data的时候把这个key和对应的value加载到字典里就可以了,大概就是下面的套路
csrf_str = $("[name='csrfmiddlewaretoken']").val() $("#b2").on("click",function(){ $.ajax({ url:"/ajax_add/", type:"POST", data:{name:"aaa",age:22,csrfmiddlewaretoken:csrf_str}, success:function(arg){ alert(arg) } }) })
但是一定要记得在前面把CSRF对应的input标签放在页面模板里。
这个方法总而言之就是通过获取页面中隐藏的包含CSRF信息的值放在data中发送
方法2
还有一种简单的方法是通过获取返回的cookie中的字符串,放置在请求头中发送。
这个方法有些复杂,需要我们新建一个JS文件,把下面的代码放进去,然后在模板中导入就可以了
function getCookie(name){ var cookieValue = null; if(document.cookie && document.cookie !==''){ var cookies = document.cookie.split(';'); for (var i=0; i<cookies.length;i++){ var cookie = jQuery.trim(cookies[i]); if (cookie.substring(0,name.length +1) === (name + '=')){ cookieValue = decodeURIComponent(cookie.substring(name.length +1)); break; } } } return cookieValue; } csrftoken = getCookie('csrftoken') function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } });
然后在模板里把这个文件(AJAXPOST.JS)导入就可以了
<script src="/static/jquery-3.2.1.min.js"></script> <script src="/static/AJAXPOST.js"></script>
现在我们回过头看一看 一开始那个注册时候显示"用户名已注册"的效果是如何实现的(其他的类似密码复杂度太简单的提示也一样)。这里主要说明效果的实现,数据库和路由系统的设置这里就不再赘述了。
模板里就放一个input标签就行了,利用jQuery里的keypress()事件,在键盘按下的时候利用AJAX发送数据请求给idcheck,判定id是否存在以后进行提示.
为了便于演示,这里用来判定用户名是否存在的方法是判定用户名(必须是int类型),如果是单数则返回用户名未使用,否则显示用户名以存在,如果是连数据库直接用filter过一遍就行了,
模板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div> <input type="text" id="i1"><span id="p1"></span> </div> </body> <script src="/static/jquery-3.2.1.min.js"></script> <script> // var a = $("#i1").val() $('#i1').keyup(function(){ var check_id = $("#i1").val(); $.ajax({ url:'/idcheck/', type:"GET", data:{"id":$('#i1').val()}, success:function(arg){ $('#p1').text(arg); } }) }) </script> </html>
视图
def login(request): return render(request,'login.html') def idcheck(request): check_id = request.GET.get('id') print(type(check_id)) id = int(check_id) if id % 2 == 0: return HttpResponse('用户名已存在') else: return HttpResponse('用户名未使用')
当我们通过url(login)请求的时候,得到一个页面
当我们在input框里输入字符以后,键盘抬起触发事件,事件里包括了一个ajax请求,请求的url为/idcheck/,对应的函数就是idcheck。视图通过GET获得了参数,参数值就是input框里的字符。对字符进行判定后(函数里简化成了直接判定奇偶)。最后返回一个字符串
当我们输入的用户名为奇数时
后面的span标签就显示了用户名的使用状态,而输入的值为偶数时:
span会局部刷新。
这样就实现了前面一开始所说局部刷新的效果。(忽略CSS效果)。
要注意的是事件的选取,一开始我用了keypress事件,但是发送的值是变化以前的值,要用keyup,触发时值已经被修改,请求时发送的是新的值。