title: Django之Ajax #标题tags: #标签
date: 2022-01-04
categories: python # 分类
Ajax简介
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)。
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
AJAX除了异步的特点外,还有一个就是:浏览器页面局部刷新。
言而总之,Ajax的特性就是异步请求,局部刷新。
Ajax简单示例
下面来一个AJAX的简单示例:
页面输入两个整数,通过AJAX传输到后端计算出结果并返回。
<!-- ajax.html文件内容 -->
{% load static %}
<!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.js' %}"></script>
<script>
$("#b1").on("click", function () {
$.ajax({
url: "/ajax_add/", //别忘了加双引号
type: "GET",
data: {"i1": $("#i1").val(), "i2": $("#i2").val()}, //object类型,键值形式的,可以不给键加引号
success: function (data) {
$("#i3").val(data);
}
})
})
</script>
</body>
</html>
views.py文件内容:
from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.
def ajax_demo(request):
return render(request,'ajax.html')
def ajax_add(request):
i1=int(request.GET.get("i1"))
i2=int(request.GET.get("i2"))
ret=i1+i2
return JsonResponse(ret,safe=False)
urls.py文件内容:
from django.contrib import admin
from django.urls import path
from ajax_demo import views
urlpatterns = [
# path('admin/', admin.site.urls),
path('ajax_demo/',views.ajax_demo),
path('ajax_add/',views.ajax_add),
]
settings.py的部分需要修改的内容:
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static_file'),
]
另外,需要将jquery.js
文件放到上面指定的目录下:
好,现在来启动项目看看:
Ajax常见应用场景
Ajax的常见应用场景有:搜索引擎根据用户输入的关键字,自动提示检索关键字。还有一个很重要的应用场景就是注册时候的用户名的查重。
其实下面的图片中就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。
它有如下特征:
- 整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
- 当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!
当输入用户名后,把光标移动到其他表单项上时,浏览器会使用AJAX技术向服务器发出请求,服务器会查询名为lemontree7777777的用户是否存在,最终服务器返回true表示名为lemontree7777777的用户已经存在了,浏览器在得到结果后显示“用户名已被注册!”。
Ajax的优点
- AJAX使用JavaScript技术向服务器发送异步请求;
- AJAX请求无须刷新整个页面;
- 因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;
Ajax的基本格式
button class="send_Ajax">send_Ajax</button>
<script>
$(".send_Ajax").click(function(){
$.ajax({
url:"/handle_Ajax/", // 请求路径
type:"POST", // 请求方法
data: { // 发送请求时携带的数据
uname: $('#uname').val(),
pwd: $('#pwd').val(),/
csrfmiddlewaretoken: "{{ csrf_token }}"
},
success:function(data){ // success负责处理请求路径返回的信息,data关键字用于接收请求返回的数据
console.log(data)
},
// 下面的不常用,用到了再说吧
error: function (jqXHR, textStatus, err) {
console.log(arguments);
},
complete: function (jqXHR, textStatus) {
console.log(textStatus);
},
statusCode: {
'403': function (jqXHR, textStatus, err) {
console.log(arguments);
},
'400': function (jqXHR, textStatus, err) {
console.log(arguments);
}
}
})
})
</script>
ajax请求参数
/* data: 当前ajax请求要携带的数据,是一个json的object对象,ajax方法就会默认地把它编码成某种格式
(urlencoded:?a=1&b=2)发送给服务端;此外,ajax默认以get方式发送请求。*/
function testData() {
$.ajax("/test",{ //此时的data是一个json形式的对象
data:{
a:1,
b:2
}
}); //?a=1&b=2
/*processData:声明当前的data数据是否进行转码或预处理,默认为true,即预处理;如果为false,
那么对data:{a:1,b:2}会调用json对象的toString()方法,即{a:1,b:2}.toString()
,最后得到一个[object,Object]形式的结果。*/
/* contentType:默认值: "application/x-www-form-urlencoded"。发送信息至服务器时内容编码类型。
用来指明当前请求的数据编码格式;urlencoded:?a=1&b=2;如果想以其他方式提交数据,
比如contentType:"application/json",即向服务器发送一个json字符串:*/
$.ajax("/ajax_get",{
data:JSON.stringify({
a:22,
b:33
}),
contentType:"application/json",
type:"POST",
}); //{a: 22, b: 33}
/*注意:contentType:"application/json"一旦设定,data必须是json字符串,不能是json对象。
对应的views.py文件中,需要使用: json.loads(request.body.decode("utf8")) 来获取请求中携带的数据*/
/*traditional:一般是我们的data数据有数组时会用到 :data:{a:22,b:33,c:["x","y"]},
traditional为false会对数据进行深层次迭代; */
Ajax请求设置csrf_token
Ajax在提交post请求时,也需要携带csrf_token,否则会报权限错误的错误,现在来看看设置csrf_token的三种方式。
方式1
通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。
$.ajax({
url: "/cookie_ajax/",
type: "POST",
data: {
"username": "ljz",
"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插件。插件下载地址,下载并解压后,将其中的cookie.jquery.json
文件放到你项目的js存放目录中即可。
<body>
<h1>你好,test!</h1>
{% csrf_token %}
用户名:<input type="text" id="username">
<button id="btn">提交</button>
</body>
<script src="{% static 'jquery.js' %}"></script>
<script src="{% static 'jquery.cookie.js' %}"></script>
<script>
$('#btn').click(function () {
$.ajax({
url:'/test/',
type:'post',
headers:{
/*
其实在ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,
我们发contenttype类型数据的时候,csrf_token就可以这样加
*/
"X-CSRFToken": $.cookie('csrftoken'),
},
data:{
uname:$('#username').val(),
},
success:function (res) {
console.log(res)
}
})
})
</script>
Ajax简单demo
我已经把demo上传至码云,可以移步gitee进行查阅,这里就不放源码了,直接看怎么使用。
login页面的ajax请求
登录认证通过的效果如下:
登录认证失败的效果如下:
上面就是login页面的功能,接下来说下home页面的。
home页面的ajax请求
<script src="{% static 'jquery.js' %}"></script>
<script>
$.ajax({
url: "{% url 'data' %}",
type: "post",
/* 下面两段暂时先不看
data: JSON.stringify({k1: 'v1', k2: 'v2'}),
contentType: 'application/json',
*/
success: function (res) {
// each循环后端返回的res数据
$.each(res, function (k, v) {
// console.log(k,v)
var liStr = '<li>' + v.toString() + '</li>'
$('ul').append(liStr)
})
}
})
</script>
这个ajax请求主要是在加载home页面的时候,会去请求下data的这个url,然后data返回的数据如下:
def data(request):
'''
先不看这段代码,这段代码是来处理ajax发送post请求的
body_data = request.body.decode('utf-8')
import json
ret = json.loads(body_data)
print(body_data, type(body_data), ret, type(ret))
'''
l1 = [11, 22, 33, 44, 55, 66]
# JsonResponse回复一个非字典数据的时候必须加“safe=False”,否则程序运行会报错
return JsonResponse(l1, safe=False)
可以看到,ajax拿到data响应的数据后,通过循环取值的方式,将其渲染到了无序列表中,并展现在前端页面。
现在来看看加载ajax页面时,请求data这个url携带的数据,是什么样的:
可以看到在给data发送post请求时,携带的数据在请求体中,现在来看看data如何提取这些数据的,可能有些人看了下login页面的ajax请求处理后,会想,还能怎么提取,无非就是通过request.POST.get('key')
来取值呗,错了,login页面发送的ajax请求之所以可以在request.post
中拿到值,那是因为django内部会给处理Content-Type: application/x-www-form-urlencoded;
的数据,你可以在浏览器上看下请求头,login页面提交post请求时的请求头是不是设置了Content-Type: application/x-www-form-urlencoded;
,如下:
但是在给data发送数据时,我们指明了contentType: 'application/json'
,如下:
data: JSON.stringify({k1: 'v1', k2: 'v2'}),
contentType: 'application/json',
django不认contentType: 'application/json'
,所以也就不会帮我们把请求携带的数据写到request.POST
中,因此,我们需要使用下面的代码来取(不要想为什么要发送django不认识的json类型给后端,没办法,我们在写接口的时候,经常会收到json类型的数据,这个时候就需要我们按照下面的方式进行处理):
body_data = request.body.decode('utf-8')
import json
ret = json.loads(body_data)
print(body_data, type(body_data), ret, type(ret))
Ajax上传文件
demo中的upload.html
页面是上传文件的,现在我们摘出部分代码,来看看ajax是如何上传文件的,代码如下:
<script src="{% static 'jquery.js' %}"></script>
<script>
$('#btn').click(function () {
/* ajax 上传文件,需要借助FormData对象,否则会报错,FormData可以携带文件数据
*/
var formdata = new FormData();
formdata.append('uname', $('#uname').val());
/* $('#file')[0]:表示将找到的jquery对象通过[0]转换为DOM对象
然后通过DOM对象的files方法,找到上传的文件内容列表,
然后通过索引取值[0],拿到第一个捕获到的上传的第一个文件(files方法拿到的是一个文件列表)*/
formdata.append('head_pic', $('#file')[0].files[0]);
formdata.append('csrfmiddlewaretoken', $('[name=csrfmiddlewaretoken]').val());
$.ajax({
url: "{% url 'upload' %}",
type: 'post',
// 上传文件时,下面两个固定死的要设置为false
processData: false, // 不对数据做任何处理,原汁原味的传到后端
contentType: false, // 不设置内容类型
data: formdata, // data只需要把上面加工好的formdata发送即可
success: function (res) {
console.log(res)
}
})
})
</script>
不管是页面中的form表单上传文件,还是ajax上传文件,对于后端来说,处理的流程都是一样的,如下:
# ajax上传文件
def upload(request):
print(request.POST)
if request.method == 'GET':
return render(request, 'upload.html')
elif request.method == 'POST':
print(request.POST)
'''
上传文件的时候,默认post请求里只有文件名,文件具体的数据放在了request.FILES中。
打印结果:<QueryDict: {'csrfmiddlewaretoken': ['JNukzx9qO78648myA0iu5anxBXNtSaPRnd3qUhCMiYDaHWFIKpNhECY6mfYWYHHe'], 'head-pic': ['img19_1920x1200.jpg'], 'uname': ['lvjianzhao']}>
'''
print(request.FILES)
'''
前端的form表单如果需要上传文件,需要给form表单增加属性:enctype="multipart/form-data"
就可以正常看到文件数据了,否则显示的为空。
打印结果:<MultiValueDict: {'head-pic': [<InMemoryUploadedFile: img19_1920x1200.jpg (image/jpeg)>]}>
'''
file_obj = request.FILES.get('head_pic') # file_obj,就相当于一个rb模式打开的文件句柄了
print(file_obj) # img19_1920x1200.jpg
file_name = file_obj.name # img19_1920x1200.jpg
# file_path = 'C:\\Users\\Administrator\\Desktop\\' + file_name
file_path = os.path.join(settings.BASE_DIR, 'images', file_name)
# 用wb的方式把文件写入到指定文件中
with open(file_path, 'wb') as f:
# 方式一,按行读取file_obj,按行写入f
# for i in file_obj:
# f.write(i)
# 方式二,通过chunks方法,按固定字节读取,按固定字节写入(推荐)
# chunks()默认一次返回大小为65536B,也就是64KB,最大为2.5M,是一个生成器
# 源码中定义的大小:DEFAULT_CHUNK_SIZE = 64 * 2 ** 10
# 也可以通过关键字传参:chunk_size=,进行设置大小
for chunck in file_obj.chunks():
f.write(chunck)
return HttpResponse('ok')
json模块序列化时间类型的数据
代码如下:
from datetime import date, datetime
import json
d = {'name': 'ljz', 'birthday': datetime.now()}
json_str = json.dumps(d)
print(json_str)
如果运行上面的脚本,则会报错:TypeError: Object of type datetime is not JSON serializable
,为什么呢?因为json中没有对应date、datetime类型的数据,python和json中的数据类型对应关系如下:
Python | JSON |
---|---|
dict | object |
list,tuple | array |
str | string |
int,float | number |
True | true |
False | false |
None | null |
可以看到,并没有时间日期类型的对应关系,所以如果要序列化的数据中包含时间日期类型的,需要重写json的方法,才可以正常序列化,重写后的代码如下:
from datetime import date, datetime
import json
# 对含有日期格式数据的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)
d = {'name': 'ljz', 'birthday': datetime.now()}
json_str = json.dumps(d, cls=JsonCustomEncoder)
print(json_str)