使用form表單向後端提交數據時,必須將form表單的method由默認的get改成post,若是提交的數據中包含文件,還要將form表單的enctype由默認的"application/x-www-form-urlencoded"修改成"multipart/form-data"。css
咱們能夠經過谷歌瀏覽器-》檢查 中的Network查看網絡請求的詳細信息。html
以form表單爲例,其中代碼以下(用Bootstrap裝飾了一下):前端
輸入用戶名密碼,而後隨便選一個文件點提交:python
點擊view source查看原生數據:jquery
隨後發現後端能拿到文件,不過只是文件名而已:ajax
隨後修改將enctype修改成"multipart/form-data",而後再次提交該文件:數據庫
此時原生數據中file看不到了,不事後端能夠看到request.FILES中收到了真實的文件,使用GET能夠拿到對應的文件對象。django
先後端傳輸數據編碼格式contentType:編程
1. application/x-www-form-urlencodedjson
2. multipart/form-data
注意:django會將urlencoded編碼的數據解析自動放到request.POST,即便修改了編碼格式,只要有其中有普通的鍵值對,都能經過request.POST取到數據。
前端朝後端發送請求的方式有四種:
前面三種咱們都已經接觸過了,接下來來看看第四種ajax。
ajax主要由四個部分組成:
$('#d1').click(function () { $.ajax({ // 提交的地址,不寫默認提交至當前頁面,同form表單的action url:'/index/', // 提交的方式 type:'post', // 提交的數據,通常以鍵值對的形式出現 data:{'name':'jason','password':'123'}, // 回調函數 success:function (data) { // data接收的就是異步提交返回的結果 alert(data) } }) })
注意:ajax傳輸數據的默認編碼格式也是urlencoded。先後端傳輸數據時,數據格式與編碼要一一對應,好比傳輸文件就要將編碼改成formdata。
假設有三個input框,需求是前兩個input框輸入數字,點擊提交按鈕後將結果顯示在第三個input中,中途頁面不刷新且加法運算要經過後端實現。
大體思路以下:
首先假設用戶輸入的均爲數字,因此咱們不作任何檢驗。而後ajax是異步JavaScript和XML,JQuery內部封裝了JavaScript,咱們這裏使用JQuery的語法來獲取input框中輸入內容(JQuery_obj.val()),而後使用ajax的回調函數seccess來將加法運算的結果顯示在第三個input框中(JQuery_obj.val(result))。
代碼以下(前端未作任何裝飾,可能有點醜):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test1</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <input type="text" id="s1">+<input type="text" id="s2">=<input type="text" id="s3"> <button id="b1">提交</button> <script> $('#b1').on('click', function () { $.ajax({ url: '/test1/', //不寫默認提交至當前頁面 type: 'post', data:{'s1':$('#s1').val(),'s2':$('#s2').val()}, success:function (data) { $('#s3').val(data) } }) }) </script> </body> </html>
def test1(request): if request.method == 'POST': print(request.POST) s1 = request.POST.get('s1') s2 = request.POST.get('s2') # 拿到的前端數據均爲字符串格式,因此須要類型轉換 res = int(s1) + int(s2) return HttpResponse(res) return render(request, 'test1.html')
各編程語言與前端數據傳輸一般使用json格式,由於json支持多種語言,各編程語言都有相應的json語法。在python中是使用json.dumps()和json.loads()分別實現對象的序列化和反序列化。而JavaScript中是使用JSON.stringify()和JSON.parse()分別實現對象的序列化和反序列化。
向後端傳輸JSON數據時,須要修改編碼類型,否則會出現如下狀況:
以上狀況的出現是由於編碼與數據格式不匹配形成的,你傳輸的是JSON格式字符串,而編碼urlencode卻讓後端拿到的是字典,因此就直接把你的JSON字符串當初字典的key,value拿個空來本身造一個字典出來了。
爲了統一編碼和數據格式,須要將編碼設置爲‘application/json’。
代碼以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test1</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <button id="b1">提交</button> <script> $('#b1').on('click', function () { $.ajax({ url:'', type:'post', data:JSON.stringify({'name': 'json', 'password': '123'}), contentType:'application/json', success:function (data) { alert(data) } }) }) </script> </body> </html>
def test1(request): if request.method == 'POST': import json # 編碼改爲application/json以後,傳輸過來的是二進制數據,存在request.body中 dic = json.loads(request.body.decode('utf-8')) print(dic, type(dic)) return HttpResponse('get it') return render(request, 'test1.html')
傳輸文件相比傳輸json字符串要複雜一些,首先要想辦法把用戶上傳的文件取出來,這須要用到JavaScript中的FormData對象的方法,其次仍是要統一數據格式與編碼,將編碼改成false(由於formdata內部有自帶一個編碼)。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test1</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> </head> <body> file:<input type="file" name="myfile" id="f1"> <button id="b1">提交</button> <script> $('#b1').on('click', function () { let formdata = new FormData();//生成一個FormData對象 //$('#f1')[0]獲得的是JQuery對象中的第一個JS對象,JS_obj.files拿到全部的文件,經過索引取第一個文件 formdata.append('file', $('#f1')[0].files[0]); $.ajax({ url:'', type:'post', contentType:false,//用FormData自帶的編碼,因此不須要設置contentType processData:false,//告訴瀏覽器不要處理數據 data: formdata,//直接將formdata提交至後端便可 success:function (data) { alert(data) } }) }) </script> </body> </html>
def test1(request): if request.method == 'POST': # 沒有普通鍵值對,因此request.POST是空 print(request.POST) print(request.FILES) # request.FIlES.get('file')拿出來的是文件對象 file_obj = request.FILES.get('file') # 保存文件至本地 with open(file_obj.name, 'wb') as f: for line in file_obj: f.write(line) return HttpResponse('get it') return render(request, 'test1.html')
初次接觸ajax,對ajax理解只是一點點皮毛,今天受一個朋友影響,對ajax的理解加深了一點點,在此感謝CC teacher。
接下來講的方法可能有點雞肋,但是又有點意思。拿圖書管理系統舉例,點擊添加做者,直接在當前頁面局部刷新來實現,不用標籤的隱藏等各類方法,就只用ajax來實現(就是那麼頭鐵)。
大體思路:
{% extends 'home.html' %} {% block content %} <div id="d1"> <button id="i1" class="btn btn-info">添加</button> <table class="table table-hover table-striped table-bordered"> <thead> <tr> <th>id</th> <th>name</th> <th>age</th> <th>gender</th> <th>phone</th> <th>addr</th> <th>action</th> </tr> </thead> <tbody> {% for author in author_list %} <tr> <td>{{ author.pk }}</td> <td>{{ author.name }}</td> <td>{{ author.age }}</td> <td>{{ author.get_gender_display }}</td> <td>{{ author.authordetail.phone }}</td> <td>{{ author.authordetail.addr }}</td> <td> <a href="{% url 'author_edit' author.pk %}" class="btn btn-success">編輯</a> <a href="{% url 'author_delete' author.pk %}" class="btn btn-danger">刪除</a> </td> </tr> {% endfor %} </tbody> </table> </div> {% endblock %} {% block js %} <script> $('#i1').click(function () { $.ajax({ url: '{% url "author_add" %}', type: 'post', data: {'type': '333'}, success: function (data) { //將id爲d1的div標籤中的html換成data變量中存的數據 $('#d1').html(data) } }) }) </script> {% endblock %}
form表單和ajax是能夠一塊兒使用的,不過這樣感受沒什麼意義,試驗了一下,並無出現網上說的錯誤。ajax綁定form表單中的button按鈕及submit按鈕,二者效果同樣,後端先獲取到ajax提交的數據,而後再獲取到from表單中的數據,有興趣能夠自行試驗。
注意:雖然ajax是異步提交(GitHub註冊示例)、局部刷新,可是並非全部ajax使用的越多越好,由於ajax異步的回調函數會向後端詢問執行的結果,當同時有不少該請求時,服務端會有很大的負擔。
先在數據庫中建立多條數據,而後展現在頁面上,這裏以1000條爲例。
def test1(request): for i in range(100): models.Book2.objects.create(name='第%s本' % i) book_list = models.Book2.objects.all() return render(request, 'test1.html', locals())
以上這麼增長數據時,發現要在頁面等一段時間纔會有數據顯示,由於寫入數據庫要時間,前端只能等待數據寫入數據庫結束。這種狀況須要用到批量插入bulk_create:
def test1(request): # 定義一個列表 book_list = [] for i in range(100): # 實例化出Book2的對象,並將其加入列表 book_list.append(models.Book2(name='第%s本' % i)) # 這就是批量導入的精髓,至關於異步,程序執行無需等待該代碼執行完畢,可直接去執行後續代碼 models.Book2.objects.bulk_create(book_list) # 隨後前端直接可使用book_list中的書籍對象點屬性去展現內容,不須要等數據庫中數據寫入完畢 return render(request, 'test1.html', locals())
上述使用的是列表,可是當列表中數據不少時,會佔用不少的內存,能夠採用生成器的方式來進行優化:g = (models.book(name = '第%s本' % i for i in range(10000)))。
分頁器是須要考慮一共有幾頁的,這須要依據數據總條數來定,其次分頁器每一次只顯示幾個按鈕,這就意味着咱們是無法在前端來完成這個動態的過程的,只能在後端完成,這時候須要用到後端或者前端的取消轉義語法。
html = '' for i in range(1,pager_nums+1): html += '<li><a href="?page=%s">%s</a></li>'%(i,i)
最終代碼以下:
class Pagination(object): def __init__(self, current_page, all_count, per_page_num=2, pager_count=11): """ 封裝分頁相關數據 :param current_page: 當前頁 :param all_count: 數據庫中的數據總條數 :param per_page_num: 每頁顯示的數據條數 :param pager_count: 最多顯示的頁碼個數 用法: queryset = model.objects.all() page_obj = Pagination(current_page,all_count) page_data = queryset[page_obj.start:page_obj.end] 獲取數據用page_data而再也不使用原始的queryset 獲取前端分頁樣式用page_obj.page_html """ 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>' <ul class='pagination'> ''') first_page = '<li><a href="?page=%s">首頁</a></li>' % (1) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '<li class="disabled"><a href="#">上一頁</a></li>' else: prev_page = '<li><a 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"><a href="?page=%s">%s</a></li>' % (i, i,) else: temp = '<li><a 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"><a href="#">下一頁</a></li>' else: next_page = '<li><a href="?page=%s">下一頁</a></li>' % (self.current_page + 1,) page_html_list.append(next_page) last_page = '<li><a 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)
先在應用下新建一個叫utils的文件夾,裏面建立一個.py文件,將該代碼拷貝進去保存。而後將該py文件導入到views.py中。
from django.shortcuts import render,HttpResponse from app01 import models # 導入拷貝了上述分頁器代碼的py文件 from app01.utils import my_page def booklist(request): book_list = models.Book2.objects.all() # 拿到數據總條數 all_count = book_list.count() # 獲得當前頁面,前端的分頁器被點擊時返回page,標明用戶點擊的頁編號 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] return render(request,'booklist.html',locals())