{% raw %}
第五章 模板
***********
本章,我們會討論以下議題:
1. Django模板語法的特性
2. 組織模板
3. Bootstrap
4. 模板繼承樹模式
5. 活動連接模式
## 理解Django的模板語言特點
是時候談談MTV模板三件套中的第三個東西了。而你的團隊或許有關心模板設計的設計者。或者你想要自己設計模板。無論選擇哪種方式,你都需要對它們非常熟悉。畢竟,模板是直接面向用戶的。
我們對Django模板語言特性進行快速入門。
## 變量
每個模板都獲取一組上下文變量。類似于Python的字符串`format()`方法的單大括號`{variable}`語法,Django使用雙大括號`{{ variable }}`語法。我們來看看它們之間的比較:
? 在純凈的Python中語法為`<h1>{title}</h1>`。例如:
```python
>>> "<h1>{title}</h1>".format(title="SuperBook") '<h1>SuperBook</h1>'
```
? Django模板中的相等的語法是 `<h1>{{ title }}</h1>`.
? 如下傳遞下相同的上下文產生同樣的結果:
```python
>>> from django.template import Template, Context
>>> Template("<h1>{{ title }}</h1>").render(Context({"title":
"SuperBook"}))
'<h1>SuperBook</h1>'
```
## 屬性
點號在Django模板中是多用途的運算符。這里有三種不同類型的運算符,屬性查找,字典查找,列表索引查找(依照順序)。
? Python中我們首先,定義上下文變量和類:
```python
>>> class DrOct:
arms = 4
def speak(self):
return "You have a train to catch."
>>> mydict = {"key":"value"}
>>> mylist = [10, 20, 30]
```
我們來看看三種查詢類型的Python語法:
```python
>>> "Dr. Oct has {0} arms and says: {1}".format(DrOct().arms,
DrOct().speak())
'Dr. Oct has 4 arms and says: You have a train to catch.'
>>> mydict["key"]
'value'
>>> mylist[1]
20
```
? Django的模板等于下面:
```python
Dr. Oct has {{ s.arms }} arms and says: {{ s.speak }}
{{ mydict.key }}
{{ mylist.1 }}
```
>##### 注釋
注意*speak*方法沒有接受參數,除了被當作屬性的*self*之外。
## 過濾器
某些時候,變量是需要修改的。從根本上來說,你要利用這些變量去調用函數。Django使用類似于Unix過濾器的管道語法`{{ var|method1|method2:"tag" }}`,而不是`var.method().method2(arg)`這樣的鏈式函數調用。
過濾器的另外一個限制是它不能夠訪問模板上下文。過濾器僅在數據傳遞到過濾器,以及過濾器的參數時才有效。因此,在模板上下文中它主要用來更改變量。
- 在Python中運行以下命令:
```python
>>> title="SuperBook"
>>> title.upper()[:5]
'SUPER'
```
- 其對應的Django模板為:
```python
{{ title|upper|slice:':5' }}
```
## 標簽
編程語言能夠做的事情不僅僅是顯示變量。Django的模板語言多中相似的語法形式,比如`if`和`for`。它們應該用`{% if %}`這樣的語法寫成。多個針對模板的形式,比如`include`和`block`也都是用標簽語法寫成的。
? 在Python中運行以下命令:
```python
>>> if 1==1:
... print(" Date is {0} ".format(time.strftime("%d-%m-%Y")))
Date is 31-08-2014
```
? 其對應的Django模板形式如下:
```python
{% if 1 == 1 %} Date is {% now 'd-m-Y' %} {% endif %}
```
## 哲學——不要去發明一種編程語言
新手們間的常見問題是如何執行發現模板中百分比這樣的數字計算。從設計哲學的角度來說,模板系統無意支持這樣做:
- 對變量賦值
- 高級邏輯
該決定能夠阻止你在模板中添加業務邏輯。根據使用PHP或者類ASP語言的經歷,將邏輯和表現層混合到一起是一場后期維護的噩夢。不過,你可以編寫自定義的模板標簽(很快就會學到)以執行任何計算,特別是和表現層相關的計算。
>##### 提示
>*最佳實踐*
>保證業務邏輯遠離模板。
## 組織模板
由startproject命令創建的默認項目布局并沒有定義好的模板的位置。這個問題解決起來也非常簡單。在項目的根目錄中創建一個名稱為`templates`的目錄。然后在`settings.py`目錄中添加`TEMPLATE_DIRS`變量。
>譯者注
>在Django 1.8之后的版本中,已經不用手動定義模板路徑。startproject命令創建的默認配置文件已經自動包含了項目根目錄下的templates目錄。
>```python
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
```
```python
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]
```
```
要定義的就這么多。例如,你可以添加一個稱做`about.html`的模板,然后在`urls.py`文件中引用它:
```python
urlpatterns = patterns('',url(r'^about/$', TemplateView.as_view(template_name='about.html'),name='about'),
```
模板也可以定義在應用中。在應用的目錄中創建模板目錄對存儲應用專用的模板來說是非常理想的。
下面是組織模板的一些優秀實踐:
- 再一個單獨的目錄中,保證所有app專用的模板都放在這個app的模板目錄匯總,例如,`projroot/app/templates/app/template`。
- 對模板使用.html這樣對擴展名。
- 對模板添加一個下劃線前綴,表示將要繼承使用對代碼片段,例如,`_navbar.html`。
- 將應用專用的模板放到一個獨立的應用的模板目錄中,例如,`projecroot/app/templates/app/template`, 而`html`也能夠理解`app`兩次出現在路徑的原因。
- 為模板使用`.html`格式的文件擴展名。
- 對模板使用帶下劃線的前綴,該模板是能夠在繼承中使用的代碼片段,例如,`_navbar.html`。
## 對其他模板語言的支持
從Django1.8起多模板引擎將得到支持。屆時會存在內建的Django模板語言(我們之前談到的普通模板語言)和Jinja2的支持。在很多的性能基準測試中,Jinja2比Django模板快了很多。
對于指定的模板引擎和所有模板相關的設置來說都需要存在額外的TEMPLATES設置。而TEMPLATE_DIRS設置很快也會被移除。
### 歐女士
For the first time in weeks, Steve's office corner was bustling with frenetic activity. With more recruits, the now five-member team comprised of Brad, Evan, Jacob, Sue, and Steve. Like a superhero team, their abilities were deep and amazingly well-balanced.
Brad and Evan were the coding gurus. While Evan was obsessed over details, Brad was the big-picture guy. Jacob's talent in finding corner cases made him perfect for testing. Sue was in charge of marketing and design.
In fact, the entire design was supposed to be done by an avant-garde design agency. It took them a month to produce an abstract, vivid, color-splashed concept loved by the management. It took them another two weeks to produce an HTML-ready version from their Photoshop mockups. However, it was eventually discarded as it proved to be sluggish and awkward on mobile devices.
Disappointed by the failure of what was now widely dubbed as the "unicorn vomit" design, Steve felt stuck. Hart had phoned him quite concerned about the lack of any visible progress to show management. In a grim tone, he reminded Steve, "We have already eaten up the project's buffer time. We cannot afford any last-minute surprises."
It was then that Sue, who had been unusually quiet since she joined, mentioned that she had been working on a mockup using Twitter's Bootstrap. Sue was the growth hacker in the team—a keen coder and a creative marketer.
She admitted having just rudimentary HTML skills. However, her mockup was surprisingly thorough and looked familiar to users of other contemporary social networks. Most importantly, it was responsive and worked perfectly on every device from tablets to mobiles.
The management unanimously agreed on Sue's design, except for someone named Madame O. One Friday afternoon, she stormed into Sue's cabin and began questioning everything from the background color to the size of the mouse cursor. Sue tried to explain to her with surprising poise and calm.
An hour later, when Steve decided to intervene, Madame O was arguing why the profile pictures must be in a circle rather than square. "But a site-wide change like that will never get over in time," he said. Madame O shifted her gaze to him and gave him a sly smile. Suddenly, Steve felt a wave of happiness and hope surge within him. It felt immensely reliving and stimulating. He heard himself happily agreeing to all she wanted.
Later, Steve learnt that Madame Optimism was a minor mentalist who could influence prone minds. His team loved to bring up the latter fact on the slightest occasion.
## 使用Bootstrap
對任何人來說開始從零建設整個網站的那些日子都是件很艱苦的事情。像推特的或者說是扎克伯格基金會的Bootstrap這樣的CSS框架,柵格系統,極好的排列板式以及預先設置好的風格,下手好起點。使用Bootstrap的大多數網頁設計都對移動設備顯示友好。
圖片:略
我們將使用Bootstrap,而且步驟也類似于其他的CSS框架。在網站中使用Bootstrap有三種方法:
- Find a project skeleton: If you have not yet started your project, then finding a project skeleton that already has Bootstrap is a great option. A project skeleton such as edge (created by yours truly) can be used as the initial structure while running startproject as follows:
- 尋找項目架構:如果還沒有啟動項目,那么查找項目架構
```python
$ django-admin.py startproject --template=https://github.com/arocks/edge/archive/master.zip --extension=py,md,html myproj
```
可選擇的是,你可以使用其中一個擁有Bootstrap支持的cookiecutter模板。
- 使用包:最簡單的選項是如果你已經開始對自己的項目使用包,比如django-frontend-skeleton 或者 django-bootstrap-toolkit。
- 手動復制:之前的選項并不能保證Bootstrap的版本是最新的。而Bootstrap發布的很頻繁,所以包的捉著很難保證其中文件的更新。因此,如果你想要使用最新版本的Bootstrap,最好的選擇是自己到這個地址下載 http://getbootstrap.com。要記得去閱讀發型注釋,以便檢查你的模版是否需要為了向后兼容行而作出改變。
復制包含css、js的dist目錄,以及靜態目錄下根目錄中的字體目錄。保證這個路徑設置在settings.py中的STATICFILES_DIRS:
```python
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
Now you can include the Bootstrap assets in your templates, as follows:
{% load staticfiles %}
<head>
<link href="{% static 'css/bootstrap.min.css' %}"
rel="stylesheet">
```
## 怎么它們看上去都一個模樣啊!
Bootstrap或許是快速開發的非常好的選擇。不過,有時候,開發者懶得去改變默認的外觀。這就留給了瀏覽網站的用戶一個糟糕的映像,他們發現你的網站的外觀也太熟悉了而且很無趣。
Bootstrap帶來了很多的改進外觀需求的選項。如下,有一個稱作 `variables.less` 的文件包含了來自默認字體的主要種類色彩中的多個變量:
```html
@brand-primary: #428bca;
@brand-success: #5cb85c;
@brand-info: #5bc0de;
@brand-warning: #f0ad4e;
@brand-danger: #d9534f;
@font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-
serif;
@font-family-serif:
@font-family-monospace:
monospace;
@font-family-base:
Georgia, "Times New Roman", Times, serif;
Menlo, Monaco, Consolas, "Courier New",
@font-family-sans-serif;
```
Bootstrap文檔解釋了你該如何設置構造系統(包含LESS編譯器在內),以便講這些文件編譯為樣式表。另外一個很方便的選擇是你去訪問Bootstrap往后在哪的“定制”區域以在線生成定制的樣式表。
要感謝大量的有關Bootstrap的社區,這里有很多網站,比如bootswatch.com,該站點擁有可以替換bootstrap.min.css的主題化樣式表。
另外一個方法是重寫Bootstrap版式。如果發現再不同版本的Bootstrap之間升級自定義的Bootstrap樣式是多么無聊的事情,因為這只是個建議而已。在這個方法中,你可以在獨立的CSS(或者LESS)文件中添加自己的全站樣式,然后在標準的Bootstrap樣式表中繼承它。這樣,你就能夠以對全站樣式表最小改變來簡單的升級Bootstrap文件。
最后然后卻不是最少的,你可以讓自己CSS類更為有意義以替換結構式的名稱,比如“row”或者'column-md-4', 和 'wrapper' 或者 'sidebar'。如下,你可以用幾行LESS代碼完成它:
```html
.wrapper {
.make-row();
}
.sidebar {
.make-md-column(4);
}
```
這是處理一個功能的可行稱作mixin的方法(聽著很耳熟,是吧!)。使用LESS源碼你可以完全依照自身需求完成定制。
## 模板模式
Django模板語言非常的簡單。然而,它通過一些簡潔的模板設計模式可以讓你節省很多的時間。讓我們來看看其中的一些模板設計模式。
## 模式-模板繼承樹
問題:在多個頁面中模板存在很多重復的內容。
解決方案: 只要是有可能的地方就使用模板繼承,并在其他地方繼承代碼片段。
## 問題細節
用戶期望網站的頁面能夠遵循結構的一致性。某些接口元素,比如在很多web應用都能夠見到的導航欄菜單,首部,和頁腳。不過,在每一個模板中都重復它們顯示相當笨重。
大多數的模板語言都擁有模板繼承機制。其他文件的內容,也可以是一個模板都能夠在它們被調用的地方導入。在大型項目中這樣做會讓你感到乏味的。
在每一個模板中將要繼承的代碼片段序列很大程度上是相同的。倒入順序很重要,而錯誤檢查卻很難。理論上,我們能夠創建一個`基礎`結構。新的頁面應當擴展這個基礎模板
## 方案詳情
Django模板擁有一個強大的擴展機制。類似于編程中的類,模板可以通過繼承來擴展。不過,要讓模板工作起來,base自身必要像下面這樣組織到block中。
圖片:略
The base.html template is, by convention, the base structure for the entire site. This template will usually be well-formed HTML (that is, with a preamble and matching closing tags) that has several placeholders marked with the {% block tags %} tag. For example, a minimal base.html file looks like the following:
我們約定,base.html 模板是真個網站的基本結構。這個模板
```html
<html>
<body>
<h1>{% block heading %}Untitled{% endblock %}</h1>
{% block content %}
{% endblock %}
</body>
</html>
```
這里存在兩個能夠被重寫塊,heading和content。你可以擴展基礎模板以創建能夠重寫這些塊的特有頁面。例如,這里是一個about頁面:
```html
{% extends "base.html" %}
{% block content %}
<p> This is a simple About page </p>
{% endblock %}
{% block heading %}About{% endblock %}
```
注意,我們不需要出現重復的結構。我們按照任意順次飲用block。渲染結果將和定義在base.html的內容一樣在正確的地方使用正確的block。
如果繼承的模板沒有重寫block,那么它的父模板內容可以被使用。在前面的例子中,假如about模板不存在頭部,那么它將擁有默認的'Untitled'頭部。
The inheriting template can be further inherited forming an inheritance chain. This pattern can be used to create a common derived base for pages with a certain layout, for example, single-column layout. A common base template can also be created for a section of the site, for example, blog pages.
通常,所有的繼承鏈都可以通過一個公共的根,base.html來回溯;因此,樣式的名稱——模板繼承樹。當然,這樣的做法不需要到什么技巧的。而錯誤頁面404.html和500.html通常并不需要繼承,并去掉了大多數的標簽以阻止更多錯誤的發生。
## 模式-活動鏈接
問題:在很多對頁面中導航欄是一個常見對組件。然而,活動鏈接需要在用戶登陸時進行響應的映射。
解決方案:根據情況不同,通過設置上下文變量或者請求路徑來改變活動鏈接的外觀。
### 問題細節
在導航欄內實現活動鏈接的簡單辦法是手動地在每個一個需要實現該功能的頁面中設置。不過,這樣過既不符合DRY原則,也并不能保證萬無一失。
### 方案詳情
確定活動鏈接的解決方法有多種。包括,基于Javascript的方法,它們主要是將純模板和自定義標簽組合到一起。
### 臨時模板方案
在繼承導航模板的代碼片段時可以飲用一個稱作active_link的變量,這個方案實現起來既簡單又容易。
在每一個模板中,你都需要包括以下內容(或者繼承它):
```python
{% include "_navbar.html" with active_link='link2' %}
```
`_navbar.html`文件包含了擁有一組檢查活動鏈接的變量的導航菜單:
```python
{# _navbar.html #}
<ul class="nav nav-pills">
<li{% if active_link == "link1" %} class="active"{% endif %}><a
href="{% url 'link1' %}">Link 1</a></li>
<li{% if active_link == "link2" %} class="active"{% endif %}><a
href="{% url 'link2' %}">Link 2</a></li>
<li{% if active_link == "link3" %} class="active"{% endif %}><a
href="{% url 'link3' %}">Link 3</a></li>
</ul>
```
##自定義標簽
Django模板提供了一組多功能的內奸標簽。創建自己自定義標簽很簡單。因此自定義標簽是使用在應用內部的,那么就需要在應用的內部創建一個稱作templatetags到目錄。這個目錄必須是一個Python包,所以應該包含一個(空的)`__init__.py `文件。
接下來,在給定名稱的Python文件中編寫自己的自定義模板。例如,對于激活鏈接樣式,我們可以使用一下內容創建一個稱作nav.py的文件:
```python
# app/templatetags/nav.py
from django.core.urlresolvers import resolve
from django.template import Library
register = Library()
@register.simple_tag
def active_nav(request, url):
url_name = resolve(request.path).url_name
if url_name == url:
return "active"
return ""
```
這個文件定義了一個稱作`active_nav`的自定義標簽。它從request參數重新取回URL的路徑組件(即,第四章——視圖與路由,其中有對URL路徑對詳細說明)。然后resolve()函數用來從路徑查詢URL的樣式名稱(定義在urls.py中)。最后,讓僅在樣式名稱匹配期望的樣式名稱時反悔字符串“active”。
在模板中調用這個自定義標簽的語法是`{% active_nav request 'pattern_name' %}`。注意,在用到這個標簽的每個地方都需要將request傳遞進去。
在多個視圖中使用一個變量顯得太笨拙了。如下,我們在settings.py中添加一個內建的上下文處理器到`TEMPLATE_CONTEXT_PROCESSORS`,request便可以用request變量的身份出現在整個網站了:
```python
# settings.py
from django.conf import global_settings
TEMPLATE_CONTEXT_PROCESSORS = \
global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'django.core.context_processors.request',
)
```
現在,在模板中剩下的內容要使用這個定制標簽,這樣就可以設置激活屬性了:
```python
{# base.html #}
{% load nav %}
<ul class="nav nav-pills">
<li class={% active_nav request 'active1' %}><a href="{% url
'active1' %}">Active 1</a></li>
<li class={% active_nav request 'active2' %}><a href="{% url
'active2' %}">Active 2</a></li>
<li class={% active_nav request 'active3' %}><a href="{% url
'active3' %}">Active 3</a></li>
</ul>
```
##總結
在這一章,我們瀏覽了Django的模板特性。因為在Django中切換模板語言很簡單,所以很多人都考慮替換。不過,在瀏覽其他的選擇之前重要的是學些內建模板語言的設計哲學。
下一章,我們會學習Django的殺手級功能,即,admin接口,以及我們如何定制它。
{% endraw %}