Django與Ajax
- 2019 年 10 月 3 日
- 筆記
一. MTV與MVC
MTV模型(django): M:models.py T: templates V: views
MVC模型: M:模型層(models.py) T: 視圖層(views.py) V: 控制層(Controller)
本質: Django的MTV也是MVC
二. 多對多的三種創建方式
1. 第一種Django orm自動幫我們創建
class Book(models.Model) name = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author') class Author(mofels.Model) name = models.CharField(max_length=32)
創建這張表時,會自動告訴Django幫我們進行第三張表的創建,創建的名稱為app01_Book_Author
2. 手動創建第三張表(用foreignkey創建時,mysql會自動在欄位名後面加_id,那我們在創建的時候在模型層就沒有必要在欄位名後面手動添加_id)
class Book(models.Model) name = models.CharField(max_length=32) class Author(mofels.Model) name = models.CharField(max_length=32) class Book2Author(models.Model) book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') info = models.CharField(max_length=32)
純手動創建第三張表,好處在於可以給第三張表中添加其他欄位,如果在orm中創建表時,不能再新加其他其他欄位,純手動創建的表不支援反向查詢例子奉上,基於雙下劃綫

基於對象的反向查詢:

3. 半自動創建第三張表
class Book(models.Model) name = models.CharField(max_length=32) # 第三種創建表的方式 authors = models.ManyToManyField(to='Author', through='Book2Author', through_fields=('book', 'author') class Author(mofels.Model) name = models.CharField(max_length=32) class Book2Author(models.Model) book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') info = models.CharField(max_length=32)
通過through來告訴orm book與author已經創建第三張表book2author, book與author已經創建book2author這張表,orm不用再創建了, thorugh_fields表示的是雖然orm知道book與author創建關係,但是純手動創建的表可以再添加欄位,也就是說orm不知道第三張表中哪個欄位與那個欄位有關聯,通過through_fields可以清楚的知道哪張表與哪張表有關係,比如想從第三張表中查出第一張表,通過什麼能查到呢,在本例子中是通過book找到Book這張表,那麼through_fields的第一個參數就是book,第二個參數表示book這張表與哪張表有關係,就寫那張表名小寫.
這種方法既可以支援在第三張表中添加欄位,也可以支援反向查詢(可擴展性強)

查詢第三張表中新加的欄位

三. 前後端傳輸數據編碼格式contentType
urlencoded:
對應的數據格式: name=jason&password=123
後端獲取數據: request.POST
ps: django會將urlencoded編碼的數據解析自動放在request.POST中
formdata:
form表單傳輸文件的編碼格式
後端獲取文件格式的數據:request.FILES
後端獲取普通鍵值對數據: request.POST
Django後端針對只要是符合urlencoded編碼格式的數據都會自動解析到request.POST中
四. ajax
1. 前端向後端發送請求的方式:
瀏覽器中手動輸入網址: get請求
a標籤的href屬性: get請求
form表單: get/post請求(默認為get請求)
ajax: get/post請求
2. ajaxt特點: 非同步提交, 局部刷新
ajax不是新的語言,而是一種現有標準的新方法,基於JavaScript
同步交互: 客戶端發出一個請求之後,需要等待伺服器響應結束,才能發出第二個請求; (form表單提交)
非同步交互: 客戶端發出一個請求之後,無需等待伺服器響應結束,就可以發出第二個請求
ajax最大的優點是在不重新載入整個頁面的情況下,可以與伺服器交換數據並更新部分網頁內容.(這一特點給用戶的感受是在不知不覺中完成請求和響應過程)
ajax基本語法: 提交的地址, 提交的方式,提交的數據, 回調函數
$('#d1').click(function () { $.ajax({ // 提交的地址 url: '/index/', // 提交的方式 type:'post', // 提交的數據 data:{'name': 'bgon', 'password':'123'}, // 回調函數 success:function(data){ // data接收的就是非同步提交返回的結果 alert(data) } }} })
4. ajax默認傳輸數據的編碼格式也是urlencoded
5. 前後端進行數據交互時,前端傳輸什麼格式的數據,後端也要傳輸什麼樣的格式在前端傳輸數據時先聲明下數據的編碼格式,在前端傳輸json格式的數據時,後端會將json格式的數據保存在request.body裡面,這是一個字元類型的數據,我們可以將他進行解碼,然後進行反序列化出原來的樣式.
$('#d1').click(function () { $.ajax({ // 提交的地址 url: '/index/', // 提交的方式 type:'post', // 傳輸格式 contentType:'application/json', // 提交的數據 data:json.stringify{'name': 'bgon', 'password':'123'}, // 回調函數 success:function(data){ // data接收的就是非同步提交返回的結果 alert(data) } }} })
ajax傳輸文件
$('#d1').click(function(){ let formdata = new FormData(); // 實例化產生對象 // FormData對象不僅可以傳輸文件,還可以傳輸普通的鍵值對 formdata.append('name', 'jason'); // 固定這麼寫 // 獲取input框存放的文件,先用jQuery查找存儲文件的標籤,利用jQuery對象轉換成js對象,利用原生js對象的方法.files[0]來獲取到標籤內部存儲的文件對象 // $('#il')[0].files[0] // 先將jquery對象轉換成原生的js對象.然後就支援.files,當前input框中所有所有的文件 formdata.append('myfile , $('#il')[0].files[0]); $.ajax({ url:'', type:'post', data: formdata, // ajax發送文件需要修改兩個固定的參數 processData: false, // 告訴瀏覽器不要處理我的數據 contentType: false, // 不要用任何的編碼,我就用formdata自帶的編碼格式 // 回調函數 success: function (data) { alert } }) }); ajax傳輸文件需要藉助內置對象FormDate
form表單與ajax異同點:
1. form表單不支援非同步提交局部刷新
2. form表單不支援傳輸json格式的數據
3. form表單與ajax默認傳輸數據的編碼格式都是urlencoded
特別提醒: 在用ajax傳輸數據時,一定要注意傳輸的類型與編碼格式一致
五. 批量插入數據
批量插入數據 l = [] for i in range(10000): l.append(models.Book2(name='第%s本書'%i)) models.Book2.objects.bulk_create(l) # 批量插入數據
六. 分頁器
後端: book_list = models.Book2.objects.all() # 數據總條數 all_count = book_list.count() # 當前頁 current_page = request.GET.get('page',1) # 示例一個分頁器對象 page_obj = my_page.Pagination(current_page=current_page,all_count=all_count) # 對總數據進行切片 page_queryset = book_list[page_obj.start:page_obj.end] 前端: {{ page_obj.page_html|safe }} # 幫你渲染的是帶有bootstrap樣式的分頁器
分頁器組件
1 class Pagination(object): 2 def __init__(self,current_page,all_count,per_page_num=2,pager_count=11): 3 """ 4 封裝分頁相關數據 5 :param current_page: 當前頁 6 :param all_count: 資料庫中的數據總條數 7 :param per_page_num: 每頁顯示的數據條數 8 :param pager_count: 最多顯示的頁碼個數 9 10 用法: 11 queryset = model.objects.all() 12 page_obj = Pagination(current_page,all_count) 13 page_data = queryset[page_obj.start:page_obj.end] 14 獲取數據用page_data而不再使用原始的queryset 15 獲取前端分頁樣式用page_obj.page_html 16 """ 17 try: 18 current_page = int(current_page) 19 except Exception as e: 20 current_page = 1 21 22 if current_page <1: 23 current_page = 1 24 25 self.current_page = current_page 26 27 self.all_count = all_count 28 self.per_page_num = per_page_num 29 30 31 # 總頁碼 32 all_pager, tmp = divmod(all_count, per_page_num) 33 if tmp: 34 all_pager += 1 35 self.all_pager = all_pager 36 37 self.pager_count = pager_count 38 self.pager_count_half = int((pager_count - 1) / 2) 39 40 @property 41 def start(self): 42 return (self.current_page - 1) * self.per_page_num 43 44 @property 45 def end(self): 46 return self.current_page * self.per_page_num 47 48 def page_html(self): 49 # 如果總頁碼 < 11個: 50 if self.all_pager <= self.pager_count: 51 pager_start = 1 52 pager_end = self.all_pager + 1 53 # 總頁碼 > 11 54 else: 55 # 當前頁如果<=頁面上最多顯示11/2個頁碼 56 if self.current_page <= self.pager_count_half: 57 pager_start = 1 58 pager_end = self.pager_count + 1 59 60 # 當前頁大於5 61 else: 62 # 頁碼翻到最後 63 if (self.current_page + self.pager_count_half) > self.all_pager: 64 pager_end = self.all_pager + 1 65 pager_start = self.all_pager - self.pager_count + 1 66 else: 67 pager_start = self.current_page - self.pager_count_half 68 pager_end = self.current_page + self.pager_count_half + 1 69 70 page_html_list = [] 71 # 添加前面的nav和ul標籤 72 page_html_list.append(''' 73 <nav aria-label='Page navigation>' 74 <ul class='pagination'> 75 ''') 76 first_page = '<li><a href="?page=%s">首頁</a></li>' % (1) 77 page_html_list.append(first_page) 78 79 if self.current_page <= 1: 80 prev_page = '<li class="disabled"><a href="#">上一頁</a></li>' 81 else: 82 prev_page = '<li><a href="?page=%s">上一頁</a></li>' % (self.current_page - 1,) 83 84 page_html_list.append(prev_page) 85 86 for i in range(pager_start, pager_end): 87 if i == self.current_page: 88 temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,) 89 else: 90 temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,) 91 page_html_list.append(temp) 92 93 if self.current_page >= self.all_pager: 94 next_page = '<li class="disabled"><a href="#">下一頁</a></li>' 95 else: 96 next_page = '<li><a href="?page=%s">下一頁</a></li>' % (self.current_page + 1,) 97 page_html_list.append(next_page) 98 99 last_page = '<li><a href="?page=%s">尾頁</a></li>' % (self.all_pager,) 100 page_html_list.append(last_page) 101 # 尾部添加標籤 102 page_html_list.append(''' 103 </nav> 104 </ul> 105 ''') 106 return ''.join(page_html_list)
分頁器組件
當我們在使用分頁器的時候可以在app01下建立一個utils的文件夾用來存放分頁器組件程式碼
