Django之ajax、form表單上傳文件

一.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:

  1. JSON 指的是 JavaScript 對象表示法(JavaScript Object Notation)
  2. JSON 是輕量級的文本數據交換格式
  3. JSON 獨立於語言 *
  4. JSON 具有自我描述性,更易理解
  5. 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"}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章