你肯能已經注意到我們在例子視圖中傳回文本的方式有點特别。 也就是說,HTML被直接寫死在 Python代碼之中。
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
盡管這種技術便于解釋視圖是如何工作的,但直接将HTML寫死到你的視圖裡卻并不是一個好主意。 讓我們來看一下為什麼:
- 對頁面設計進行的任何改變都必須對 Python 代碼進行相應的修改。 站點設計的修改往往比底層 Python 代碼的修改要頻繁得多,是以如果可以在不進行 Python 代碼修改的情況下變更設計,那将會友善得多。
- Python 代碼編寫和 HTML 設計是兩項不同的工作,大多數專業的網站開發環境都将他們配置設定給不同的人員(甚至不同部門)來完成。 設計者和HTML/CSS的編碼人員不應該被要求去編輯Python的代碼來完成他們的工作。
- 程式員編寫 Python代碼和設計人員制作模闆兩項工作同時進行的效率是最高的,遠勝于讓一個人等待另一個人完成對某個既包含 Python又包含 HTML 的檔案的編輯工作。
基于這些原因,将頁面的設計和Python的代碼分離開會更幹淨簡潔更容易維護。 我們可以使用 Django的 模闆系統 (Template System)來實作這種模式,這就是本章要具體讨論的問題。
|

def current_time(req):
# ================================原始的視圖函數
# import datetime
# now=datetime.datetime.now()
# html="<html><body>現在時刻:<h1>%s.</h1></body></html>" %now
# ================================django模闆修改的視圖函數
# from django.template import Template,Context
# now=datetime.datetime.now()
# t=Template('<html><body>現在時刻是:<h1>{{current_date}}</h1></body></html>')
# #t=get_template('current_datetime.html')
# c=Context({'current_date':str(now)})
# html=t.render(c)
#
# return HttpResponse(html)
#另一種寫法(推薦)
import datetime
now=datetime.datetime.now()
return render(req, 'current_datetime.html', {'current_date':str(now)[:19]})

1 模闆文法之變量
在 Django 模闆中周遊複雜資料結構的關鍵是句點字元, 文法:
|
views.py:

def index(request):
import datetime
s="hello"
l=[111,222,333] # 清單
dic={"name":"yuan","age":18} # 字典
date = datetime.date(1993, 5, 2) # 日期對象
class Person(object):
def __init__(self,name):
self.name=name
person_yuan=Person("yuan") # 自定義類對象
person_egon=Person("egon")
person_alex=Person("alex")
person_list=[person_yuan,person_egon,person_alex]
return render(request,"index.html",{"l":l,"dic":dic,"date":date,"person_list":person_list})

template:
|
注意:句點符也可以用來引用對象的方法(無參數方法):
|
2 模闆之過濾器
文法:
|
default
default
如果一個變量是false或者為空,使用給定的預設值。否則,使用變量的值。例如:
|
length
傳回值的長度。它對字元串和清單都起作用。例如:
|
如果 value 是 ['a', 'b', 'c', 'd'],那麼輸出是 4。
filesizeformat
filesizeformat
将值格式化為一個 “人類可讀的” 檔案尺寸 (例如
'13 KB'
,
'4.1 MB'
'102 bytes'
, 等等)。例如:
|
如果
value
是 123456789,輸出将會是
117.7 MB
。
date
如果 value=datetime.datetime.now()
|
slice
如果 value="hello world"
|
truncatechars http://python.usyiyi.cn/documents/django_182/ref/templates/builtins.html#truncatechars
如果字元串字元多于指定的字元數量,那麼會被截斷。截斷的字元串将以可翻譯的省略号序列(“...”)結尾。
參數:要截斷的字元數
例如:
|
safe
Django的模闆中會對HTML标簽和JS等文法标簽進行自動轉義,原因顯而易見,這樣是為了安全。但是有的時候我們可能不希望這些HTML元素被轉義,比如我們做一個内容管理系統,背景添加的文章中是經過修飾的,這些修飾可能是通過一個類似于FCKeditor編輯加注了HTML修飾符的文本,如果自動轉義的話顯示的就是保護HTML标簽的源檔案。為了在Django中關閉HTML的自動轉義有兩種方式,如果是一個單獨的變量我們可以通過過濾器“|safe”的方式告訴Django這段代碼是安全的不必轉義。比如:
|
|
這裡簡單介紹一些常用的模闆的過濾器,
更多詳見3 模闆之标簽
标簽看起來像是這樣的:
{% tag %}
。标簽比變量更加複雜:一些在輸出中建立文本,一些通過循環或邏輯來控制流程,一些加載其後的變量将使用到的額外資訊到模版中。一些标簽需要開始和結束标簽 (例如
{% tag %} ...
标簽 内容 ... {% endtag %})。
for标簽
周遊每一個元素:
{% for person in person_list %}
<p>{{ person.name }}</p>
{% endfor %}
可以利用{% for obj in list reversed %}反向完成循環。
周遊一個字典:
{% for key,val in dic.items %}
<p>{{ key }}:{{ val }}</p>
{% endfor %}
注:循環序号可以通過{{forloop}}顯示
forloop.counter The current iteration of the loop (1-indexed)
forloop.counter0 The current iteration of the loop (0-indexed)
forloop.revcounter The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)
forloop.first True if this is the first time through the loop
forloop.last True if this is the last time through the loop
for ... empty http://python.usyiyi.cn/documents/django_182/ref/templates/builtins.html#for-empty
for 标簽帶有一個可選的{% empty %} 從句,以便在給出的組是空的或者沒有被找到時,可以有所操作。
{% for person in person_list %}
<p>{{ person.name }}</p>
{% empty %}
<p>sorry,no person here</p>
{% endfor %}
if 标簽
{% if %}會對一個變量求值,如果它的值是“True”(存在、不為空、且不是boolean類型的false值),對應的内容塊會輸出。

{% if num > 100 or num < 0 %}
<p>無效</p>
{% elif num > 80 and num < 100 %}
<p>優秀</p>
{% else %}
<p>湊活吧</p>
{% endif %}

with http://python.usyiyi.cn/documents/django_182/ref/templates/builtins.html#with
使用一個簡單地名字緩存一個複雜的變量,當你需要使用一個“昂貴的”方法(比如通路資料庫)很多次的時候是非常有用的
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
csrf_token http://python.usyiyi.cn/documents/django_182/ref/templates/builtins.html#csrf-token
這個标簽用于跨站請求僞造保護
4 自定義标簽和過濾器
1、在settings中的INSTALLED_APPS配置目前app,不然django無法找到自定義的simple_tag.
2、在app中建立templatetags子產品(子產品名隻能是templatetags)
3、建立任意 .py 檔案,如:my_tags.py
from
django
import
template
from
django.utils.safestring
import
mark_safe
register
=
template.Library()
#register的名字是固定的,不可改變
@register
.
filter
def
filter_multi(v1,v2):
return
v1
*
v2
<br>
@register
.simple_tag
def
simple_tag_multi(v1,v2):
return
v1
*
v2
<br>
@register
.simple_tag
def
my_input(
id
,arg):
result
=
"<input type='text' id='%s' class='%s' />"
%
(
id
,arg,)
return
mark_safe(result)
4、在使用自定義simple_tag和filter的html檔案中導入之前建立的 my_tags.py
|
5、使用simple_tag和filter(如何調用)
|
注意:filter可以用在if等語句後,simple_tag不可以
{
%
if
num|filter_multi:
30
>
100
%
}
{{ num|filter_multi:
30
}}
{
%
endif
%
}
5 模闆繼承 (extend)
Django模版引擎中最強大也是最複雜的部分就是模版繼承了。模版繼承可以讓您建立一個基本的“骨架”模版,它包含您站點中的全部元素,并且可以定義能夠被子模版覆寫的 blocks 。
通過從下面這個例子開始,可以容易的了解模版繼承:

<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css" />
<title>{% block title %}My amazing site{%/span> endblock %}</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>

這個模版,我們把它叫作
base.html
, 它定義了一個可以用于兩列排版頁面的簡單HTML骨架。“子模版”的工作是用它們的内容填充空的blocks。
在這個例子中,
block
标簽定義了三個可以被子模版内容填充的block。
block
告訴模版引擎: 子模版可能會覆寫掉模版中的這些位置。
子模版可能看起來是這樣的:
{
%
extends
"base.html"
%
}
{
%
block title
%
}My amazing blog{
%
endblock
%
}
{
%
block content
%
}
{
%
for
entry
in
blog_entries
%
}
<h2>{{ entry.title }}<
/
h2>
<p>{{ entry.body }}<
/
p>
{
%
endfor
%
}
{
%
endblock
%
}
extends
标簽是這裡的關鍵。它告訴模版引擎,這個模版“繼承”了另一個模版。當模版系統處理這個模版時,首先,它将定位父模版——在此例中,就是“base.html”。
那時,模版引擎将注意到
base.html
中的三個
block
标簽,并用子模版中的内容來替換這些block。根據
blog_entries
的值,輸出可能看起來是這樣的:
<!DOCTYPE html>
<html lang
=
"en"
>
<head>
<link rel
=
"stylesheet"
href
=
"style.css"
/
>
<title>My amazing blog<
/
title>
<
/
head>
<body>
<div
id
=
"sidebar"
>
<ul>
<li><a href
=
"/"
>Home<
/
a><
/
li>
<li><a href
=
"/blog/"
>Blog<
/
a><
/
li>
<
/
ul>
<
/
div>
<div
id
=
"content"
>
<h2>Entry one<
/
h2>
<p>This
is
my first entry.<
/
p>
<h2>Entry two<
/
h2>
<p>This
is
my second entry.<
/
p>
<
/
div>
<
/
body>
<
/
html>
請注意,子模版并沒有定義
sidebar
block,是以系統使用了父模版中的值。父模版的
{% block %}
标簽中的内容總是被用作備選内容(fallback)。
這種方式使代碼得到最大程度的複用,并且使得添加内容到共享的内容區域更加簡單,例如,部分範圍内的導航。
這裡是使用繼承的一些提示:
- 如果你在模版中使用
标簽,它必須是模版中的第一個标簽。其他的任何情況下,模版繼承都将無法工作。{% extends %}
- 在base模版中設定越多的
标簽越好。請記住,子模版不必定義全部父模版中的blocks,是以,你可以在大多數blocks中填充合理的預設内容,然後,隻定義你需要的那一個。多一點鈎子總比少一點好。{% block %}
- 如果你發現你自己在大量的模版中複制内容,那可能意味着你應該把内容移動到父模版中的一個
中。{% block %}
- If you need to get the content of the block from the parent template, the
variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using{{ block.super }}
will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.{{ block.super }}
- 為了更好的可讀性,你也可以給你的
标簽一個 名字 。例如:{% endblock %}
在大型模版中,這個方法幫你清楚的看到哪一個{
%
block content
%
}
...
{
%
endblock content
%
}
标簽被關閉了。{% block %}
- 不能在一個模版中定義多個相同名字的
标簽。block