Django序列化
何為序列化,說白了就是能夠將數據存到磁盤上,比如存到txt文件上。而什么能夠被存到文件里面呢?字符串!So,序列化也就是將其他的數據類型轉換為字符串的過程,那么在Django響應前端Ajax請求的時候,返回的數據是字符串類型的,那么如何將python中不同的類型轉換成字符串類型呢?下面針對不同的情況提出了不同的解決辦法
如果字典中有對象
眾所周知,python的字典什么都能存,因此存一個兩個對象自然不在話下,而有時候也確實有這樣的需求。但是偏偏不巧,在使用 json.dump() 序列化字典的時候如果里面包含了對象就會報錯,那么針對這問題應該怎樣解決呢?解決辦法就是使用 serializers ,如下
視圖函數:
def index(request): ret = {'status': True, 'data': None} user_list = models.UserInfo.objects.all() ret['data'] = serializers.serialize("json", user_list) return render(request, "index.html", {'data': json.dump(ret)})
HTML頁面:
$.ajax({ url: "/index", type: "post", dataType: 'json', // 在這里進行了一次反序列化 success:function(arg){ if(arg.status){ var ret = JSON.parse(arg.data); // 在這里進行了第二次反序列化 } } });
當前端接收到數據后會把字符串變成它原本的類型,這一過程稱為反序列化,而上面由於進行了兩次序列化的過程,因此在進行反序列化的時候也要兩次,在使用 dataType:'JSON' 后,還要再將數據 JSON.parse 一次才行
如果字典中沒有對象
如果在字典中沒有出現對象,那么久更容易了,直接用 json.dump() 就可以了
視圖函數:
def index(request): ret = {'status': True, 'data': None} user_list = models.UserInfo.objects.all().values('id', 'username') ret['data'] = list(user_list) return render(request, "index.html", {'data': json.dump(ret)})
HTML頁面:
$.ajax({ url: "/index", type: "post", dataType: 'json', success:function(arg){ if(arg.status){ var ret = arg.data; // 獲取到的數據直接使用 } } });
由於在序列化的時候只進行了一次,因此這次拿到的數據直接使用就好了
Django接收文件
現在的網站少不了的便是一個賬號,既然有賬號就得有頭像什么的,那么就需要上傳圖片,那么在后台如何接收到用戶上傳的圖片並進行保存呢?
在Django中,上傳的文件的信息都保存在了 request.FILES 里面了,因此只需要去那里面獲取就好
file = request.FILES.get('file') # 獲取文件對象,包括文件名文件大小和文件內容 print(file.name) # 文件名 print(file.size) # 文件大小 # 將文件寫入本地 f = open(file.name, 'wb') for line in file.chunks(): # 由於文件不是一次性上傳的,因此一塊一塊的寫入 f.write(line) f.close()
上面是自己寫 <input type="file" /> 來上傳文件,下面介紹使用Form來獲取文件
相比於傳統的方式,使用Form獲取文件唯一的區別就是文件對象的位置不同,使用Form后文件保存到了 obj.cleaned_data 中,我們獲取的時候去那里面獲取,除此之外和上面的沒有什么區別
class UploadForm(forms.Form): file = fields.FileField() def index(request): obj = UploadForm(request.POST, request.FILES) if obj.is_valid(): file = obj.cleaned_data['file'] print(file.name) # 文件名 print(file.size) # 文件大小 f = open(file.name, 'wb') for line in file.chunks(): f.write(line) f.close()
Ajax通過FormData上傳文件
上面介紹了如何在后端接收文件,那么如何在前端上傳文件呢?當然,上傳文件還能用什么?不就是用 <input type="file" /> 嗎?確實,只能用這個標簽,但是這里是指怎樣將文件發送給服務器,以何種方式?通常使用的是form表單提交,這是一種比較普遍的方式,但是使用表單的一個特點就是只要一提交就會刷新頁面,那么有的時候不希望頁面刷新應該怎么辦呢?這就是Ajax的特性了,但是怎樣通過Ajax發送文件呢?這就要用到瀏覽器中的一個對象——FormData了,首先介紹下FormData的使用,如下代碼:
var data = new FormData(); // 獲取FormData對象 data.append('k1', 'v1'); // 將數據以鍵值對的形式寫入 data.append('k2', 'v2'); // 需要加上processData跟contentType $.ajax({ url: '', type: 'post', data: data, processData: false, // 告訴jQuery不要處理數據 contentType: false, // 告訴jQuery不要設置類型 success: function(arg){ console.log(arg); } });
由上面的代碼可知,FormData跟一個字典差不多,都是以鍵值對存儲數據,發送的時候將對象設置給ajax的data就好了,只是想要發送還要將jQuery的processData跟contentType都設置成false才行,那這么看這個FormData好像跟普通的json對象差不多,那要它何用?或者換句話說它的存在一定有其他的作用,那這個作用顯然就是FormData能夠上傳文件了,那么究竟它要如何上傳文件呢?見下面的一段小Demo就知道了。
<input type="file" id="file" /> <!-- 在這里上傳文件 --> <script> var data = new FormData(); data.append('file', document.getElementById("file").files[0]); // 獲取文件放入FormData中 // 發送數據時沒有任何區別 $.ajax({ url: '/index.html', type: 'post', data: data, processData: false, // 告訴jQuery不要處理數據 contentType: false, // 告訴jQuery不要設置類型 success: function(arg){ console.log(arg); } }); </script>
由上面的Demo可知,無論是否上傳文件,在使用Ajax發送的時候沒有任何區別,那么唯一不同的就是在封裝數據的那里需要獲取上傳文件的標簽里面的文件內容,即 document.getElementById("file").files[0] , document.getElementById("file").files 獲取的是該標簽里面所有上傳文件的內容,因為有的時候不只會上傳一份文件
ifram+表單偽造Ajax請求
上面使用Ajax上傳文件雖然好,但是仍舊有弊端,因為有的瀏覽器不支持FormData對象,因此為了更好的兼容性,還是使用表單會好一些,但是別忘了最初尋找Ajax方法上傳文件的目的,因此有沒有即能夠解決兼容性問題又能夠在上傳后不刷新頁面的方式呢?還真有,那就是使用iframe和表單來偽造Ajax請求,這樣既解決了兼容性問題又不會刷新頁面。那么究竟如何去做呢?其實很簡單,將form的target屬性與iframe的name屬性關聯即可
偽造Ajax發送數據
<ifram id="ifram" name="fram"></ifram> <!-- name="fram" target="from" --> <form id="fm" action="/ajax.html" method="post" target="from"> <input type="text" /> <input type="submit" value="提交" /> </form>
如此之后,再提交表單刷新的就是ifram了,而返回的數據也會在ifram中顯示,但是既然是偽造Ajax,發送數據是偽造了,但是Ajax還有一個功能就是擁有一個回調函數,可以處理接收到的數據,那么怎樣去偽造這個回調函數呢?
偽造回調函數
上面有說過,請求返回的內容都會在ifram中顯示,那么在請求響應后去到ifram中獲取數據然后處理不就可以偽造回調函數了嗎?那么現在要解決的問題就是怎么確定請求的響應到達的時間,在ifram中有一個onload屬性,在頁面加載完畢后會觸發,那么就可以在onload中去獲取數據並處理
<ifram id="ifram" name="fram" onload="reloadIfram(this);"></ifram> <form id="fm" action="/ajax.html" method="post" target="from"> <input type="text" /> <input id="sub" type="submit" value="提交" /> </form> <script> function reloadIfram(ths){ // 使用原生js獲取 ths.contentWindow.document.body.innerHTML; // 使用jquery獲取 $(ths).contents().find('body').html(); } </script>
這樣就可以在請求到達的時候執行 reloadIfram() 函數,做到模擬回調函數的效果,但是現在這樣寫有點問題,因為在第一次加載頁面的時候,ifram加載完成會執行onload,但是由於頁面從上到下加載,因此此時的 reloadIfram() 函數還沒有被加載,因此會出現未定義的錯誤,雖然影響不大,但是能不能去解決這個錯誤呢?當然是可以的,思路就是在提交表單的時候再去為ifram綁定onload事件,那樣就不怕reloadIfram()函數還沒有加載了,具體代碼如下:
document.getElementById("sub").click = function(){ document.getElementById("ifram").onload = reloadIfram; }