您好,登錄后才能下訂單哦!
一切偉大的行動和思想,都有一個微不足道的開始; -- 徐志摩《勵志天下》
一切能忍受的微不足道,都有一個行動思想在腳下。 -- 小QQ 《勵志自己》
------------------------------------------------------------------------------------------------------
前一節,我們把HTML直接硬編碼在python中,盡管他便于解釋使徒的工作原理,但是卻不是一個好主意:
1、對頁面設計進行任何修改都必須對python代碼進行修改,而且站點設計的修改又頻繁與python代碼;
2、python代碼和HTML設計是不同的工作,大多的企業都將他們分配在給不同的部門;
3、python的編寫和HTML頁面設計同時工作,遠高于一人等待另一人完成即python又HTML的編輯。
所以我們要將頁面的設計和Python代碼分離開,而使用的就是Django模板系統(Template system)。
【 模 板 系 統 示 例 解 讀 】
模板是一個文本,用于分離文檔的表現形式和內容;
模板定義了占位符以及各種規范文檔顯示的基本邏輯(模板標簽);
模板通常用于產生HTML,但是Django的模板也能產生熱和機遇文本格式的文檔;
舉例:(第二段即簡單的模板文件,以下是我ipython解釋器格式)
In [1]: from django.template import Template, Context In [2]: raw_template="""<p>Dear{{ name }},</p> #兩個大括號括起來的值{{ name }}稱變量 ...: <p>Thanks an order from {{ company }}.It's scheduled #{{ company }}也是變量 ...: ship on {{ s_date|date:"F j, Y" }}.</p> #過濾器,見下 ...: ...: {% if ordered_warranty %} #if標簽,通知模板完成某些工作標簽,后面會詳解 ...: <p>Your warranty information will be included in the packaging.</p> ...: {% else %} ...: <p>You didn't order a warranty, so you're on your own when ...: the products inevitably stop working.</p> ...: {% endif %} ...: <p>Sincerely,<br />{{ company }}</p>""" In [3]: t = Template(raw_template) In [4]: import datetime In [5]: c = Context({'name': 'John Smith', ...: 'Company': 'Outdoor Equipment', ...: 's_date': datetime.date(2009, 4, 2), ...: 'ordered_warranty': False}) In [6]: t.render(c) Out[6]: u"<p>Dear John Smith,</p>\n\n<p>Thanks for placing an order from . It's scheduledto\nship on April 2, 2009.</p>\n\n\n<p>You didn't order a warranty, so you're on your own when\nthe products inevitably stop working.</p>\n\n\n<p>Sincerely,<br /> </p>"
代碼解讀:
1、導入 (import)類 Template 和 Context ;
2、將模板的原始文本保存到變量raw_template,并用三引號表示這些文本(包含多行),{{ s_date|date:"F j, Y" }}過濾器,s_date變量傳到date,并以F j, Y格式輸出 April 2, 2009
3、創建模板對象't',把raw_template作為Template類構造函數的參數;
4、從python標準庫導入datetime模塊使用;
5、創建Context對象'c',Context構造的參數是Python字典數據類型;
6、然后輸出(模板文本中\n,為什么輸出沒有換行呢?因為解釋器只缺省顯示這些字符串,不是打印值,用print t.render(c)就可以打印值,輸出換行了)(模板中的company變量沒有被打印,因為context構造的參數Company無效的變量,所以模板系統默認將它展示為空字符串)
7-15、對上下文(context)對象的添加刪除;
In [7]: c = Context({"foo": "bar"}) In [8]: c['foo'] Out[8]: 'bar' In [9]: del c['foo'] In [10]: c['foo'] --------------------------------------------------------------------------- KeyError Traceback (most recent call last) ......... KeyError: 'foo' In [11]: c['newvariable'] = 'hello' In [12]: c['newvariable'] Out[12]: 'hello' In [13]: c['newva'] = 'helloooo' In [14]: c['newva'] Out[14]: 'helloooo' In [15]: print c [{'False': False, 'True': True},{'newva': 'helloooo', 'newvariable': 'hello'}]
【 拆 解 模 板 系 統 】
模板系統最重要的兩個功能創建模板對象、模板渲染。
創建模板對象
創建一個template對象最簡單的辦法就是直接實例化它;Template類就在django.template模塊中,構造函數接受一個參數。
當創建模板對象時,模板系統在內部編譯這個模板到內部格式,并做優化,做好渲染準備
轉到項目目錄下:cd ~/HelloWorld/
啟動交互界面:python manage.py shell
啟動后會進入python界面,但與python不同的是,他會告訴python使用哪個配置文件,而django大部分子系統、模板系統,都依賴于配置文件,否則django將不能工作;
背后工作:執行后,Django去搜索DJANGO_SETTING_MODULE環境變量(它被設置在setting.py中),然后自動幫你處理這個環境變量,建議用“python manage.py shell”。
隨著熟悉Django,我們可能偏向廢棄此方法,而是直接在.bash_profile中添加DJANGO_SETTING_MODULE變量。
模板渲染
一旦創建Template對象,就可以用context傳遞數據,如上代碼:
>>> from django import template
>>> t = template.Template('My name is ` name `.') #創建模板對象
>>> c = template.Context({'name': 'Adrian'}) #模板渲染,調用context上下文切換name變量值
>>> t.render(c)
u'My name is Adrian.'
>>> print t.render(c)
'My name is Adrian.'
print t
<django.template.Template object at 0xb7d5f24c> #0xb7d5f24c template對象ID
代碼解讀:
t.render(c)返回一個unicode對象,不是普通字符串,可通過開頭'u'來區分,原因是如此可以讓你的應用程序更輕松處理各式各樣傳遞字符集,而不僅僅是A-Z英文字符;
============================================================================================
一模板,多渲染
一旦有了模板對象,我們可以渲染多個context
## Bad
for name in ('John', 'Julie', 'Pat'):
t = Template('Hello, ` name `')
print t.render(Context({'name': name}))
## Good
t = Template('Hello, ` name `')
for name in ('John', 'Julie', 'Pat'):
print t.render(Context({'name': name}))
注:模板解析非常的快捷,大部分的解析工作都是在后臺通過對簡短正則表達式一次性調用完成。和基于XML模板引擎對比鮮明,那些引擎承擔了XML解析器的開銷,且往往比Django模板渲染引擎慢幾個數量級。
點語法處理復雜數據結構
句點字符"."是django模板遍歷復雜數據結構的關鍵。遍歷的原則順序如下
字典類型查找(比如 foo["bar"])
屬性查找 (比如 foo.bar)
方法調用 (比如 foo.bar())
列表類型索引(比如 foo[bar])
1、點語法用于函數屬性
In [24]: from django.template import Template, Context In [25]: import datetime In [26]: d = datetime.date(1993, 5, 2) In [27]: d.year Out[27]: 1993 In [28]: t = Template('The month {{ date.month }} and the year {{ date.year }}.') In [29]: c = Context({'date': d}) In [30]: t.render(c) Out[30]: u'The month is 5 and the year is 1993.'
2、點語法用于upper()、isdigit()方法
In [44]: from django.template import Template, Context In [45]: t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}') In [46]: print t.render(Context({'var': '123'})) 123 -- 123 -- True In [47]: t.render(Context({'var': 'hello'})) Out[47]: u'hello -- HELLO -- False'
3、點語法用于列表索引
In [48]: t = Template('Item 2 is {{ items.2 }}.') #但是不允許用items.-1這樣的語法 In [49]: c = Context({'items': ['apples', 'bananas', 'carrots']}) In [50]: t.render(c) Out[50]: u'Item 2 is carrots.'
4、點語法用于多級嵌套
In [60]: from django.template import Template, Context In [61]: person = {'name': 'Sally', 'age': '43'} In [62]: t = Template('{{ person.name.upper }} is {{ person.age }} years old.') In [63]: c = Context({'person': person}) In [64]: t.render(c) Out[64]: u'SALLY is 43 years old.'
============================================================================================
【模板標簽和過濾器】
if/else 標 簽
上面我們用了if/else 的例子,判斷條件、執行方式和其他語言類似,而且{% else %} 標簽是可選的。
{% if condition1 and/or condition2 %} ........ {% elif condition3 %} ........ {% else %} ........ {% endif %}
注:不允許同一標簽同時出現and和or、不支持圓括號組合比較、每一個標簽都要用{% endif %}關閉。
for 標 簽
{% for condition %} ##{% for condition reversed %} 表示反向迭代 ............... {% empty %} ##相當于外層加了{% if condition %} ,判斷條件是否為空 <p>This condition is empty. </p> {% endfor %}
注:django不支持退出循環操作,也不支持continue語句,從當前迭代跳回循環頭部。
============================================================================================
"forloop"模板變量
a、forloop.counter:整數加法計數器,默認從1開始,forloop.counter0表示從0開始;
{% for item in todo_list %}
<p>` forloop`.`counter `: ` item `</p>
{% endfor %}
b、forloop.revcounter:整數減法計數器,默認初次執行被設為總數,執行至1時結束,forloop.revcounter0表示執行至0時結束,開始為總數-1;
c、forloop.first:布爾值,第一次執行后被置為""空;
d、forloop.last:布爾值,最后一次執行時被置為Ture,常用語法:
>>> {% for link in links %}` link `{% if not forloop.last %} | {% endif %}{% endfor %}
Link1 | Link2 | Link3 | Link4
============================================================================================
ifequal/ifnotequal 標 簽
可以等效{% if %} 部分功能,判斷兩個值或變量是否相等或不等
{% ifequal/ifnotequal var 'var' %} ............. {% endifequal/endifnotequal %}
注:參數可以是模板變量、字符串、小數、整數,不能是字典、列表、布爾類型。
{% comment %}/{# #} 注 釋
簡單注釋 {# ........ #}:注釋一兩行可以,但是不能跨多行
多行注釋 {% comment %} .......... {% endcomment %}:多行注釋時使用。
include 標 簽
{% include %} 標簽允許在模板中包含其它的模板的內容,下面會舉例。
過 濾 器
實現模板過濾器關鍵是使用管道符,并且可以多次過濾,即多次管道。
{{ name|lower }}:將變量name,通過過濾器被lower轉換為小寫
{{ my_list|first|upper }}:將my_list的第一個元素轉換為大寫
{{ pub_date|date:"F j, Y" }}:格式參數不介紹了,返回April 2, 2009
{{ bio|truncatewords:"30" }}:顯示bio變量的前30個詞
date:按指定格式字符串格式date或者datetime對象;
length:可以對列表或字符串返回變量的長度(就是說有__len__()方法的對象);
addslashes:添加反斜杠到任何反斜杠、單引號、雙引號前面,對于處理JavaScript文本很重要;
【 視 圖 函 數 應 用 模 板 】
以上我們大致了解了django模板系統的一些知識,下面我們要用到實戰中,即在視圖中調用django的模板。繼續current_time視圖,引入Django模板系統:
def current_time(request): now = datetime.datetime.now() ** t = Template("<html><body>It is now {{ current_date }}.</body></html>") ## fp = open('/django/HelloWorld/templates/template.html') ## t = Template(fp.read()) ## fp.close() ** html = t.render(Context({'current_date': now})) return HttpResponse(html)
** 更改,雖說是引入了模板系統,但是django的理念是模板與python代碼分離,所以這樣是不行的;
##更改,將模板單獨寫一個文件,然后用open()函數,但是文件丟失或權限會引發異常、對文件的位置仍是硬編碼、每次加載都要輸繁瑣代碼。
為了解決以上這些問題,django使用了模板加載、模板目錄的技巧。
模 板 加 載
為了減少模板加載調用過程及模板本身的冗余代碼,django提供一種強大的API,在磁盤中加載模板。要使用此模板加載API,首先要告訴框架(項目)模板的位置:
vim settings.py
TEMPLATE_DIRS = ( '/python/django/HelloWorld/templates', #== os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'), ##調用函數表示 # 'C:/python/django/HelloWorld/templates', ## windos平臺下寫法 )
注:路徑后面一定要加",",以消除圓括號與表達式之間的歧義。有的版本的django設置如下
cat ~/HelloWorld/HelloWorld/view.py
**from django.template.loader import get_template # 調用get_template函數,加載模板 **from django.template import Context # 調用context對象函數 from django.http import Http404,HttpResponse import datetime def hello(request): return HttpResponse("Hello World !!!") def current_time(request): now = datetime.datetime.now() ** t = get_template('current_time.html') #去找這個文件,并將內容實例化給't',文件名隨意 ** html = t.render(Context({'current_date': now})) #render方法傳入context對象參數 return HttpResponse(html)
============================================================================================
error:TemplateDoesNotExist at /time/ ;/usr/lib/python2.7/site-packages/django/template/loader.py in get_template, line 25
解決:因為get_template()根據setting.py中路徑,找不到current_time.html文件,去建這個文件。
============================================================================================
cat ~/HelloWorld/view.py
<html><body><h2>It is now {{ current_date }}.</h2></body></html>
============================================================================================
render_to_response()方法
上面我們用的get_template()方法載入文件,render()方法傳入context對象渲染,最后用HttpResponse處理后的對象給用戶;
這在django內,算是較冗余繁雜了,因為它提供了一個更好的方法render_to_response()方法。如下:
**from django.shortcuts import render_to_response from django.http import Http404,HttpResponse #保留他是因為hello視圖 import datetime def hello(request): return HttpResponse("Hello World !!!") def current_time(request): now = datetime.datetime.now() ** return render_to_response('current_time.html',{'current_date':now})
很明顯,原本三步三個方法完成的事,現在一步完成,極其簡便。
render_to_response第一參數必須是模板名稱,第二個若存在,必須是模板創建context使用的字典。
把所有模板放在一個目錄下,會讓很多事情變得難以掌控,于是會有人考慮建一個子目錄,但是指定模板文件時怎么指定呢?
比如將current_time.html文件放到模板目錄下的dateapp下:
return render_to_response('dateapp/current_time.html', {'current_date': now})
t = get_template('dateapp/current_time.html') ##get_template() 方法也支持,并都可多級指定。
locals() 方法
有些偷懶的程序員會想再次優化此視圖代碼。用locals()返回字典對所有(注意所有)局部變量的名稱和值進行映射。如下:
def current_time(request):
** current_date = datetime.datetime.now()
** return render_to_response('current_datetime.html', locals())
=============================================================================================
【 模 板 繼 承 】
問題:目前為止,我們所有的示例都是零星片段,實際應用中,我們會使用django模板去創建整個HTML頁面,這就有一個問題:如何減少共用頁面區域的代碼?
傳統答案是 :用上面提到的{% include %}標簽。
Django答案:更優雅的模板繼承。即先構造一個基礎框架模板,然后在其子模板對他共用部分進行重載。
接下來我們對前一章節寫的current_time、hours_ahead 視圖舉例:
cat ~/HelloWorld/HelloWorld/view.py
from django.shortcuts import render_to_response import datetime #def hello(request): # return HttpResponse("Hello World !!!") def current_time(request): now = datetime.datetime.now() return render_to_response('current_time.html',{'current_date':now}) def hours_ahead(request,offset): try: offset = int(offset) except ValueError: raise Http404() dt = datetime.datetime.now() + datetime.timedelta(hours=offset) return render_to_response('hours_ahead.xml',{'next_time':dt,'hour_offset':offset})
cat ~/HelloWorld/templates/current_time.html
<html><body><h2>It is now {{ current_date }}.</h2></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>The current time</title> </head> <body> <h2>My helpful timestamp site</h2> <p>It is now {{ current_date }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
cat ~/HelloWorld/templates/hours_ahead.xml (定義xml為了展示文件名字可隨意,但最好規范)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>Future time</title> </head> <body> <h2>My helpful timestamp site</h2> <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
很明顯,hours_ahead.xml、current_time.html兩個文件重復地方太多,再想如果是真是網站的話,那重復量可是無法想象的。。。
如果用傳統的 include 標簽,就會找出兩個模板相同的地方,保存成不同的模板片段,比如頭部相似處保存為header.html,尾部保存footer.html,然后在hours_ahead.xml文件中用{% include "header.html" %}調用; 中間部分其實大體相同,如果能保存到header.html中最好了,但是每個<title>不同,無法定制。而Django的模板繼承卻解決了這個問題。
簡言之:
模板繼承可視為 include的逆向思維,對不同代碼段進行定義,而非對相同代碼段。參照如下三步:
1、定義基礎摸板框架base.html:(主定義外觀感受,幾乎不修改)
vim ~/HelloWorld/templates/base.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>{% block title %}{% endblock %}</title> # 定義了一個title 塊 </head> <body> <h2>My helpful timestamp site</h2> {% block content %}{% endblock %} # 定義了一個content block {% block footer %} # 定義了一個footer塊,并沒有調用,當做一條退路 <hr> <p>Thanks for visiting my site.</p> {% endblock %} </body> </html>
2、創建區域模板 base_SECTION.html (如:base_photos.html、base_forum.html )對base.html進行擴展。
3、創建獨立視圖子模板文件
vim ~/HelloWorld/templates/current_time.html
{% extends "base.html" %} # extends就是加載 setting.py 中設置模板目錄下的文件 {% block title %}The current time{% endblock %} {% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}
vim ~/HelloWorld/templates/hours_ahead.xml
{% extends "base.html" %} {% block title %}Future time{% endblock %} {% block content %} <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> {% endblock %}
注:
a、{% extends %} 必須為子模板的第一個模板標記,否則模板繼承將不起作用;
b、一般來說,基礎模板中{% block %}越多越好,而且子模板文件只是調用用的著的block部分;
c、` block`.`super ` 標簽,將會表現出父模板中內容,若只想在上級代碼塊添加內容,而不是全部加載,該魔法變量就很有用了;
--------------------------------------------------------------------------------------------------
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。