1. 程式人生 > >Django框架之——模板

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>