Ajax
JavaScript和普通的jQuery都是操作Html标签以及css属性,不能发送接收数据,最多也只是.submit(),
实际发送数据还是Form表单,提交到同一个url,后台view获取数据然后return同一个页面
悄悄发送数据到后台进行处理,并返回结果而页面不刷新,只能通过Ajax实现
一、原生Ajax
Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),
Ajax首次出现IE5.5中存在(ActiveX控件)。
1、XmlHttpRequest对象的主要方法:
a. void open(String method,String url,Boolen async) 用于创建请求 参数: method: 请求方式(字符串类型),如:POST、GET、DELETE... #get方式时,xhr.send(null),数据写在url上 url: 要请求的地址(字符串类型) async: 是否异步(布尔类型) #异步发,不影响任何效果 b. void send(String body) #对比jQuery的data: 用于发送请求 参数: body: 要发送的数据(字符串类型) c. void setRequestHeader(String header,String value) 用于设置请求头 参数: header: 请求头的key(字符串类型) vlaue: 请求头的value(字符串类型) d. String getAllResponseHeaders() 获取所有响应头 返回值: 响应头数据(字符串类型) e. String getResponseHeader(String header) 获取响应头中指定header的值 参数: header: 响应头的key(字符串类型) 返回值: 响应头中指定的header对应的值 f. void abort() 终止请求
终止请求
2、主要属性,如xhr.xxx来使用,不用加括号
a. Number readyState
状态值(整数)
详细:
0-未初始化,尚未调用open()方法,只创建了xml对象;
1-启动,调用了open()方法,未调用send()方法;
2-发送,已经调用了send()方法,未接收到响应;
3-接收,已经接收到部分响应数据;
4-完成,已经接收到全部响应数据;
b. Function onreadystatechange
当readyState的值改变时自动触发执行其对应的函数(回调函数)
这是一个属性,写在open前也可以,只要在创建xhr之后即可
c. String responseText
服务器返回的数据(字符串类型)
d. XmlDocument responseXML
服务器返回的数据(Xml对象)
e. Number states
状态码(整数),如:200、404... 如return HttpResponse(json.dumps(ret),status=404,reason='NOT FOUND')
f. String statesText
状态文本(字符串),如:OK、NotFound...
3、示例:
function Ajax1() { var xhr = getXHR(); #一般创建写法var xhr = new XMLHttpRequest() xhr.onreadystatechange = function(){ #状态码改变时自动执行 if (xhr.readyState == 4){ #状态码 var obj = JSON.parse(xhr.responseText); #转化为json对象,可以理解为字典 console.log(obj); } }; xhr.open('POST','/ajax_json/',true); //xhr.setRequestHeader('k1','v1'); #设置请求头,如csrf xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8'); #POST必须按照格式设置文件解析请求头,GET不用设置直接发 xhr.send('name=root;pwd=123'); #发送数据
4、浏览器兼容问题:
function getXHR(){ var xhr = null; if(XMLHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveXObject("Microsoft.XMLHTTP"); } return xhr; }
二、jQuery Ajax
内部调用原生XmlHttpRequest,一种浏览器对象,低版本IE没有,有替代的对象active object,jQuery可以用这个替代xml
jQuery 2.x 3.x不支持active object,而1.x支持,兼容性好
1、前端写法:
$('#ajax_submit').click(function () { $.ajax({ 开始语句 url: "/test_ajax/", 提交到哪个url type: 'POST', 提交方式 data: {'k': 'v', 'list': [1,2,3,4], 'k3': JSON.stringfy({'k1': 'v'}))}, $(form对象).serilize() # 字典不能嵌套字典,可以将字典stringfy传过去 traditional:true, # 提交的data中value包括数组时要声明 // dataType:'JSON', # 后台发送的字符串自动转换为json对象,text\html不作处理,xml能转就转这格式 // data:$('#add_form').serialize(), 封装提交更方便 success: function (data) { 提交成功后执行函数 // JSON.parse(data); #将字符串转为对象,转字符串是stringfy(),对应前端json.dumps\loads #声明dataType:'JSON'后,就不用转换了 if(data == 'ok'){ location.reload() # 重新加载 location.href = "某个地址" # 跳转,只能在这跳转,不能在视图函数中redirect } else{ alert(data); } }, error:function(){ #网络异常,url错误等 } }); }) 此外还有: $.get(url='',data={},success=) $.post() #内部都是调用ajax方法
2、Django序列化与Ajax
通过Ajax发送数据,在返回时,只能返回字符串,故进行序列化
Django发送:
a.HttpResponse返回字符串时,无需序列化与反序列化 HttpResponse('ok') b.返回render渲染模板后生成的字符串,也无需序列化与反序列化 user_list = models.UserInfo.objects.all() return render(request,'get_data.html',{'user_list':user_list}) 注:不能return redirect,ajax不受理;而render返回的是渲染好数据的字符串,不用再dumps和parse c.返回一个普通的字典,定义一个字典用于返回,统一,判断时修改其中的value ret = {'status':True,'error':None,'data':{}} return HttpResponse(json.dumps(ret)) #只能返回字符串,用dumps序列化为形式字典 注:python中序列化与反序列化是json.dumps\loads(),JavaScript中是JSON.stringfy\parse() 也可以返回JsonResponse(ret) #django内部执行json.dumps(),只能传字典 JsonResponse([],safe=False),列表也可以,需要传safe=False d.返回的字典中value是对象的QuerySet时,需要先将对象序列化为字符串,再加入字典中 from django.core import serializers user_list = models.BookType.objects.all() ret['data']= serializers.serialize("json", user_list) #前端需要JSON.parse()转为对象列表即可循环,生成标签等 e.字典中value是字典的QuerySet时,list即可,将QuerySet变成python的list user_list = models.UserInfo.objects.all().values('id','username') return HttpResponse(json.dumps(ret)) g.字典中value是元组的QuerySet时,list即可,将QuerySet变成python的list user_list = models.UserInfo.objects.all().values_list('id','username') return HttpResponse(json.dumps(ret))
Ajax发送:
a.普通的字典 data: {'k': 'v'} b.字典value有数组时,需要声明 data: {'k': 'v'}, 'list': [1,2,3,4]} traditional:true #声明数组 c.字典value有字典时,需要序列化字典 data: {'k': 'v'}, 'list': [1,2,3,4], 'k3': JSON.stringfy({'k1': 'v'}))} d.封装普通表单数据 data: $(form对象).serilize() f.表单数据有文件时,需要创建FormData对象并声明 var form = new FormData(); form.append('avatar_img', file_obj); form.append('csrfmiddlewaretoken','{{ csrf_token }}'); $.ajax({ url: '/avatar_upload.html', type:'POST', data: form, processData: false, // tell jQuery not to process the data #声明1 contentType: false, // tell jQuery not to set contentType #声明2 success: function (arg) { #这里处理回调数据 var obj = JSON.parse(arg); $('#previewImg').attr('src','/' + obj.data); } })
3、CSRF请求头 X-CSRFToken
全局默认的settings中会更新seetings.py配置,其中csrf有默认设置,字段为HTTP_X_CSRFTOKEN='',HTTP_是django自动
加的前缀,请求头不能包含下划线,这是非法的,后台拿不到数据,X-CSRFTOKEN,官网推荐为X-CSRFToken
方式一:
$.ajax({ ... headers:{'X-CSRFToken':$.cookie('csrftoken')} })
方式二:统一配置一次即可,发送前
$.ajaxSetup({ beforeSend:function(xhr,settings){ #xhr为xmlhttprequest对象 xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')) #设置csrf } })
4、与Form组件验证的关系
- Ajax提交:验证正确了cleaned_data是一个字典,可以传,但错误信息ErrorDict不是字典,是一个django对象,不能用python的json.dumps序列化。
- 可以as_json化为字典,再as_json化为字符串HttpResponse给浏览器
(常用)- 通过json.dumps定制一个Clis,进行特殊对象的序列化,什么类型取什么值,构造出json对象,返回需要的值
5、与原生Ajax的联系:
$.ajax({
...
success: function (data,a1,a2) { #第一个参数是success,第二个参数为XmlHttpRequest对象,即可调用原生ajax方法写
if(){}
else{}
}
})
三、伪Ajax
1、原理:利用iframe框改src时框跳转,但大页面不刷新,实现不刷新发送请求,而普通的form表单会刷新
兼容性最好
2、iframe标签跳转:
<input type="text" id="url"/> <input type="button" value="发送" onclick="ifm_request();"/> <iframe id="ifm" src="http://www.baidu.com"></iframe> #跳转网页 function ifm_request() { var url = $('#url').val(); // console.log(url) $('#ifm').attr('src',url); }
因此,利用iframe实现伪ajax方法,不依赖任何插件
3、iframe实现发送和接收数据
<form action="/ajax_json/" method="post" target="ifm1"> #target属性让form和iframe建立关系,表单数据通过iframe发送 <iframe id='ifm1' name="ifm1"></iframe> #iframe框,可写在表单外 <input type="text" name="username" /> <input type="text" name="email" /> <input type="submit" onclick="submitForm();" value="提交"/> </form>
写一个js,当点击提交时,获取发送的数据:
function submitForm() { $('#ifm1').load(function () { var text = $('#ifm1').contents().find('body').text(); #contents是整个html,找到body标签 var obj = JSON.parse(text); #text是字符串 }) }
4.iframe标签的内容:
iframe标签包含的是document对象,相当于上下文,或者空间管理,嵌套了html,innerText、children等无法使用
对象里面有html标签-header\body标签,body标签的文本即返回的字符串
Jquery方式:
通过.contents()拿到html对象
$('#ifm1').contents().find('body').text()\html():文本内容
DOM方式:
.contentWindow:拿到嵌套的document
.contentWindow.document.body:拿到body内容
.contentWindow.document.body.innerHtml:拿到body文本内容
5.iframe接收数据的时机:
form提交后,要等到服务器返回数据时iframe框才接收到数据,同时触发onload事件,在jQuery为$().load()
由于从上到下加载时先给iframe绑定load事件会报错,可以点submit时再绑定load
6.上传文件图片以及预览
view函数中 username = request.POST.get('username') fafafa = request.FILES.get('fafafa') #获取文件,可chunks()写入 img_path = os.path.join('static/imgs/',fafafa.name) with open(img_path,'wb') as f: for item in fafafa.chunks(): f.write(item) ret = {'status':True,'error':None,'data':img_path} #返回文件路径,用于预览 return HttpResponse(json.dumps(ret)) 前端 function ifmSubmit() { $('#ifm1').load(function () { var text = $('#ifm1').contents().find('body').text(); var obj = JSON.parse(text); $('#preview').empty(); #先清空 var imgTag = document.createElement('img'); #创建标签,也可以创建好一个img标签,直接赋值src即可 imgTag.src = "/"+obj.data; #路径要以/开头,这是静态路径,添加到src即可查看 $('#preview').append(imgTag); #添加内容 }) }
对比jQuery:
success: function (arg) { var obj = JSON.parse(arg); #对比iframe,这里return的数据直接拿到,arg即ret if(obj.status){ $('#previewImg').attr('src','/' + obj.data); #不同于上面的创建标签,这里是直接对img标签操作 } }
对比原生Ajax:
xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ var obj = JSON.parse(xhr.responseText); #拿到 $('#previewImg').attr('src','/' + obj.data); } };
三个的不同在于返回数据的获取,标签src都要使用jQuery,也可以dom
====================================ajax操作时机===============================
如果发送的是普通的数据、字符串 ------> jQuery,XMLHttpRequest,iframe
如果发送的是文件 ------> iframe,jQuery(FormData),XMLHttpRequest(FormData)
===============================================================================