Ajax準備知識:JSON
什麼是 JSON ?
- JSON 指的是 JavaScript 對象表示法(JavaScript Object Notation)
- JSON 是輕量級的文本數據交換格式
- JSON 獨立於語言 *
- JSON 具有自我描述性,更易理解
* JSON 使用 JavaScript 語法來描述數據對象,但是 JSON 仍然獨立於語言和平臺。JSON 解析器和 JSON 庫支持許多不同的編程語言。
stringify與parse方法
JavaScript中關於JSON對象和字符串轉換的兩個方法:
JSON.parse(): 用於將一個 JSON 字符串轉換爲 JavaScript 對象(json只認雙引的字符串格式)
JSON.parse('{"name":"Howker"}');
JSON.parse('{name:"Stack"}') ; // 錯誤
JSON.parse('[18,undefined]') ; // 錯誤
JSON.stringify(): 用於將 JavaScript 值轉換爲 JSON 字符串。
JSON.stringify({"name":"Tonny"})
Json和XML的比較
JSON 格式於2001年由 Douglas Crockford 提出,目的就是取代繁瑣笨重的 XML 格式。
JSON 格式有兩個顯著的優點:書寫簡單,一目瞭然;符合 JavaScript 原生語法,可以由解釋引擎直接處理,不用另外添加解析代碼。所以,JSON迅速被接受,已經成爲各大網站交換數據的標準格式,並被寫入ECMAScript 5,成爲標準的一部分。
XML和JSON都使用結構化方法來標記數據,下面來做一個簡單的比較。
用XML表示中國部分省市數據如下:
<?xml version="1.0" encoding="utf-8"?> <country> <name>中國</name> <province> <name>黑龍江</name> <cities> <city>哈爾濱</city> <city>大慶</city> </cities> </province> <province> <name>廣東</name> <cities> <city>廣州</city> <city>深圳</city> <city>珠海</city> </cities> </province> <province> <name>臺灣</name> <cities> <city>臺北</city> <city>高雄</city> </cities> </province> <province> <name>新疆</name> <cities> <city>烏魯木齊</city> </cities> </province> </country>
用JSON表示如下:
{ "name": "中國", "province": [{ "name": "黑龍江", "cities": { "city": ["哈爾濱", "大慶"] } }, { "name": "廣東", "cities": { "city": ["廣州", "深圳", "珠海"] } }, { "name": "臺灣", "cities": { "city": ["臺北", "高雄"] } }, { "name": "新疆", "cities": { "city": ["烏魯木齊"] } }] }
由上面的兩端代碼可以看出,JSON 簡單的語法格式和清晰的層次結構明顯要比 XML 容易閱讀,並且在數據交換方面,由於 JSON 所使用的字符要比 XML 少得多,可以大大得節約傳輸數據所佔用得帶寬。
Ajax介紹
- 同步交互:客戶端發出一個請求後,需要等待服務器響應結束後,才能發出第二個請求;
- 異步交互:客戶端發出一個請求後,無需等待服務器響應結束,就可以發出第二個請求。
""" 異步提交 局部刷新 例子:github註冊 動態獲取用戶名實時的跟後端確認並實時展示的前端(局部刷新) 朝發送請求的方式 1.瀏覽器地址欄直接輸入url回車 GET請求 2.a標籤href屬性 GET請求 3.form表單 GET請求/POST請求 4.ajax GET請求/POST請求 AJAX 不是新的編程語言,而是一種使用現有標準的新方法(比較裝飾器) AJAX 最大的優點是在不重新加載整個頁面的情況下,可以與服務器交換數據並更新部分網頁內容。(這一特點給用戶的感受是在不知不覺中完成請求和響應過程) Ajax我們只學習jQuery封裝之後的版本(不學原生的 原生的複雜並且在實際項目中也一般不用) 所以我們在前端頁面使用ajax的時候需要確保導入了jQuery
ps:並不只有jQuery能夠實現ajax,其他的框架也可以 但是換湯不換藥 原理是一樣的 """
示例:
頁面輸入兩個整數,通過AJAX傳輸到後端計算出結果並返回。(整個過程頁面不準有刷新,也不能在前端計算)<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<input type="text" id="i1"> + <input type="text" id="i2"> = <input type="text" id="i3">
<button id="b1">Ajax Test</button>
<script src="/static/jquery-3.3.1.min.js"></script>
<script>
$('#b1').click(function () {
$.ajax({
url:'', // 1.指定向哪個後端提交ajax請求,不填則默認向當前地址提交
type:'POST', // 2.指定請求提交方式:get/post 默認是get請求
data:{i1:$('#i1').val(),i2:$('#i2').val()}, // 3.要提交給後端的數據
//dataType:'json', //預期服務器返回的數據類型
success:function (data) { // 4.回調函數:當後端返回結果時會自動觸發,args用來接收後端的返回結果
$('#i3').val(data)
}
})
})
</script>
</body>
</html>def ajax_test(request):
if request.method=='POST':
i1=request.POST.get('i1')
i2=request.POST.get('i2')
ret=int(i1)+int(i2)
return HttpResponse(ret)
return render(request,'ajax_test.html')
"""
針對後端返回字典類型數據:
HttpResponse返回 (注意:HttpResponse只能返回字符串)
必須先將字典轉成json格式才能返回,否則無法返回完整的字典
JsonResponse返回,底層已經自動的幫你序列化成了json數據返回
針對前端接收字典類型數據:
dataType //預期服務器返回的數據類型
HttpResponse返回的json數據,回調函數不會自動幫我們反序列化
需要我們自己手動來反序列化
方法1:在ajax中設置dataType參數
方法2:自己手動JSON.parse()來反序列化
JsonResponse返回的json數據,回調函數會自動進行反序列化,拿到手的就是一個對象了
"""
views.py
from django.conf.urls import url from app01 import views urlpatterns=[ url(r'^ajax_test/',views.ajax_test), ]
AJAX的優缺點
優點:
- AJAX使用JavaScript技術向服務器發送異步請求;
- AJAX請求無須刷新整個頁面;
- 因爲服務器響應內容不再是整個頁面,而是頁面中的部分內容,所以AJAX性能高;
- 兩個關鍵點:1.局部刷新,2.異步請求
前後端傳輸數據編碼格式
'''前提:前後端數據傳輸時一定要保證編碼格式與數據真正格式是一致的''' # get請求數據的編碼格式直接跟在了url後面 '''url?name=123&age=123''' ''' 可以發送post請求的方式: 1、form表單 2、ajax請求 前後端傳輸數據的編碼格式: 1、urlencoded 2、formdata 3、json ''' # form表單post提交: ''' 1、默認提交的編碼格式:urlencoded 數據格式:user=yumi&pwd=123 django後端針對符合urlencoded編碼格式的數據會自動解析封裝到request.POST中 2、涉及文件提交的編碼格式:form-data 數據格式:不顯示 那麼針對普通數據還是解析封裝到request.POST中,但是文件數據解析封裝到了request.FILES中了 3、form表單無法提交json格式數據 ''' # ajax請求post提交: ''' 1、默認提交的編碼格式:urlencoded 數據格式:user=yumi&pwd=123&file=C%3A%5Cfakepath%5C2020-03-21-19-28-39_0.png django後端針對符合urlencoded編碼格式的數據會自動解析封裝到request.POST中 '''
ajax發送json格式數據
''' <script> $('#btn').click(function(){ $.alax({ url:'', type:'post', data:JSON.stringify({'user': 'yumi', 'age': 21}), contentType:'applecation/json', success:function(args){} }) }) </script> ''' # 提交方式:applecation/json # 數據格式:{"user":"yumi","age":21} json_byte = request.body # json_str = json_byte.decode(json_byte) # json.loads括號內如果傳入了一個二進制格式的數據其內部會自動解碼再反序列化 dic = json.loads(json_byte) ''' 注意:針對json數據,後端不會做任何處理,也就不會幫你解析封裝到request.POST中 而是變成了二進制的請求,此時你可以用request.body來獲取二進制請求數據 request對象方法補充:request.is_ajax()判斷請求是否是ajax請求,返回布爾值 ''' ''' ajax請求發送json格式數據時需要注意: 1、要指定提交方式:設置contentType參數並且指定爲'application/json' 2、數據必須是真正的json格式數據:data:JSON.stringify({'user': 'yumi', 'age': 21}) 3、針對json格式數據django後端需要自己通過request.body來手動獲取處理 '''
序列化--Django內置的serializers
什麼意思呢?就是我的前段想拿到由ORM得到的數據庫裏面的一個個用戶對象,我的後端想直接將實例化出來的數據對象直接發送給客戶端,那麼這個時候,就可以用Django給我們提供的序列化方式
def ser(request): #拿到用戶表裏面的所有的用戶對象 user_list=models.User.objects.all() #導入內置序列化模塊 from django.core import serializers #調用該模塊下的方法,第一個參數是你想以什麼樣的方式序列化你的數據 ret=serializers.serialize('json',user_list) return HttpResponse(ret)
ajax發送文件
''' $('#btn').click(function () { // 1 需要先利用FormData內置對象 let formData = new FormData(); // 2 添加普通的鍵值對 formData.append('user', $('#inp1').val()); formData.append('pwd', $('#inp2').val()); // 3 添加文件對象 formData.append('file', $('#f1')[0].files[0]); //先將jQuery對象轉成標籤對象再進一步獲取文件數據 // 4 將對象基於ajax發送給後端 $.ajax({ url:'', type:'post', data:formData, //直接將對象放data後面即可 //ajax發送文件時必須要指定的兩個參數: contentType:false, // 不需使用任何編碼 django後端能夠自動識別formdata對象 processData:false, // 告訴瀏覽器不要對你的數據進行任何處理 success:function (args) { {#alert(args)#} } }) }) ''' if request.is_ajax(): if request.method == 'POST': print(request.POST) print(request.FILES) ''' 總結: 1、利用FormData內置對象 let formData = new FormData() 2、針對普通鍵值對,直接往對象裏添加即可 formData.append('username',$('#d1').val()); 3、針對文件數據,需要先將文件對應的jQuery對象轉成標籤對象再通過files獲取真正的文件數據再添加到formData對象中 4、ajax發送文件時必須要指定兩個關鍵性的參數 contentType:false, // 不需使用任何編碼 processData:false, // 告訴瀏覽器不要對你的數據進行任何處理 5、django後端自動識別formdata對象 自動將內部的普通鍵值對解析封裝到request.POST中 自動將文件數據解析封裝到request.FILES中 '''
ajax結合sweetalert實現刪除按鈕的二次確認操作
''' 知道如何CV代碼 學會基於別人代碼的基礎之上做修改 研究別人代碼中各個參數的意思,再照貓畫虎的加些效果 ''' ''' $('.del').click(function () { let deleteBtn = $(this); swal({ title: "真的要刪除嗎?", text: "請確認以備份數據", type: "warning", showCancelButton: true, confirmButtonClass: "btn-danger", confirmButtonText: "刪除", cancelButtonText: "返回", closeOnConfirm: false, closeOnCancel: false, showLoaderOnConfirm: true //遇到ajax請求遇到延遲時的等待效果 }, function (isConfirm) { if (isConfirm) { $.ajax({ // 朝後端發送ajax請求刪除數據之後 再彈下面的提示框 url:'', type:'post', data:{'del_id':deleteBtn.attr('delete_id')}, success:function (args) { swal("刪除成功", "已刪除", "success"); if (args.code === 1000) { // 判斷響應狀態碼 然後做不同的處理 swal("刪除成功", args.msg, "success"); // 刪除成功後刷新頁面的兩種方式 // 1、直接刷新當前頁面 // window.location.reload() // 2、利用DOM操作,動態刷新 deleteBtn.parent().parent().remove(); } else { swal("刪除失敗", '內部未知錯誤', "info"); } } }) } else { swal("刪除失敗", "取消刪除", "error"); } }); '''
django自帶的序列化組件(drf鋪墊)
# 需求:在前端給我獲取到後端用戶表裏面所有的數據 並且要是列表套字典 '''只需將獲取的數據重新組成一個個字典然後添加到列表中轉成json格式數據傳給前端即可''' # 方法一:手動拼接字典 ''' from django.http import JsonResponse def ab_serializers(request): queryset = models.User.objects.all() tmp_list = [] for obj in queryset: tmp = { 'pk': obj.pk, 'user': obj.user, 'age': obj.age, 'gender': obj.gender } tmp_list.append(tmp) return JsonResponse(tmp_list, safe=False) ''' # 方法二:使用serializers模塊 from django.core import serializers ''' from django.core import serializers def ab_ser(request): # 需求:在前端給我獲取到後端用戶表裏面所有的數據 並且要是列表套字典 queryset = models.User.objects.all() """會自動將數據序列化變成json格式的字符串 並且內部非常的全面""" res = serializers.serialize('json', queryset) return HttpResponse(res) ''' """ [ {"pk": 1, "username": "jason", "age": 25, "gender": "male"}, {"pk": 2, "username": "egon", "age": 31, "gender": "female"}, {"pk": 3, "username": "kevin", "age": 32, "gender": "others"}, {"pk": 4, "username": "tank", "age": 40, "gender": 4} ] 前後端分離的項目 作爲後端開發的你只需要寫代碼將數據處理好 能夠序列化返回給前端即可 再寫一個接口文檔 告訴前端每個字段代表的意思即可 [ { "model": "app01.user", "pk": 1, "fields": {"username": "jason", "age": 25, "gender": 1}}, { "model": "app01.user", "pk": 2, "fields": {"username": "egon", "age": 31, "gender": 2}}, { "model": "app01.user", "pk": 3, "fields": {"username": "kevin", "age": 32, "gender": 3}}, { "model": "app01.user", "pk": 4, "fields": {"username": "tank", "age": 40, "gender": 4}} ] 寫接口就是利用序列化組件渲染數據然後寫一個接口文檔 該交代交代一下就完事 """
批量插入
''' 批量插入數據的時候 使用orm給提供的bulk_create能夠大大的減少操作時間 ''' def ab_pl(request): # 先給Book插入一萬條數據 # for i in range(10000): # models.Book.objects.create(title='第%s本書'%i) # # 再將所有的數據查詢並展示到前端頁面 book_queryset = models.Book.objects.all() # 批量插入 # book_list = [] # for i in range(100000): # book_obj = models.Book(title='第%s本書'%i) # book_list.append(book_obj) # models.Book.objects.bulk_create(book_list) """ 當你想要批量插入數據的時候 使用orm給你提供的bulk_create能夠大大的減少操作時間 :param request: :return: """ return render(request,'ab_pl.html',locals())
自己寫一個分頁器(只需掌握分頁器的推到思路即可)
""" 總數據100 每頁展示10 需要10 總數據101 每頁展示10 需要11 總數據99 每頁展示10 需要10 在製作頁碼個數的時候 一般情況下都是奇數個 符合中國人對稱美的標準 """ '''分頁器邏輯推導''' # 當前頁面 current_page = request.GET.get('page') count_book = models.Book.objects.count() # 每頁數據量 per_page_num = 10 # 解壓複製獲取總頁數以及多出的數據 count_page, more = divmod(count_book, per_page_num) if more: # 多處數據存在則總頁數+1 count_page += 1 # 將前端傳回來的字符串頁面數轉成整形,頁面不存在則設置當前頁爲第一頁 try: current_page = int(current_page) if current_page > count_page: current_page = 1 except Exception: current_page = 1 # 起始位置 start_page = (current_page - 1) * per_page_num # 終止位置 end_page = current_page * per_page_num page_html = '' is_active = current_page if current_page < 6: current_page = 6 for i in range(current_page-5, current_page+6): # 限制頁碼顯示個數爲11 if is_active == i: # 在後端寫好html代碼然後前端通關轉義直接使用 page_html += '<li class="page-item active"><a class="page-link" href="?page=%s">%s</a></li>' % (i, i) else: page_html += '<li class="page-item"><a class="page-link" href="?page=%s">%s</a></li>' % (i, i) book_queryset = models.Book.objects.all()[start_page:end_page] return render(request, 'ab_pl.html', locals()) ''' django有自帶的分頁器模塊但是書寫起來太過麻煩且功能太簡單,因此推薦使用自定義分頁器 對於上述分頁器代碼,只需要知道內部邏輯推導即可, 基於上述思路已經封裝好了自定義分頁器,之後有需要直接拷貝使用即可 '''
自定義分頁器封裝代碼
自定義封裝代碼
# 自定義分頁器 class Pagination(object): def __init__(self, current_page, all_count, per_page_num=2, pager_count=7): """ 封裝分頁相關數據 :param current_page: 當前頁 :param all_count: 數據庫中的數據總條數 :param per_page_num: 每頁顯示的數據條數 :param pager_count: 最多顯示的頁碼個數 """ try: current_page = int(current_page) except Exception as e: current_page = 1 if current_page < 1: current_page = 1 self.current_page = current_page self.all_count = all_count self.per_page_num = per_page_num # 總頁碼 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count self.pager_count_half = int((pager_count - 1) / 2) @property def start(self): return (self.current_page - 1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num def page_html(self): # 如果總頁碼 < 11個: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 總頁碼 > 11 else: # 當前頁如果<=頁面上最多顯示11/2個頁碼 if self.current_page <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 當前頁大於5 else: # 頁碼翻到最後 if (self.current_page + self.pager_count_half) > self.all_pager: pager_end = self.all_pager + 1 pager_start = self.all_pager - self.pager_count + 1 else: pager_start = self.current_page - self.pager_count_half pager_end = self.current_page + self.pager_count_half + 1 page_html_list = [] # 添加前面的nav和ul標籤 page_html_list.append(''' <nav aria-label='Page navigation example'> <ul class='pagination'> ''') first_page = '<li class="page-item"><a class="page-link" href="?page=%s">首頁</a></li>' % (1) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '<li class="disabled page-item"><a class="page-link" href="#">上一頁</a></li>' else: prev_page = '<li class="page-item"><a class="page-link" href="?page=%s">上一頁</a></li>' % (self.current_page - 1,) page_html_list.append(prev_page) for i in range(pager_start, pager_end): if i == self.current_page: temp = '<li class="active page-item"><a class="page-link" href="?page=%s">%s</a></li>' % (i, i,) else: temp = '<li class="page-item"><a class="page-link" href="?page=%s">%s</a></li>' % (i, i,) page_html_list.append(temp) if self.current_page >= self.all_pager: next_page = '<li class="disabled page-item"><a class="page-link" href="#">下一頁</a></li>' else: next_page = '<li class="page-item"><a class="page-link" href="?page=%s">下一頁</a></li>' % (self.current_page + 1,) page_html_list.append(next_page) last_page = '<li class="page-item"><a class="page-link" href="?page=%s">尾頁</a></li>' % (self.all_pager,) page_html_list.append(last_page) # 尾部添加標籤 page_html_list.append(''' </nav> </ul> ''') return ''.join(page_html_list)
自定義分頁器----後端使用
def get_book(request): book_list = models.Book.objects.all() current_page = request.GET.get("page",1) all_count = book_list.count() page_obj = Pagination(current_page=current_page,all_count=all_count,per_page_num=10) page_queryset = book_list[page_obj.start:page_obj.end] return render(request,'booklist.html',locals())
自定義分頁器----前端使用
<div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> {% for book in page_queryset %} <p>{{ book.title }}</p> {% endfor %} {{ page_obj.page_html|safe }} </div> </div> </div>