# 課程鏈接:
全新 django3 入門到項目實戰(零基礎學django、項目開發實踐、大學畢業設計均可用)_哔哩哔哩_bilibili
# 課程覆蓋:
管理員模塊_(4-7 ~~~ 4-10 的課程)
# (1)登錄頁面
1)登陸頁面這裡直接去 bootstrap 的官網上扒拉一個登錄的模板就行。那麼具體的就是這樣的(當然沒這麼丑,這是我改的)。具體的 html 代碼就不給了,很簡單。那麼我們在路由上添加了 相應的 admin/login 的路由函數。經過 render 請求就可以查看頁面了。
2)因為這一次要使用 forms 來做,因此我們首先復習一下 forms 。如下圖所示。我們可以看到占位符的 placeholder 不夠聰明,那麼為了解決這個問題,我們使用之前的封裝的 bootstrap 代碼來做。
下面是之前 bootstrap 的詳細代碼,我們可以看到,這是繼承自 forms.modelform 寫的,但是對於我們這次的任務而言,必須再寫一個繼承自 forms.Form 的 bootstrap 代碼。然而這樣就造成了代碼冗余了。
代碼冗余的解決方法很簡單,因為我們上面是兩個類,使用的是同一個代碼,但是繼承的父類又不同,因此我們可以封裝一個總的類,這裡放著共有的代碼。然後再寫兩個類,分別繼承上面的總類和各自的父類。這樣就可以了。如下面的代碼
那麼我們再使用的時候,只需要根據我們當前的路由函數是使用的 Form 還是ModelForm 來自由選擇對應的 bootstrap 樣式插件就行。下圖可以看到,我們直接繼承修改後的 bootstrap 插件,因為我門使用的是 Form ,所以我們導入 BootStrapForm
3)添加錯誤信息。在前端的 html 頁面中,我們添加一個 span 標簽包裹這錯誤信息的 { { field.errors.0}} ,其實 field.errors 應該是個對象,裡面含有多種錯誤信息提示,我們只是使用了第一種。當然了在使用前,我們還要將浏覽器自帶的校驗給關了,即在 form 表單中的 novalidate。
於是,在後端的路由函數中,我們直接進入 post 請求,那麼我們實例化 LoginForm(data=request.POST) 之後,得到的就是所輸入的信息了,這裡不管輸入是否為空。然後進行數據校驗,如果為空,我們還是返回登錄頁面,並且傳回錯誤信息的 form 。顯然如果不為空,那麼我們判斷所輸入信息(也就是form.cleaned_data)是否在數據庫中,如果不在,那麼返回值 admin_info 為 None ,這樣子的話,我們可以使用 form 自定義添加錯誤提示,來新增錯誤提示。也就是語句 form.add_error("a", "b"),其中 a 為字段名, b 為提示的信息。如果
這裡補充一下,我們先前對密碼是進行了 md5() 加密的。那麼具體該其實在 Form 中也和 ModelForm 相同就是在類中定義相應的鉤子方法。【可以看到類中,我們定義的字段,如 username 這些有個 required 屬性,如果為 True,就意味著這個地方不能為空,默認的就是 True】
# (2)cookie 和 session 的應用
1)前面講過 cookie 和 session 的理論原理,也就是以下的步驟,可以結合下圖來看。
第一: 用戶密碼校驗成功後,網站生成隨機字符串;
第二: 寫到用戶浏覽器的 cookie 中;
第三: 再寫入到 session 中。
2)再 Django 中,其實就已經封裝了上述的步驟,即 request.session["info"] = "xxx" 語句。那麼通過這個語句,我們能夠完成上述的步驟,然後相應的 info 就是 xxx。具體可以參考上一節的筆記。如下圖所示,我們知道session隊列中,每一個塊都有一個憑證,每個憑證都有其對應的區內信息(假定只有 info )。如果我們寫定了 request.session["info"] = "s",那麼整個 session 隊列的不同憑證對應的區內信息都是 "s",這沒意義。
因此,我們需要讓 info 字段能夠與其憑證相關聯。那麼如果寫成下面紅框的樣子就可以了,比如輸入的是 "梁旭" 的用戶信息 ,那麼之後 info 裡面就根據數據庫取得的結果,將 "梁旭" 和 其 "id" 取出,拼接成一個字典,也就是說: {"id": 1, "name": "梁旭"},這樣就能夠讓憑證內信息動態起來,並且與其憑證相關聯了。最後我們選擇重定向回管理員列表。
3)Django 隱含了cookie 和 session 的步驟。那麼我們如何查看浏覽器給我們生成的憑證(也就是結構圖的那堆隨機字符串)。有兩個方法,一種是在浏覽器中;還有一種就是在數據庫中。 下圖是浏覽器的。
那麼數據庫中的如下圖,可以看到不管是浏覽器還是數據庫的憑證都是一樣的。當然了,我們也可以看到數據庫中,在憑證後面還要相應的 session_data ,可以理解為就是將我們先前說的 info 數據(我們當時是以字典類型存入的)進行了加密,使用時系統會幫我們進行反解密的,這不是我關注的內容啦。
# (1)認識中間件之前。
1)在前面我們提到了,如果用戶沒有登錄的時候,很多頁面都是用不了的,比如說下圖框住的選項框。 具體的邏輯是在點擊這些頁面的時候需要驗證一下當前是否有用戶登錄,如果有的話,就可以訪問;否則,跳轉到登錄頁面。
2)如下圖,拿 admin/list 為例,詳細的注解見注釋。這裡有個問題,為什麼不是 request.session['info'] ,而是 request.session.get('info') ,這是因為字典數據類的兩種取值方式,前者如果取字典不存在的字段就會報錯,而後者則會返回 None,因此使用後者能夠實現流程控制。如果使用這種方法,我們需要對每一個需要登錄才能訪問的頁面進行這樣的流程判斷,如此就很繁瑣了。【就是要在所有的視圖函數中,加入下面的代碼,這是很智障的操作,不夠靈活】
# (2)中間件處理(初識)
1)為了解決上面繁瑣的問題,我們需要認識什麼是中間件。如下圖所示,A、B、C 是三個中間件。對於浏覽器的請求,會首先通過 A、B、C 這三個中間件,然後再執行視圖函數,執行視圖函數的 return 後也要穿過這三個中間件,再返回給浏覽器。
2)那麼再 Django 中的中間件就是一個類,然後類裡面有兩個,進來的時候有 process_request() 方法,執行完視圖函數返回時,又有 process_response() 方法執行完後,就是這麼個流程。如下圖所示
為了更詳細說一下中間件這個類,就比如下面的代碼,就是一組中間件的兩個方法了。
當然了,如果某個中間件不允許請求繼續往後了,那麼請求就直接在該中間件返回了,到達不了視圖函數。即下面的圖示。基於這個思路,我們就可以做一個中間件來判斷用戶是否登錄。如果登錄了,請求就繼續到達視圖函數,否則,直接就從當前中間件 response 回去了。
# (3)中間件處理(體驗環節)
1)我們拋開上面的,單純的來嘗試一下中間件。如下圖所示,我們需要知道,所定義的中間件需要繼承自 from django.utils.deprecation import MiddlewareMixin 。
2)要應用的時候,需要到 settings.py 文件中的 MIDDLEWARE 這個列表中添加我們自定義的中間件,誰在前面就先執行哪個中間件。如下圖所示。
3)比如我們隨便訪問一個頁面,可以看到輸出的結果如下圖所示。可以看到流程和我們之前說的時一致的。
我們注意到,原先我們定義的中間件的類中,process_request() 方法是沒有返回值的,事實上是返回 None。如果我們再 process_request() 方法中定義了 return 的值,那麼執行完當前中間件就直接返回浏覽器了,不再往下執行。那麼如下圖所示
# (4)中間件處理(實戰)
1)在 Django 中,在用戶沒有登錄的情況下,浏覽器去發起請求的時,中間件會返回沒有登錄的信息,但是浏覽器並不知道中間件這個過程,因而就會繼續嘗試訪問這個視圖函數,以此無限循環。因此,我們需要排除掉不需要登錄就能訪問的頁面,也就是說,我們再次訪問浏覽器的時候,檢測一下當前浏覽器請求的 url,這個 url 就是我們要排除的。
2)代碼實現,務必記得在 settings.py 上面注冊。
# (1)用戶注銷
1)如下圖所示,為注銷的原理。
2)Django 中實現這一個步驟其實很簡單,我們可以使用 request.session.clear() 方法就可以輕松實現注銷的目的。我們需要為注銷的功能同樣的定義一個注銷函數。那麼具體的代碼如下。同時,我們要在相關的母版頁面 layout.html 把注銷指向的路徑給寫好。
# (2)用戶登錄的名稱問題
1)我們注意到,用戶登錄的時候,不管怎麼樣都是梁旭,這不是我們想要的。我們要求用戶名這裡需要根據登錄用戶的名稱來賦相應的用戶名。
2)這個時候我們就可以使用傳回的 request 參數了,我們在母版 layout.html 頁面中,直接添加 { {request.session.info.name}} 即可。
注意這裡面為什麼是 info,因為我們寫入了 info 這個字典。即下圖示。
# (1)圖片驗證碼
1)圖片驗證碼是用來防止黑客暴力破解用戶的賬戶密碼的。這點很好理解,有驗證碼的話能夠讓黑客每次嘗試密碼的時候,多一份工作,比如自動填寫驗證碼就很麻煩,除非用 ocr 去做。【後面會深入說的】
# (2)pillow 模塊的使用。
1)基本的方法使用之使用 pillow 庫(即 PIL)創建圖片,那麼具體的使用如下面代碼和注釋。
2)畫筆工具 ImageDraw,如下圖所示
3)利用畫筆工具來畫點。在 2)中我們利用畫筆工具 ImageDraw.Draw() 方法創建了 draw 畫筆對象。然後我們可以利用這個畫筆對象來進行塗畫的操作。下面是具體的代碼。
我們執行了 img.show() 方法來查看實際的效果,如下圖所示:
4)利用畫筆對象來進行畫線操作。
效果如圖所示,
5)利用畫筆對象來畫圓。這裡特別注意就是起始點和結束點的兩個坐標並不是說是畫圓的開始,而是這個圓介於這兩點坐標之間。可以理解為以 (x0, y0)為左上角坐標,(x1, y1)為右下角坐標所圍成的正方形的內切圓。
那麼我們具體打點看的結果如下:
6)利用畫筆對象寫文字
結果如圖:
7)設置特殊字體文字。具體見下面的注釋。
輸出結果如圖所示:
# (3)基於 pillow 的圖片驗證碼模塊。
1)直接給出代碼。這個是來自於 武sir 博客園的。那麼我們可以將這個代碼做成相應的插件供後台使用。至於代碼邏輯其實也很簡單的,這裡不贅述。再次感謝 武sir !!!
import random from PIL import Image, ImageDraw, ImageFont, ImageFilter def check_code(width=120, height=30, char_length=5, font_file='kumo.ttf', font_size=28): code = [] img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') def rndChar(): """ 生成隨機字母 :return: """ return chr(random.randint(65, 90)) def rndColor(): """ 生成隨機顏色 :return: """ return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255)) # 寫文字 font = ImageFont.truetype(font_file, font_size) for i in range(char_length): char = rndChar() code.append(char) h = random.randint(0, 4) draw.text([i * width / char_length, h], char, font=font, fill=rndColor()) # 寫干擾點 for i in range(40): draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) # 寫干擾圓圈 for i in range(40): draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) x = random.randint(0, width) y = random.randint(0, height) draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor()) # 畫干擾線 for i in range(5): x1 = random.randint(0, width) y1 = random.randint(0, height) x2 = random.randint(0, width) y2 = random.randint(0, height) draw.line((x1, y1, x2, y2), fill=rndColor()) # img.filter() 用來做圖像濾波,ImageFilter.EDGE_ENHANCE_MORE 是圖像增強的方式,可以深度加強邊緣 # 參考資料:https://wenku.baidu.com/view/b28e261d64ec102de2bd960590c69ec3d5bbdbab.html img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) return img, ''.join(code)
# (4)代碼實現。
1)我們回到前端的 login.html 頁面,把驗證碼的那一塊寫好(這裡我們去 pythonav資源網可以直接拿人家的 html 代碼用).下面紅框是我們圖片驗證碼的視圖函數 url。
2)視圖函數的編寫。
結果如下圖所示,紅框處就是我們自己生成的驗證碼了: