[Django學習]模板

模板介紹

  • 做爲Web框架,Django提供了模板,能夠很便利的動態生成HTML
  • 模版系統致力於表達外觀,而不是程序邏輯
  • 模板的設計實現了業務邏輯(view)與顯示內容(template)的分離,一個視圖可使用任意一個模板,一個模板能夠供多個視圖使用
  • 模板包含
    • HTML的靜態部分
    • 動態插入內容部分
  • Django模板語言,簡寫DTL,定義在django.template包中
  • 由startproject命令生成的settings.py定義關於模板的值:
    • DIRS定義了一個目錄列表,模板引擎按列表順序搜索這些目錄以查找模板源文件
    • APP_DIRS告訴模板引擎是否應該在每一個已安裝的應用中查找模板
  • 經常使用方式:在項目的根目錄下建立templates目錄,設置DIRS值
DIRS=[os.path.join(BASE_DIR,"templates")]

模板處理

  • Django處理模板分爲兩個階段
  • Step1 加載:根據給定的標識找到模板而後預處理,一般會將它編譯好放在內存中
loader.get_template(template_name),返回一個Template對象
  • Step2 渲染:使用Context數據對模板插值並返回生成的字符串
Template對象的render(RequestContext)方法,使用context渲染模板
  • 加載渲染完整代碼:
from django.template import loader, RequestContext
from django.http import HttpResponse

def index(request):
    tem = loader.get_template('temtest/index.html')
    context = RequestContext(request, {})
    return HttpResponse(tem.render(context))

快捷函數

  • 爲了減小加載模板、渲染模板的重複代碼,django提供了快捷函數
  • render_to_string("")
  • render(request,'模板',context)
from django.shortcuts import render

def index(request):
    return render(request, 'temtest/index.html')

1. 定義模板

  • 模板語言包括
    • 變量
    • 標籤 { % 代碼塊 % }
    • 過濾器
    • 註釋{# 代碼或html #}

變量

  • 語法:
{{ variable }}
  • 當模版引擎遇到一個變量,將計算這個變量,而後將結果輸出
  • 變量名必須由字母、數字、下劃線(不能如下劃線開頭)和點組成
  • 當模版引擎遇到點("."),會按照下列順序查詢:
    1. 字典查詢,例如:foo["bar"]
    2. 屬性或方法查詢,例如:foo.bar
    3. 數字索引查詢,例如:foo[bar]
  • 若是變量不存在, 模版系統將插入'' (空字符串)
  • 在模板中調用方法時不能傳遞參數

在模板中調用對象的方法

  • 在models.py中定義類HeroInfo
from django.db import models

class HeroInfo(models.Model):
    ...
    def showName(self):
        return self.hname
  • 在views.py中傳遞HeroInfo對象
from django.shortcuts import render
from models import *

def index(request):
    hero = HeroInfo(hname='abc')
    context = {'hero': hero}
    return render(request, 'temtest/detail.html', context)
  • 在模板detail.html中調用
{{hero.showName}}

標籤

  • 語法:{ % tag % }
  • 做用
    • 在輸出中建立文本
    • 控制循環或邏輯
    • 加載外部信息到模板中供之後的變量使用
  • for標籤
{ %for ... in ...%}
循環邏輯
{{forloop.counter}}表示當前是第幾回循環
{ %empty%}
給出的列表爲或列表不存在時,執行此處
{ %endfor%}
  • if標籤
{ %if ...%}
邏輯1
{ %elif ...%}
邏輯2
{ %else%}
邏輯3
{ %endif%}
  • comment標籤
{ % comment % }
多行註釋
{ % endcomment % }
  • include:加載模板並以標籤內的參數渲染
{ %include "foo/bar.html" % }
  • url:反向解析
{ % url 'name' p1 p2 %}
  • csrf_token:這個標籤用於跨站請求僞造保護
{ % csrf_token %}
  • 布爾標籤:and、or,and比or的優先級高
  • block、extends:詳見「模板繼承」
  • autoescape:詳見「HTML轉義」

過濾器

  • 語法:{ { 變量|過濾器 }},例如{ { name|lower }},表示將變量name的值變爲小寫輸出
  • 使用管道符號 (|)來應用過濾器
  • 經過使用過濾器來改變變量的計算結果
  • 能夠在if標籤中使用過濾器結合運算符
if list1|length > 1
  • 過濾器可以被「串聯」,構成過濾器鏈
name|lower|upper
  • 過濾器能夠傳遞參數,參數使用引號包起來
list|join:", "
  • default:若是一個變量沒有被提供,或者值爲false或空,則使用默認值,不然使用變量的值
value|default:"什麼也沒有"
  • date:根據給定格式對一個date變量格式化
value|date:'Y-m-d'
  • escape:詳見「HTML轉義」
  • 點擊查看詳細的過濾器

註釋

  • 單行註釋
{#...#}
  • 註釋能夠包含任何模版代碼,有效的或者無效的均可以
{# { % if foo % }bar{ % else % } #}
  • 使用comment標籤註釋模版中的多行內容
 

2. 模板繼承

  • 模板繼承能夠減小頁面內容的重複定義,實現頁面內容的重用
  • 典型應用:網站的頭部、尾部是同樣的,這些內容能夠定義在父模板中,子模板不須要重複定義
  • block標籤:在父模板中預留區域,在子模板中填充
  • extends繼承:繼承,寫在模板文件的第一行
  • 定義父模板base.html
{ %block block_name%}
這裏能夠定義默認值
若是不定義默認值,則表示空字符串
{ %endblock%}
  • 定義子模板index.html
{ % extends "base.html" %}
  • 在子模板中使用block填充預留區域
{ %block block_name%}
實際填充內容
{ %endblock%}

說明

  • 若是在模版中使用extends標籤,它必須是模版中的第一個標籤
  • 不能在一個模版中定義多個相同名字的block標籤
  • 子模版沒必要定義所有父模版中的blocks,若是子模版沒有定義block,則使用了父模版中的默認值
  • 若是發如今模板中大量的複製內容,那就應該把內容移動到父模板中
  • 使用能夠獲取父模板中block的內容
  • 爲了更好的可讀性,能夠給endblock標籤一個名字
{ % block block_name %}
區域內容
{ % endblock block_name %}

三層繼承結構

  • 三層繼承結構使代碼獲得最大程度的複用,而且使得添加內容更加簡單
  • 以下圖爲常見的電商頁面

1.建立根級模板

  • 名稱爲「base.html」
  • 存放整個站點共用的內容
<!DOCTYPE html>
<html>
<head>
    <title>{%block title%}{%endblock%} 水果超市</title>
</head>
<body>
top--{{logo}}
<hr/>
{%block left%}{%endblock%}
{%block content%}{%endblock%}
<hr/>
bottom
</body>
</html>

2.建立分支模版

  • 繼承自base.html
  • 名爲「base_*.html」
  • 定義特定分支共用的內容
  • 定義base_goods.html
{%extends 'temtest/base.html'%}
{%block title%}商品{%endblock%}
{%block left%}
<h1>goods left</h1>
{%endblock%}
  • 定義base_user.html
{%extends 'temtest/base.html'%}
{%block title%}用戶中心{%endblock%}
{%block left%}
<font color='blue'>user left</font>
{%endblock%}
  • 定義index.html,繼承自base.html,不須要寫left塊
{%extends 'temtest/base.html'%}
{%block content%}
首頁內容
{%endblock content%}

3.爲具體頁面建立模板,繼承自分支模板

  • 定義商品列表頁goodslist.html
{%extends 'temtest/base_goods.html'%}
{%block content%}
商品正文列表
{%endblock content%}
  • 定義用戶密碼頁userpwd.html
{%extends 'temtest/base_user.html'%}
{%block content%}
用戶密碼修改
{%endblock content%}

4.視圖調用具體頁面,並傳遞模板中須要的數據

  • 首頁視圖index
logo='welcome to itcast'
def index(request):
    return render(request, 'temtest/index.html', {'logo': logo})
  • 商品列表視圖goodslist
def goodslist(request):
    return render(request, 'temtest/goodslist.html', {'logo': logo})
  • 用戶密碼視圖userpwd
def userpwd(request):
    return render(request, 'temtest/userpwd.html', {'logo': logo})

5.配置url

from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^list/$', views.goodslist, name='list'),
    url(r'^pwd/$', views.userpwd, name='pwd'),
]

3. HTML轉義

  • Django對字符串進行自動HTML轉義,如在模板中輸出以下值:
視圖代碼:
def index(request):
    return render(request, 'temtest/index2.html',
                  {
                      't1': '<h1>hello</h1>'
                  })
模板代碼:
{{t1}}
  • 顯示效果以下圖:

會被自動轉義的字符

  • html轉義,就是將包含的html標籤輸出,而不被解釋執行,緣由是當顯示用戶提交字符串時,可能包含一些攻擊性的代碼,如js腳本
  • Django會將以下字符自動轉義:
< 會轉換爲&lt;

> 會轉換爲&gt;

' (單引號) 會轉換爲&#39;

" (雙引號)會轉換爲 &quot;

& 會轉換爲 &amp;
  • 當顯示不被信任的變量時使用escape過濾器,通常省略,由於Django自動轉義
{{t1|escape}}

關閉轉義

  • 對於變量使用safe過濾器
{{ data|safe }}
  • 對於代碼塊使用autoescape標籤
{ % autoescape off %}
{{ body }}
{ % endautoescape %}
  • 標籤autoescape接受on或者off參數
  • 自動轉義標籤在base模板中關閉,在child模板中也是關閉的

字符串字面值

  • 手動轉義
{ { data|default:"<b>123</b>" }}
  • 應寫爲
{ { data|default:"&lt;b&gt;123&lt;/b&gt;" }}

4. csrf

  • 全稱Cross Site Request Forgery,跨站請求僞造
  • 某些惡意網站上包含連接、表單按鈕或者JavaScript,它們會利用登陸過的用戶在瀏覽器中的認證信息試圖在你的網站上完成某些操做,這就是跨站攻擊
  • 演示csrf以下
  • 建立視圖csrf1用於展現表單,csrf2用於接收post請求
def csrf1(request):
    return render(request,'booktest/csrf1.html')
def csrf2(request):
    uname=request.POST['uname']
    return render(request,'booktest/csrf2.html',{'uname':uname})
  • 配置url
url(r'^csrf1/$', views.csrf1),
url(r'^csrf2/$', views.csrf2),
  • 建立模板csrf1.html用於展現表單
<html>
<head>
    <title>Title</title>
</head>
<body>
<form method="post" action="/crsf2/">
    <input name="uname"><br>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
  • 建立模板csrf2用於展現接收的結果
<html>
<head>
    <title>Title</title>
</head>
<body>
{{ uname }}
</body>
</html>
  • 在瀏覽器中訪問,查看效果,報錯以下:

 

  • 將settings.py中的中間件代碼'django.middleware.csrf.CsrfViewMiddleware'註釋
  • 查看csrf1的源代碼,複製,在本身的網站內建一個html文件,粘貼源碼,訪問查看效果

防csrf的使用

  • 在django的模板中,提供了防止跨站攻擊的方法,使用步驟以下:
  • step1:在settings.py中啓用'django.middleware.csrf.CsrfViewMiddleware'中間件,此項在建立項目時,默認被啓用
  • step2:在csrf1.html中添加標籤
<form>
{% csrf_token %}
...
</form>
  • step3:測試剛纔的兩個請求,發現跨站的請求被拒絕了,效果以下圖

 

取消保護

  • 若是某些視圖不須要保護,可使用裝飾器csrf_exempt,模板中也不須要寫標籤,修改csrf2的視圖以下
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def csrf2(request):
    uname=request.POST['uname']
    return render(request,'booktest/csrf2.html',{'uname':uname})
  • 運行上面的兩個請求,發現均可以請求

保護原理

  • 加入標籤後,能夠查看源代碼,發現多了以下代碼
<input type='hidden' name='csrfmiddlewaretoken' value='nGjAB3Md9ZSb4NmG1sXDolPmh3bR2g59' />
  • 在瀏覽器的調試工具中,經過network標籤能夠查看cookie信息
  • 本站中自動添加了cookie信息,以下圖

  • 查看跨站的信息,並無cookie信息,即便加入上面的隱藏域代碼,發現又能夠訪問了
  • 結論:django的csrf不是徹底的安全
  • 當提交請求時,中間件'django.middleware.csrf.CsrfViewMiddleware'會對提交的cookie及隱藏域的內容進行驗證,若是失敗則返回403錯誤
相關文章
相關標籤/搜索