Django框架之——模板
模板
作為Web框架,Django提供了模板,用於編寫html程式碼,還可以嵌入模板程式碼更快更方便的完成頁面開發,再通過在檢視中渲染模板,將生成最終的html字串返回給客戶端瀏覽器。模版致力於表達外觀,而不是程式邏輯。模板的設計實現了業務邏輯view與顯示內容template的分離,一個檢視可以使用任意一個模板,一個模板可以供多個檢視使用。
模板包含兩部分:
- 靜態部分,包含html、css、js。
- 動態部分,就是模板語言。
Django模板語言,簡寫DTL,定義在django.template包中。 建立專案後,在"專案名稱/settings.py"檔案中定義了關於模板的配置。
DIRS定義一個目錄列表,模板引擎按列表順序搜尋這些目錄以查詢模板檔案,通常是在專案的根目錄下建立templates目錄。
Django處理模板分為兩個階段:
- 1.載入:根據給定的路徑找到模板檔案,編譯後放在記憶體中。
- 2.渲染:使用上下文資料對模板插值並返回生成的字串。
為了減少開發人員重複編寫載入、渲染的程式碼,Django提供了簡寫函式render,用於呼叫模板。
模板語言
模板語言包括4種類型,分別是:
- 變數
- 標籤
- 過濾器
- 註釋
接下來逐個介紹4種類型。
模板變數
模板變數的作用是計算並輸出,變數名必須由字母、數字、下劃線(不能以下劃線開頭)和點組成。
語法如下:
{{變數}}
當模版引擎遇到點如book.title,會按照下列順序解析:
- 1.字典book['title']
- 2.先屬性後方法,將book當作物件,查詢屬性title,如果沒有再查詢方法title()
- 3.如果是格式為book.0則解析為列表book[0]
如果變數不存在則插入空字串''。
在模板中呼叫方法時不能傳遞引數。
標籤
語法如下:
{%程式碼段%}
for標籤語法如下:
{%for item in 列表%} 迴圈邏輯 {{forloop.counter}}表示當前是第幾次迴圈,從1開始 {%empty%} 列表為空或不存在時執行此邏輯 {%endfor%}
if標籤語法如下:
{%if ...%} 邏輯1 {%elif ...%} 邏輯2 {%else%} 邏輯3 {%endif%}
過濾器
語法如下:
- 使用管道符號|來應用過濾器,用於進行計算、轉換操作,可以使用在變數、標籤中。
- 如果過濾器需要引數,則使用冒號:傳遞引數。
變數|過濾器:引數
長度length,返回字串包含字元的個數,或列表、元組、字典的元素個數。
預設值default,如果變數不存在時則返回預設值。
data|default:'預設值'
日期date,用於對日期型別的值進行字串格式化,常用的格式化字元如下:
- Y表示年,格式為4位,y表示兩位的年。
- m表示月,格式為01,02,12等。
- d表示日, 格式為01,02等。
- j表示日,格式為1,2等。
- H表示時,24進位制,h表示12進位制的時。
- i表示分,為0-59。
- s表示秒,為0-59。
模板繼承
模板繼承和類的繼承含義是一樣的,主要是為了提高程式碼重用,減輕開發人員的工作量。
典型應用:網站的頭部、尾部資訊。
父模板
如果發現在多個模板中某些內容相同,那就應該把這段內容定義到父模板中。
標籤block:用於在父模板中預留區域,留給子模板填充差異性的內容,名字不能相同。 為了更好的可讀性,建議給endblock標籤寫上名字,這個名字與對應的block名字相同。父模板中也可以使用上下文中傳遞過來的資料。
{%block 名稱%}
預留區域,可以編寫預設內容,也可以沒有預設內容
{%endblock 名稱%}
子模板
標籤extends:繼承,寫在子模板檔案的第一行。
{% extends "父模板路徑"%}
子模版不用填充父模版中的所有預留區域,如果子模版沒有填充,則使用父模版定義的預設值。
填充父模板中指定名稱的預留區域。
{%block 名稱%}
實際填充內容
{{block.super}}用於獲取父模板中block的內容
{%endblock 名稱%}
CSRF
CSRF全拼為Cross Site Request Forgery,譯為跨站請求偽造。CSRF指攻擊者盜用了你的身份,以你的名義傳送惡意請求。CSRF能夠做的事情包括:以你名義傳送郵件,發訊息,盜取你的賬號,甚至於購買商品,虛擬貨幣轉賬......造成的問題包括:個人隱私洩露以及財產安全。
CSRF示意圖如下:
如果想防止CSRF,首先是重要的資訊傳遞都採用POST方式而不是GET方式,接下來就說POST請求的攻擊方式以及在Django中的避免。
總結
- 重要資訊如金額、積分等,採用POST方式傳遞
- 啟用CSRF中介軟體,預設啟用
- 在form表單中post提交時加入標籤csrf_token
保護原理
加入標籤後,可以檢視post.html的原始碼,發現多了一個隱藏域。
在瀏覽器的“開發者工具”中檢視cookie資訊。
說明:當啟用中介軟體並加入標籤csrf_token後,會向客戶端瀏覽器中寫入一條Cookie資訊,這條資訊的值與隱藏域input元素的value屬性是一致的,提交到伺服器後會先由csrf中介軟體進行驗證,如果對比失敗則返回403頁面,而不會進行後續的處理。
驗證碼
在使用者註冊、登入頁面,為了防止暴力請求,可以加入驗證碼功能,如果驗證碼錯誤,則不需要繼續處理,可以減輕業務伺服器、資料庫伺服器的壓力。
手動實現驗證碼
接下來的程式碼不要求手動寫出來,因為這種程式碼在網上可以搜到很多。
1)安裝包Pillow3.4.1。
pip install Pillow==3.4.1
點選檢視PIL模組API,以下程式碼中用到了Image、ImageDraw、ImageFont物件及方法。
2)在booktest/views.py檔案中,建立檢視verify_code。
- 提示1:隨機生成字串後存入session中,用於後續判斷。
- 提示2:檢視返回mime-type為image/png。
from PIL import Image, ImageDraw, ImageFont from django.utils.six import BytesIO ... def verify_code(request): #引入隨機函式模組 import random #定義變數,用於畫面的背景色、寬、高 bgcolor = (random.randrange(20, 100), random.randrange( 20, 100), 255) width = 100 height = 25 #建立畫面物件 im = Image.new('RGB', (width, height), bgcolor) #建立畫筆物件 draw = ImageDraw.Draw(im) #呼叫畫筆的point()函式繪製噪點 for i in range(0, 100): xy = (random.randrange(0, width), random.randrange(0, height)) fill = (random.randrange(0, 255), 255, random.randrange(0, 255)) draw.point(xy, fill=fill) #定義驗證碼的備選值 str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0' #隨機選取4個值作為驗證碼 rand_str = '' for i in range(0, 4): rand_str += str1[random.randrange(0, len(str1))] #構造字型物件,ubuntu的字型路徑為“/usr/share/fonts/truetype/freefont” font = ImageFont.truetype('FreeMono.ttf', 23) #構造字型顏色 fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255)) #繪製4個字 draw.text((5, 2), rand_str[0], font=font, fill=fontcolor) draw.text((25, 2), rand_str[1], font=font, fill=fontcolor) draw.text((50, 2), rand_str[2], font=font, fill=fontcolor) draw.text((75, 2), rand_str[3], font=font, fill=fontcolor) #釋放畫筆 del draw #存入session,用於做進一步驗證 request.session['verifycode'] = rand_str #記憶體檔案操作 buf = BytesIO() #將圖片儲存在記憶體中,檔案型別為png im.save(buf, 'png') #將記憶體中的圖片資料返回給客戶端,MIME型別為圖片png return HttpResponse(buf.getvalue(), 'image/png')
3)開啟booktest/urls.py檔案,配置url。
url(r'^verify_code/$', views.verify_code),
4)執行伺服器,在瀏覽器中輸入如下網址。
http://127.0.0.1:8000/verify_code/
5)瀏覽效果如下圖:
可以多重新整理幾次看值會不會變。
呼叫驗證碼
1)在booktest/views.py檔案中,建立檢視verify_show。
def verify_show(request): return render(request,'booktest/verify_show.html')
2)開啟booktest/urls.py檔案,配置url。
url(r'^verify_show/$', views.verify_show),
3)在templates/booktest/目錄下建立verify_show.html。
<html> <head> <title>驗證碼</title> </head> <body> <form method="post" action="/verify_yz/"> {%csrf_token%} <input type="text" name="yzm"> <img id="yzm" src="/verify_code/"/> <span id="change">看不清,換一個</span> <br> <input type="submit" value="提交"> </form> </body> </html>
4)執行伺服器,在瀏覽器中輸入如下網址。
http://127.0.0.1:8000/verify_show/
5)瀏覽效果如下圖:
驗證
1)在booktest/views.py檔案中,建立檢視verify_yz。
def verify_yz(request): yzm=request.POST.get('yzm') verifycode=request.session['verifycode'] response=HttpResponse('no') if yzm==verifycode: response=HttpResponse('ok') return response
2)開啟booktest/urls.py檔案,配置url。
url(r'^verify_yz/$', views.verify_yz),
3)回到瀏覽器後重新整理,在文字框中填寫驗證碼,點選提交按鈕。
4)瀏覽效果如下圖:
反向解析
先看看原來怎麼做
1) 開啟booktest/views.py檔案,建立檢視fan1、fan2。
def fan1(request): return render(request,'booktest/fan1.html') def fan2(request): return HttpResponse('fan2')
2)開啟booktest/urls.py檔案,配置url。
url(r'^fan1/$', views.fan1), url(r'^fan2/$', views.fan2),
3)在templates/booktest/目錄下建立fan1.html。
<html> <head> <title>反向解析</title> </head> <body> 普通連結:<a href="/fan2/">fan2</a> </body> </html>
4)執行伺服器,在瀏覽器中輸入如下網址:
http://127.0.0.1:8000/fan1/
瀏覽效果如下圖:
5)點選連結後轉向fan2,效果如下圖:
6)開啟booktest/urls.py檔案,修改"fan2"的正則表示式為"fan_show"。
url(r'^fan_show/$', views.fan2),
7)開啟瀏覽器,後退一下,重新整理後再次點選連結,瀏覽如下圖:
問題就來了:隨著功能的增加會出現更多的檢視,可能之前配置的正則表示式不夠準確,於是就要修改正則表示式,但是正則表示式一旦修改了,之前所有對應的超連結都要修改,真是一件麻煩的事情,而且可能還會漏掉一些超連結忘記修改,有辦法讓連結根據正則表示式動態生成嗎? 答:反向解析。
反向解析應用在兩個地方:模板中的超連結,檢視中的重定向。
反向解析
要實現反向解析功能,需要如下步驟:
1)在test4/urls.py中為include定義namespace屬性。
url(r'^',include('booktest.urls',namespace='booktest')),
2)在booktest/urls.py中為url定義name屬性,並修改為fan2。
url(r'^fan2/$', views.fan2,name='fan2'),
3)在模板中使用url標籤做超連結,此處為templates/booktest/fan1.html檔案。
<html> <head> <title>反向解析</title> </head> <body> 普通連結:<a href="/fan2/">fan2</a> <hr> 反向解析:<a href="{%url 'booktest:fan2'%}">fan2</a> </body> </html>
4)回到瀏覽器中,後退,重新整理,檢視原始檔如下圖,兩個連結地址一樣。
5)在booktest/urls.py中,將fan2修改為fan_show。
url(r'^fan_show/$', views.fan2,name='fan2'),
6)回到瀏覽器中,重新整理,檢視原始檔如下圖,兩個連結地址不一樣。
7)反向解析也可以應用在檢視的重定向中。
from django.shortcuts import redirect from django.core.urlresolvers import reverse return redirect(reverse('booktest:fan2'))
總結:在定義url時,需要為include定義namespace屬性,為url定義name屬性,使用時,在模板中使用url標籤,在檢視中使用reverse函式,根據正則表示式動態生成地址,減輕後期維護成本。
URL的引數
有些url配置項正則表示式中是有引數的,接下來講解如何傳遞引數。
情況一:位置引數
1)在booktest/urls.py中,修改fan2如下:
url(r'^fan(\d+)_(\d+)/$', views.fan3,name='fan2'),
2)在booktest/views中,定義檢視fan3如下:
def fan3(request, a, b): return HttpResponse(a+b)
3)修改templates/booktest/fan1.html檔案如下:
<html> <head> <title>反向解析</title> </head> <body> 普通連結:<a href="/fan2_3/">fan2</a> <hr> 反向解析:<a href="{%url 'booktest:fan2' 2 3%}">fan2</a> </body> </html>
4)回到瀏覽器中,重新整理,檢視原始檔如下圖:
使用重定向傳遞位置引數格式如下:
return redirect(reverse('booktest:fan2', args=(2,3)))
情況二:關鍵字引數
1)在booktest/urls.py中,修改fan2如下:
url(r'^fan(?P<id>\d+)_(?P<age>\d+)/$', views.fan4,name='fan2'),
2)在booktest/views中,定義檢視fan4如下:
def fan4(request, id, age): return HttpResponse(id+age)
2)修改templates/booktest/fan1.html檔案如下:
<html> <head> <title>反向解析</title> </head> <body> 普通連結:<a href="/fan100_18/">fan2</a> <hr> 反向解析:<a href="{%url 'booktest:fan2' id=100 age=18%}">fan2</a> </body> </html>
3)回到瀏覽器中,重新整理,檢視原始檔如下圖:
使用重定向傳遞關鍵字引數格式如下:
return redirect(reverse('booktest:fan2', kwargs={'id':100,'age':18}))
換一個
- 點選“看不清,換一個”,換一個新的驗證碼
<script type="text/javascript" src="/static/jquery-1.12.4.min.js"></script> <script type="text/javascript"> $(function(){ $('#change').css('cursor','pointer').click(function() { $('#yzm').attr('src',$('#yzm').attr('src')+1) }); }); </script> ... <img id="yzm" src="/verify_code/?1"/> <span id="change">看不清,換一個</span>