一.JsonResponse
上篇文章中:django之ajax我們給前端傳一個字典數據需要先序列化成一個json字符串,前端使用JSON.parse來解析數據,如果我們在響應頭裏面加一個content_type='application/json’這個時候前端就不用在解析我們的json數據了,應爲js中有自己的內部的解析器專門用來解析json數據
def test(request):
if request.method == 'GET':
return render(request,'test.html')
else:
username = request.POST.get('uname')
password = request.POST.get('pwd')
if username=='test' and password == '123':
ret = {'code':0,'success':'/app03/show_book/'}
return HttpResponse(json.dumps(ret),content_type='application/json')
else:
ret = {'code':1,'fail':'用戶名或密碼錯誤!!'}
return HttpResponse(json.dumps(ret),content_type='application/json')
前端HTML:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>JsonResponse</h1>
<form action="" method="post">
用戶名:<input type="text" name="username" id="username">
用戶名:<input type="password" name="password" id="password">
<input type="button" id="btn" value="確認">
<span style="color: red;font-size: 16px"></span>
</form>
<script src="{% static 'jquery.js' %}"></script>
<script>
$('#btn').click(function () {
$.ajax({
url: '/app03/test/',
type: 'post',
data: {
uname: $('#username').val(),
pwd: $('#password').val()
},
success: function (res) {
//var res_Str = JSON.parse(res); 註釋掉了
if (res['code'] === 0) {
location.href = res['success']
} else if (res['code'] === 1) {
$('span').text(res['fail'])
}
}
})
})
</script>
</body>
</html>
結果還是一樣能訪問並沒有報錯,可以看到在我們的響應頭裏面有Content-Type: application/json就是我們視圖函數裏面設置的:
有沒有一個方法可以將我們的數據序列化並且加上響應頭呢,當然有,就是我們的JsonResponse,看名字就知道是返回一個json的數據,我們只需要該一個地方就行:
def test(request):
if request.method == 'GET':
return render(request,'test.html')
else:
username = request.POST.get('uname')
password = request.POST.get('pwd')
if username=='test' and password == '123':
ret = {'code':0,'success':'/app03/show_book/'}
# return HttpResponse(json.dumps(ret),content_type='application/json')
return JsonResponse(ret) #使用JsonResponse
else:
ret = {'code':1,'fail':'用戶名或密碼錯誤!!'}
# return HttpResponse(json.dumps(ret),content_type='application/json')
return JsonResponse(ret)
JsonResponse源碼,可以看到JsonResponse已經給你做了設置請求頭和序列化了:
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
json_dumps_params=None, **kwargs):
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
if json_dumps_params is None:
json_dumps_params = {}
kwargs.setdefault('content_type', 'application/json') #設置請求頭
data = json.dumps(data, cls=encoder, **json_dumps_params) #序列化
super(JsonResponse, self).__init__(content=data, **kwargs)
JsonResponse返回其他數據:
示例,發送一個ajax請求後端數據(列表),拿到數據之後展示在前端頁面:
home頁面:
def home(request):
return render(request, 'ajaxtes/home.html')
data頁面:
def data_base(request):
li = [11,22,33,44,55,66]
#return JsonResponse(li)
return JsonResponse(li,safe=False)
報錯:TypeError:In order to allow non-dict objects to be serialized set the safe parameter to False.
中文:爲了允許非dict對象被序列化,請將safe參數設置爲False。
前端HTML:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>JsonResponse返回列表數據</h1>
<ul>
</ul>
<script src="{% static 'jquery.js' %}"></script>
<script>
$.ajax({
url:"{% url 'app03:data_base' %}",
type:'get',
success:function (res) {
$.each(res,function (k,v) {
//console.log(k,v);
var res_Str = '<li>' + v.toString() + '</li>';
$('ul').append(res_Str)
})
}
})
</script>
</body>
</html>
如果傳入的不是字典數據,需要加上safe=False參數
JsonResponse作用:
1.序列化數據
2.設置響應頭kwargs.setdefault(‘content_type’, ‘application/json’)
二.content_type
content_type:前後端交互指定的一種消息格式
前端給後端傳入json數據:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>JsonResponse</h1>
<ul>
</ul>
<script src="{% static 'jquery.js' %}"></script>
<script>
$.ajax({
url:"{% url 'app03:data_base' %}",
type:'post',
data:{k1:'v1',k2:'v2'}, #並不是json 默認是application/x-www-form-urlencoded類型
success:function (res) {
console.log(res)
}
})
</script>
</body>
</html>
後端視圖函數:
def data_base(request):
li = [11,22,33,44,55,66]
print(request.POST) #<QueryDict: {'k1': ['v1'], 'k2': ['v2']}>
print(request.body) #b'k1=v1&k2=v2'
return JsonResponse(li,safe=False)
上面可以看到request.POST裏面有數據 QueryDict類型,request.body裏面是bytes類型的數據,django會從body裏面那數據解析完成之後放到request.POST裏面,瀏覽器默認請求類型都是application/x-www-form-urlencoded; charset=UTF-8類型:
爲什麼print(request.POST)就能拿到數據呢,這就是django有內置的解析器可以解析x-www-form-urlencoded; charset=UTF-8類型的數據,內置的解析器做了哪些事情,首先從請求頭信息裏面把請求頭拿出來判斷,如果’content_type’ == 'application/x-www-form-urlencoded;類型,就從請求體裏面把數據拿出來(‘k1=v1&k2=v2’)然後解析,解析成一對一對的鍵值對並且放到request.POST中:<QueryDict: {‘k1’: [‘v1’], ‘k2’: [‘v2’]}>
通過ajax發送json數據:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>JsonResponse</h1>
<ul>
</ul>
<script src="{% static 'jquery.js' %}"></script>
<script>
$.ajax({
url:"{% url 'app03:data_base' %}",
type:'post',
data:JSON.stringify({k1:'v1',k2:'v2'}), //將js中的字符串轉換成json字符串
//data:{k1:'v1',k2:'v2'},
contentType:'application/json', //設置請求頭
success:function (res) {
console.log(res)
}
})
</script>
</body>
</html>
後端接收的數據:
def data_base(request):
li = [11,22,33,44,55,66]
print(request.POST) #<QueryDict: {}> 裏面沒有數據
print(request.body) #b'{"k1":"v1","k2":"v2"}' 都在原始數據中
return JsonResponse(li,safe=False)
發送的json數據後端request.POST裏面就沒有數據了,反而都在body裏面,這是爲什麼呢,因爲django沒有解析器可以解析application/json的數據類型,所以request.POST裏面沒有數據,如果是post請求需要從原始數據request.body中拿數據進行處理 get在get_full_path中拿原始數據,那發送過來的數據就沒辦法解析了嗎?當然可以:
第一種辦法:
print(eval(str(request.body,'utf-8'))['k1']) #v1
第二種辦法:
推薦使用第二種方法:
print(json.loads(request.body)) {"k1":"v1","k2":"v2"}
三.json機制
什麼是json:
- JSON 指的是 JavaScript 對象表示法(JavaScript Object Notation)
- JSON 是輕量級的文本數據交換格式
- JSON 獨立於語言 *
- JSON 具有自我描述性,更易理解
- JSON 使用 JavaScript 語法來描述數據對象,但是 JSON 仍然獨立於語言和平臺。JSON 解析器和 JSON庫支持許多不同的編程語言。
json數據類型和python數據類型的對比,源碼中有對比圖:
前端中解析json數據的方法:json.parse()
前端中序列化json數據的方法:json.stringify()
python中序列化成json字符串:json.dumps()
python中反序列化成json字符串:json.loads()
總結:
相當於我有一個json方法,你有一個json方法,你給我發數據必須是json字符串的格式,那麼你就需要將你的數據類型序列化爲json的字符串,那麼序列化的時候,就把你的數據序列化爲了符合json標準的字符串,然後我接收到這個字符串之後,我通過我的json方法,將數據轉換爲我的語言支持的數據類型。在進行反序列化的時候,如果你的字符串不符合json的格式,那麼反序列化的時候就會報錯,所以只要你是通過json序列化成的字符串,都是能夠json反序列化的,因爲json序列化的時候,就把你的數據改爲了符合json標準的字符串形式.
四.文件上傳
1.form表單上傳文件:
<form action="" method="post" enctype="multipart/form-data">
頭像:<input type="file" id="header_pic" name="header_pic">
用戶名:<input type="text" name="username" id="username">
<input type="submit">
</form>
2.ajax上傳文件:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>文件上傳</h1>
ajax上傳文件:<input type="file" id="file">
ajax用戶名:<input type="text" id="username">
<button id="btn">確認</button>
<script src="{% static 'jquery.js' %}"></script>
<script>
$('#btn').click(function () {
var res_File = new FormData(); //ajax攜帶文件數據的對象 它會將添加給它的鍵值對加工成formdata的類型
res_File.append('username',$('#username').val()); //添加鍵值的方法是append,注意寫法,鍵和值之間是逗號
res_File.append('header_pic',$('#file')[0].files[0]); //$('#file')[0]:DOM對象 [0]:第一個
$.ajax({
url:"{% url 'app03:up_file' %}",
type:'post',
data:res_File, //將添加好數據的formdata放到data這裏 也可以直接在這裏寫
processData:false, //固定寫法 預處理操作 設置爲False就是不處理數據
contentType:false, //固定寫法 預處理操作 設置爲False就是不設置內容類型
success:function (res) {
console.log(res)
}
})
})
</script>
</body>
</html>
後端代碼:
def up_file(request):
if request.method == 'GET':
return render(request, 'ajaxtes/up.html')
else:
file_obj = request.FILES.get('header_pic')
from django.conf import settings
file_path = os.path.join(settings.BASE_DIR, 'statics','img', file_obj.name)
with open(file_path, 'wb') as f:
for chunk in file_obj.chunks():
//chunks():默認一次返回大小爲經測試爲65536B,也就是64KB,最大爲2.5M, 可以設置每次讀取大小,是一個生成器
f.write(chunk)
return HttpResponse('ok')
五.json序列化日期對象
json是不能序列化日期數據類型,上面的對比圖中可以看到json是沒有日期類型的,這個需要藉助一個模塊JSONEncoder:
import json
from datetime import datetime, date
class JsonCustomEncoder(json.JSONEncoder): #繼承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)
dic = {'name':'zhansgan','date_obj':datetime.now()} #裏面有日期對象
ret = json.dumps(dic,cls=JsonCustomEncoder) #需要制定那個類來序列化
print(ret) #結果:{"name": "zhansgan", "date_obj": "2020-04-10 21-55-03"}