亚洲成人一区在线观看_天堂网www_国产精品久久9_中文在线播放_伊人天天_久久精品久久久精品美女

當前位置:首頁 > 網站舊欄目 > 學習園地 > 設計軟件教程 > 翻譯www.djangobook.com之第十章:深入模板引擎

翻譯www.djangobook.com之第十章:深入模板引擎
2010-01-13 23:39:36  作者:  來源:
The Django Book:第10章 深入模板引擎
大多數時候你是以模板作者的角色來與Django的模板語言交互
本章更深的潛入到Django模板系統的五臟六腑,如果你需要擴展模板系統或者只是對它怎樣工作好奇,讀讀它
如果你在另一個程序的一部分使用Django模板系統,即不使用該框架的其它部分,確認你閱讀了本文檔后面的配置部分

基礎
模板是一個文本文檔,或者一個普通使用Django模板語言標記的Python字符串,模板可以保護塊標簽或者變量
塊標簽是模板中完成某些事情的標志
這個定義很模糊,例如,塊標簽可以輸出內容,處理控制結構("if"語句或者"for"循環),從數據庫得到數據或者允許
訪問其它模板標簽,塊標簽用{%和%}包圍:
Java代碼 復制代碼
  1. {% if is_logged_in %}   
  2.   Thanks for logging in!   
  3. {% else %}   
  4.   Please log in.   
  5. {% endif %}  

變量是模板中輸出值的標志
變量標簽用{{和}}包圍:
My first name is {{ first_name }}. My last name is {{ last_name }}.
context是傳遞給模板的"名字"->"值"的映射(類似于Python字典)
模板渲染通過用context值替代變量"洞"并執行塊標簽來渲染context

使用模板對象
最低級的使用Python模板系統只需兩步:
1,把原始模板代碼編輯到一個Template對象
2,使用一個給定的context調用Template對象的render()方法

編譯字符串
創建Template對象的最簡單的方法是直接初始化它,構造函數使用原始模板代碼作為它的參數:
Java代碼 復制代碼
  1. >>> from django.template import Template   
  2. >>> t = Template("My name is {{ my_name }}.")   
  3. >>> print t   
  4. <django.template.Template object at 0x1150c70>  

在幕后系統僅僅當你創建Template對象時解析一次你的原始代碼,然后由于性能的關系模板在內部作為"節點"結構存儲
甚至解析本身也是非常快的,大部分的解析通過調用一個單獨而簡短的正則表達式來處理

渲染context
一旦你擁有一個編譯過的Template對象,你可以使用它渲染一個context或者很多context
Context構造函數使用一個映射變量名和變量值的字典作為它的可選參數
使用context調用Template對象的render()方法來"填充"模板:
Java代碼 復制代碼
  1. >>> from django.template import Context, Template   
  2. >>> t = Template("My name is {{ my_name }}.")   
  3.   
  4. >>> c = Context({"my_name""Adrian"})   
  5. >>> t.render(c)   
  6. "My name is Adrian."  
  7.   
  8. >>> c = Context({"my_name""Dolores"})   
  9. >>> t.render(c)   
  10. "My name is Dolores."  

變量名必須由字母A-Z,數字0-9,下劃線或小數點組成
在模板渲染中小數點有特殊的意義,變量名中的小數點表示查詢,當模板系統在變量名里遇到小數點時,它嘗試一些
可能的選項,例如變量{{ foo.bar }}可能擴展為下面的任何一種:
字典查詢:foo["bar"]
屬性查詢:foo.bar
方法調用:foo.bar()
列表索引查詢:foo[bar]
模板系統使用可以工作的第一種查詢方式,這是短路邏輯,下面是一些例子:
Java代碼 復制代碼
  1. >>> from django.template import Context, Template   
  2. >>> t = Template("My name is {{ person.first_name }}.")   
  3.   
  4. >>> d = {"person": {"first_name""Joe""last_name""Johnson"}}   
  5. >>> t.render(Context(d))   
  6. "My name is Joe."  
  7.   
  8. >>> class Person:   
  9. ...     def __init__(self, first_name, last_name):   
  10. ...         self.first_name, self.last_name = first_name, last_name   
  11. ...   
  12. >>> p = Person("Ron""Nasty")   
  13. >>> t.render(Context({"person": p}))   
  14. "My name is Ron."  
  15.   
  16. >>> class Person2:   
  17. ...     def first_name(self):   
  18. ...         return "Samantha"  
  19. ...   
  20. >>> p = Person2()   
  21. >>> t.render(Context({"person": p}))   
  22. "My name is Samantha."  
  23.   
  24. >>> t = Template("The first stooge in the list is {{ stooges.0 }}.")   
  25. >>> c = Context({"stooges": ["Larry""Curly""Moe"]})   
  26. >>> t.render(c)   
  27. "The first stooge in the list is Larry."  

方法查詢比其它查詢類型稍微復雜一點,下面是需要記住的一些事情:
1,在方法查詢時當方法觸發一個異常,這個異常會一直傳播,除非異常有一個值為True的silent_variable_failure屬性
如果異常確實有這個屬性,變量將會被渲染為空字符串,例如:
Java代碼 復制代碼
  1. >>> t = Template("My name is {{ person.first_name }}.")   
  2.   
  3. >>> class Person3:   
  4. ...     def first_name(self):   
  5. ...         raise AssertionError("foo")   
  6. ...   
  7. >>> p = Person3()   
  8. >>> t.render(Context({"person": p}))   
  9. Traceback (most recent call last):   
  10. ...   
  11. AssertionError: foo   
  12.   
  13. >>> class SilentAssertionError(AssertionError):   
  14. ...     silent_variable_failure = True   
  15. ...   
  16. >>> class Person4:   
  17. ...     def first_name(self):   
  18. ...         raise SilentAssertionError("foo")   
  19. ...   
  20. >>> p = PersonClass4()   
  21. >>> t.render(Context({"person": p}))   
  22. "My name is ."  

注意所有的Django數據庫API中的DoesNotExist異常的基類django.core.exceptions.ObjectDoesNotExist有這個屬性并且
值為True,所有如果你通過Django模型對象使用Django模板,任何DoesNotExist異常都將會靜靜的失敗
2,方法調用僅僅當方法沒有必需的參數時才會工作,否則系統繼續下一個查詢類型(列表索引查詢)
3,顯然,一些方法有副作用,允許模板系統訪問它們則是很愚蠢的安全漏洞
一個好例子是每個Django模型對象的delete()方法,模板系統不應該允許做像這樣的事情:
Java代碼 復制代碼
  1. I will now delete this valuable data. {{ data.delete }}  

在方法上設置一個方法屬性alters_data來預防這點,如果這個屬性為True則模板系統不會執行這個方法:
Java代碼 復制代碼
  1. def sensitive_function(self):   
  2.     self.database_record.delete()   
  3. sensitive_function.alters_data = True  

例如,Django模型對象動態生成的delete()和save()方法會自動得到alters_data=True設置

如何處理非法變量
通常,如果變量不存在,模板系統會插入TEMPLATE_STRING_IF_INVALID設置,它默認為空
只有當TEMPLATE_STRING_IF_INVALID設置為默認值的時候適用于非法變量的過濾器才會被使用
如果TEMPLATE_STRING_IF_INVALID被設置為其它任何值,變量過濾器都會被忽略
這個行為對if,for和regroup模板標簽稍微不同,如果非法變量提供給這些模板標簽,變量將為被解析為None
過濾器在這些模板標簽中會一直對非法變量適用

和Context對象玩玩
大多數時候你將通過傳遞給Context()一個完全賦值的字典來初始化Context對象,但是一旦它初始化了,你可以使用標準
字典語法對Context對象添加和刪除項:
Java代碼 復制代碼
  1. >>> c = Context({"foo""bar"})   
  2. >>> c['foo']   
  3. 'bar'  
  4. >>> del c['foo']   
  5. >>> c['foo']   
  6. ''  
  7. >>> c['newvariable'] = 'hello'  
  8. >>> c['newvariable']   
  9. 'hello'  

而且,Context對象是一個stack,你可以push()和pop()額外的context到stack中去,所有的設置操作放在stack的最高
context里,得到操作時會搜索stack(自頂向下)直到發現值
如果你pop()的太多的話它將觸發django.template.ContextPopException
這里是這些多級別工作的一個例子:
Java代碼 復制代碼
  1. # Create a new blank context and set a simple value:   
  2. >>> c = Context()   
  3. >>> c['foo'] = 'first level'  
  4.   
  5. # Push a new context onto the stack:   
  6. >>> c.push()   
  7. >>> c['foo'] = 'second level'  
  8.   
  9. # The value of "foo" is now what we set at the second level:   
  10. >>> c['foo']   
  11. 'second level'  
  12.   
  13. # After popping a layer off, the old value is still there:   
  14. >>> c.pop()   
  15. >>> c['foo']   
  16. 'first level'  
  17.   
  18. # If we don't push() again, we'll overwrite existing values:   
  19. >>> c['foo'] = 'overwritten'  
  20. >>> c['foo']   
  21. 'overwritten'  
  22.   
  23. # There's only one context on the stack, so pop()ing will fail:   
  24. >>> c.pop()   
  25. Traceback (most recent call last):   
  26. ...   
  27. django.template.ContextPopException  

下面你會看到,把Context當成stack在一些自定義模板標簽里非常易用

RequestContext和context處理器
Django有一個特殊的Context類,django.template.RequestContext,它比普通的django.template.Context稍微復雜一點
第一個區別是它把HttpRequest對象(參考附錄8)作為它的第一個參數:
Java代碼 復制代碼
  1. c = RequestContext(request, {   
  2.     'foo''bar',   
  3. })  

第二個區別是它根據你的TEMPLATE_CONTEXT_PROCESSORS設置自動使用一些變量給context賦值
TEMPLATE_CONTEXT_PROCESSORS設置一些叫做context processors的元組,context processors使用request對象作為它們
的參數并且返回一個合并到context的項的字典,默認TEMPLATE_CONTEXT_PROCESSORS設置為:
Java代碼 復制代碼
  1. ("django.core.context_processors.auth",   
  2.  "django.core.context_processors.debug",   
  3.  "django.core.context_processors.i18n")  

每個processor按順序工作,即,如果一個processor添加一個變量到context里,第二個processor會添加一個同名的變量
第二個會覆蓋第一個,默認processors在下面解釋
你也可以給RequestContext傳遞一個額外processors的列表,使用可選的第三個參數processors
這個例子中RequestContext實例得到一個ip_address變量:
Java代碼 復制代碼
  1. def ip_address_processor(request):   
  2.     return {'ip_address': request.META['REMOTE_ADDR']}   
  3.   
  4. def some_view(request):   
  5.     # ...   
  6.     return RequestContext(request, {   
  7.         'foo''bar',   
  8.     }, processors=[ip_address_processor])  

這里是每個默認processor做的事情:
django.core.context_processors.auth
如果TEMPLATE_CONTEXT_PROCESSORS包含這個processor,每個RequestContext將會包含下面三個變量:
user
一個表示當前登錄的用戶的django.contrib.auth.models.User實例或者如果客戶沒登錄時表示一個AnonymousUser實例
messages
一個當前登錄用戶的messages列表(字符串),在幕后它為每個request調用request.user.get_and_delete_messages()
這個方法在數據庫收集和刪除用戶的messages,注意messages通過user.add_message()設置
perms
一個表示當前登錄的用戶的permissions的django.core.context_processors.PermWrapper實例
參考第12章關于users,permissions和messages的更多信息
django.core.context_processors.debug
這個processor把測試信息放到模板層,它在下面的前提下工作:
1,DEBUG設置為True
2,request來自于INTERNAL_IPS設置中的IP地址
如果這些條件都符合,則下面的變量將被設置:
debug
設置為True則你可以在模板中測試你是否處于DEBUG模式
sql_queries
一個{'sql': ..., 'time': ...}字典的列表,它表示目前為止在請求時發生的每一個SQL查詢以及所用的時間
這個列表通過query排序
django.core.context_processors.i18n
如果這個processor允許使用,則每個RequestContext將包含下面兩個變量:
LANGUAGES
LANGUAGES設置的值
LANGUAGE_CODE
表示request.LANGUAGE_CODE,如果它存在的話,否則將為LANGUAGE_CODE設置的值
附錄5有更多關于這兩個設置的信息
django.core.context_processors.request
如果允許使用它,則每個RequestContext將包含一個request變量,表示當前的HttpRequest對象
注意這個processor默認不允許使用,你將不得不自己激活它

載入模板
通常你會把模板存儲在你的文件系統的文件中(或者在其它地方,如果你些了自定義的模板載入器)而不是自己使用低級
Template API,Django根據你的模板載入設置(參看下面的"載入器類型")在幾個地方搜索模板目錄,但是最基本的指定
模板目錄的方式是使用TEMPLATE_DIRS設置,它應該被設置為一個包含你的模板目錄的完整路徑的列表或元組:
Java代碼 復制代碼
  1. TEMPLATE_DIRS = (   
  2.     "/home/html/templates/lawrence.com",   
  3.     "/home/html/templates/default",   
  4. )  

你的模板可以放在任何你需要的地方,只要目錄和模板對于Web服務器可讀,它們可以有一個你想要的后綴,例如.html
或者.txt或者根本沒有后綴,注意這些路徑應該使用Unix樣式的前斜線,甚至在Windows上也如此

Python API
Django有兩種從文件載入模板的方式:
django.template.loader.get_template(template_name)
get_template使用給定的名字返回編譯過的模板(一個Template對象)
如果模板不存在則觸發djang.template.TemplateDoesNotExist異常
django.template.loader.select_template(template_name_list)
select_template很像get_template,除了它使用模板名列表作為參數并返回列表中存在的第一個模板
例如,如果我們調用get_template('story_detail.html')并且設置了上面的TEMPLATE_DIRS,則下面是Django按順序
查找的文件:
/home/html/templates/lawrence.com/story_detail.html
/home/html/templates/default/story_detail.html
如果你調用select_template(['story_253_detail.html', 'story_detail.html']),則下面是Django查找的文件:
/home/html/templates/lawrence.com/story_253_detail.html
/home/html/templates/default/story_253_detail.html
/home/html/templates/lawrence.com/story_detail.html
/home/html/templates/default/story_detail.html
當Django找到一個存在的模板,它就是停止搜索
小貼士:
你可以使用select_template()來得到超級靈活的模板能力,例如,如果你寫了一個新聞故事并想讓一些故事擁有自定義
模板,你可以像這樣使用select_template(['story_%s_detail.html' % story.id, 'story_detail.html'])
這將允許你為一些單獨的故事使用自定義模板,并給那些沒有自定義模板的故事提供一個fallback模板

使用子目錄
很可能需要也推薦在模板目錄的子目錄組織模板,習慣用法士給每個Django app創建子目錄,并在子目錄里創建子目錄
使用你自己的智慧來做這件事,把所有的模板存放在根目錄下會十分凌亂
為了載入一個子目錄的模板,只需像這樣使用一個斜線:
Java代碼 復制代碼
  1. get_template('news/story_detail.html')  

而且,使用UNIX風格的前斜線,甚至在Windows上也是這樣

模板載入器
Django默認默認從文件系統載入模板,但是Django也有幾個其它的知道怎樣從其它源載入模板的模板載入器
這些其它的模板載入器默認不可用,但是你可以通過編輯TEMPLATE_LOADERS設置來激活它們
TEMPLATE_LOADERS應該是一個字符串的元組,其中每個字符串表示一個模板載入器,Django自帶這些模板載入器:
django.template.loaders.filesystem.load_template_source
根據TEMPLATE_DIRS從文件系統載入模板,默認可用
django.template.loaders.app_directories.load_template_source
在文件系統中從Django的apps載入模板,對于INSTALLED_APPS中的每個app,載入器尋找templates子目錄,如果該目錄
存在,Django則會在該目錄下尋找模板,這意味著你可以在單獨的app里存儲模板,這也讓使用默認模板發布Django
apps很容易,例如,如果INSTALLED_APPS包含('myproject.polls', 'myproject.music'),則get_template('foo.html')
將會按下列順序查找模板:
/path/to/myproject/polls/templates/foo.html
/path/to/myproject/music/templates/foo.html
注意載入器第一次import時使用了優化,它把INSTALLED_APPS的templates子目錄列表緩存起來
該載入器默認可使用
django.template.loaders.eggs.load_template_source
和上面的app_directories很類似,但是它從Python的eggs而不是文件系統載入模板
該載入器默認不可用,如果你使用eggs發布你的app,則你需要激活它
Django根據TEMPLATE_LOADERS設置按順序使用模板載入器,它將使用每個載入器尋找模板直到找到一個匹配的

擴展模板系統
盡管Django模板語言自帶一些默認標簽和過濾器,你可能想寫你自己的,這是很容易的
首先,在Django的app包的合適位置創建一個templatetags包,它應該和models.py,views.py等在同一級,例如:
Java代碼 復制代碼
  1. polls/   
  2.     models.py   
  3.     templatetags/   
  4.     views.py  

添加兩個文件到templatetags包,一個__init__.py文件(來告訴Python這是一個包含Python代碼的模塊)和一個包含你
自定義的標簽/過濾器定義的文件,后者的文件名是你將在后面用來載入標簽的名字,例如,如果你的自定義標簽或者
過濾器在一個叫ppll_extras.py文件里,你可以在模板里做下面的事情:
Java代碼 復制代碼
  1. {% load poll_extras %}  

{% load %}標簽查看你的INSTALLED_APPS設置并且只允許在已安裝的Django apps里面載入模板庫
這是一個安全特性,它允許你在一個單獨的計算機里為許多模板庫保存Python代碼并且不需要對每個Django安裝激活對
它們的訪問,如果你寫了一個不依賴于任何特殊的模型/視圖的模板庫,則有一個只包含了一個templatetags包的Django
app是可以的,對你在templatetags包里面放置了多少模塊沒有限制,只需記住{% load %}語句將為給定的Python模塊名
載入標簽/過濾器,而不是app名
一旦你創建了Python模塊,你將只需寫一點Python代碼,這取決于你在寫過濾器還是標簽
為了讓標簽庫合法,模塊應該包含一個模塊級的變量叫register,它是一個template.Library實例,所有的標簽和過濾器
都在它里面注冊,所以,在你的模塊最頂端加上下面的代碼:
Java代碼 復制代碼
  1. from django import template   
  2.   
  3. register = template.Library()  

在幕后,你可以閱讀Django默認過濾器和標簽的源代碼來作為例子,它們分別在django/template/defaultfilters.py和
django/template/defaulttags.py,而django.contrib也包含了許多例子

寫自定義模板過濾器
自定義過濾器只是有一到兩個參數的Python方法,參數為:
1,變量的值(輸入)
2,參數的值,它可以有默認值,也可以空出來不要它
例如,在過濾器{{ var|foo:"bar" }}中,過濾器foo將被傳入變量var和參數"bar"
過濾器方法應該一直返回一些東西,它們不應該觸發異常而應該靜靜的失敗,如果有錯誤,它們應該要么返回原始輸入
或者要么返回一個空字符串,無論哪個都有意義,這里是一個過濾器定義的例子:
Java代碼 復制代碼
  1. def cut(value, arg):   
  2.     "Removes all values of arg from the given string"  
  3.     return value.replace(arg, '')  

這里是過濾器怎樣使用的例子:
Java代碼 復制代碼
  1. {{ somevariable|cut:"0" }}  

大部分過濾器沒有參數,這種情況下,只需把參數從你的方法里剔除掉:
Java代碼 復制代碼
  1. def lower(value): # Only one argument.   
  2.     "Converts a string into all lowercase"  
  3.     return value.lower()  

當你已經寫好一個過濾器定義,你需要用你的Library實例注冊它來讓它對于Django的模板語言可用:
Java代碼 復制代碼
  1. register.filter('cut', cut)   
  2. register.filter('lower', lower)  

Library.filter()方法有兩個參數:
1,filter的名字(字符串)
2,編譯方法(一個Python方法,而不是方法名)
如果你使用Python2.4及以上,你可以把register.filter()當成裝飾器來使用:
Java代碼 復制代碼
  1. @register.filter(name='cut')   
  2. def cut(value, arg):   
  3.     return value.replace(arg, '')   
  4.   
  5. @register.filter   
  6. def lower(value):   
  7.     return value.lower()  

如果你像上面第二個例子一樣不寫name參數,Django將使用方法名作為過濾器名

寫自定義模板標簽
標簽比過濾器更復雜一點,因為標簽幾乎可以做任何事情

快速概覽
本章上面描述了模板系統怎樣以兩個步驟工作:編譯和渲染,為了定義一個自定義模板標簽,你需要告訴Django當它到達
你的標簽時怎樣管理這兩步
當Django編譯一個模板時,它把原始模板文本分開成一些"節點",每個節點都是django.template.Node的實例并且有一個
render()方法,這樣一個編譯好的模板就是一個簡單的Node對象的列表
當你對一個編譯好的模板調用render()時,模板使用給定的context對它的節點列表中的每個Node調用render()方法
結果都被連接在一起來組成模板的輸出,這樣,為了定義一個自定義模板標簽,你需要指定原始模板標簽怎樣轉換成一個
Node(編譯方法)和節點的render()方法做了些什么

寫編譯方法
對模板解析器遇到的每個模板標簽,它都使用標簽內容和解析器對象本身調用一個Python方法,這個方法負責根據標簽
內容返回一個Node實例,例如,讓我們寫一個模板標簽{% current_time %}來根據標簽里給定的參數和strftime語法顯示
當前的日期和時間并格式化它們(參考http://www.python.org/doc/current/lib/module-time.html#l2h-1941
關于strftime語法的信息),在其它任何事情之前決定標簽語法是個好注意,在我們這里的情況中則應該像這樣:
Java代碼 復制代碼
  1. <p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>  

注意,這個模板標簽重復了,Django默認的{% now %}標簽做了同樣的任何并且有更簡潔的語法,這個只是一個例子
為了解析它,方法應該得到參數并且創建一個Node對象:
Java代碼 復制代碼
  1. from django import template   
  2.   
  3. def do_current_time(parser, token):   
  4.     try:   
  5.         # split_contents() knows not to split quoted strings.   
  6.         tag_name, format_string = token.split_contents()   
  7.     except ValueError:   
  8.         raise template.TemplateSyntaxError("%r tag requires a single argument" % token.contents[0])   
  9.     return CurrentTimeNode(format_string[1:-1])  

事實上這里有許多東西:
1,parser時模板解析對象,我們這個例子中不需要它
2,token.contents是標簽的原始內容,在我們的例子中,它為'current_time "%Y-%m-%d %I:%M %p"'
3,token.split_contents()方法基于空格分開參數并且保持引號里的字符串在一起,最直接的token.contents.split()
不是很健壯,因為它會天真的分開所有的空格,包括引號字符串里的空格,一直使用token.split_contents()是個好主意
4,這個方法負責對任何語法錯誤使用有用信息觸發django.template.TemplateSyntaxError異常
5,不要在你的錯誤信息里硬編碼標簽名,因為這會耦合標簽名和你的方法,token.contents.split()[0]將一直是你的
標簽名,甚至當標簽沒有參數時也是如此
6,方法返回一個包含節點需要知道的關于此標簽的任何東西的CurrentTimeNode(我們下面將創建它),在這里,它只是
傳遞"%Y-%m-%d %I:%M %p"參數,模板標簽里開頭和結尾的引號會通過format_string[1:-1]去掉
7,模板標簽編譯方法必須返回一個Node子類,所有其它任何返回值都是錯誤的
8,解析是非常低級的,我們已經在這個解析系統上通過寫一些小框架來試驗過了(使用例如EBNF語法的技術),但是那些
試驗讓模板引擎非常變得慢,而低級解析是很快的

寫模板節點
寫自定義模板的第二步是定義一個含有render()方法的Node子類,繼續上面的例子,我們需要定義CurrentTimeNode:
Java代碼 復制代碼
  1. import datetime   
  2.   
  3. class CurrentTimeNode(template.Node):   
  4.   
  5.     def __init__(self, format_string):   
  6.         self.format_string = format_string   
  7.   
  8.     def render(self, context):   
  9.         return datetime.datetime.now().strftime(self.format_string)  

這兩個方法(__init__和render)直接映射了模板處理的兩個步驟(編譯和渲染),這樣,初始化方法只需存儲后面將使用的
字符串的格式,然后render()方法做真正的工作
像模板過濾器一樣,這些渲染方法應該靜靜的失敗而不是觸發錯誤,模板標簽允許觸發錯誤的時候只在編譯期間

注冊標簽
最后你需要使用你的模塊的Library實例注冊標簽,上面在"寫自定義過濾器"提到了:
Java代碼 復制代碼
  1. register.tag('current_time', do_current_time)  

tag()方法使用兩個參數:
1,模板標簽名(字符串),如果空著不寫,則將使用編譯方法名
2,編譯方法
類似過濾器注冊,也可以在Python2.4及以上使用裝飾器:
Java代碼 復制代碼
  1. @register.tag(name="current_time")   
  2. def do_current_time(parser, token):   
  3.     # ...   
  4.   
  5. @register.tag   
  6. def shout(parser, token):   
  7.     # ...  

如果像上面第二個例子一樣不寫name參數,Django將使用方法名作為標簽名

在context里設置變量
上面的例子簡單的輸出一個值,通常設置模板變量而不是輸出值會更有用,這里是一個CurrentTimeNode的更新版本,設置
一個模板變量current_time而不是輸出它:
Java代碼 復制代碼
  1. class CurrentTimeNode2(template.Node):   
  2.   
  3.     def __init__(self, format_string):   
  4.         self.format_string = format_string   
  5.   
  6.     def render(self, context):   
  7.         context['current_time'] = datetime.datetime.now().strftime(self.format_string)   
  8.         return ''  

注意render()返回空字符串,render()應該一直返回字符串輸出,所以如果所有的模板標簽做的都是設置變量,render()
應該返回一個空字符串,這里是你怎樣使用新版本的標簽:
Java代碼 復制代碼
  1. {% current_time "%Y-%M-%d %I:%M %p" %}   
  2. <p>The time is {{ current_time }}.</p>  

但是CurrentTimeNode2有一個問題,變量名current_time是硬編碼的,這意味著你將需要確認你的模板不會在別的地方
使用{{ current_time }},因為{% current_time %}將盲目的覆蓋掉這個變量值
一個更干凈的解決方案是讓模板標簽指定輸出變量名:
Java代碼 復制代碼
  1. {% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %}   
  2. <p>The current time is {{ my_current_time }}.</p>  

為了這樣做你需要重整編譯方法和Node類:
Java代碼 復制代碼
  1. import re   
  2.   
  3. class CurrentTimeNode3(template.Node):   
  4.   
  5.     def __init__(self, format_string, var_name):   
  6.         self.format_string = format_string   
  7.         self.var_name = var_name   
  8.   
  9.     def render(self, context):   
  10.         context[self.var_name] = datetime.datetime.now().strftime(self.format_string)   
  11.         return ''  
  12.   
  13. def do_current_time(parser, token):   
  14.     # This version uses a regular expression to parse tag contents.   
  15.     try:  
    安徽新華電腦學校專業職業規劃師為你提供更多幫助【在線咨詢
主站蜘蛛池模板: 免费一级欧美在线观看视频 | 亚洲视频在线观看免费 | 九九精品久久久 | 国产成人影院 | 欧美色欧美亚洲另类七区 | 黄色天堂在线观看 | 精品av | 欧美日韩一区二区三区四区 | 中文字幕二区 | 国产二区在线播放 | 久久福利 | 天堂资源 | 久久亚洲视频 | 日韩一区在线观看视频 | 国产中文在线 | 91亚洲精品在线观看 | 中国特级黄色片 | 美女三区 | 精品国产一区二区三区日日嗨 | 精品一区二区三区免费毛片爱 | 91春色| 日韩电影a | 国产午夜一区二区三区 | 电家庭影院午夜 | 不卡成人 | 四影虎影www4hu23cmo | 人人射人人 | 一区二区免费在线观看 | 久热最新| 午夜大片网| 欧美亚洲另类在线 | 欧美激情精品一区 | 中文字幕第100页 | 欧美一级内谢 | 国产高清视频 | 亚洲卡一| 亚洲成av人片在线观看 | 久久精品中文字幕 | 美日韩精品 | 少妇黄色一级片 | 亚洲精品视频在线免费 | 在线播放国产精品 | 国产午夜精品一区二区 | 91日日| 国产一区成人 | 成人在线播放 | 91在线播| 成人免费在线视频观看 | 亚洲一区 日韩精品 中文字幕 | 在线看欧美 | 免费av电影网站 | 亚洲综合无码一区二区 | 亚洲一级黄色 | 国精产品一区二区三区有限公司 | 国产精品久久久一区二区 | 亚洲一区精品在线 | 精品视频在线免费 | 欧美激情综合五月色丁香小说 | 国产一级一级特黄女人精品毛片 | 粉嫩高清一区二区三区精品视频 | 精品久久久久久久久久久久久久 | 九九热这里只有精品在线观看 | 欧美日韩精品一区 | 国产精品一区二区视频 | 成人福利在线观看 | 亚洲成人精品久久 | 欧美在线观看一区 | 在线观看91| 在线不卡a资源高清 | 久久久久久久99精品免费观看 | 欧美中文字幕在线 | 色婷婷综合久色 | 欧美精品久久一区 | 中文字幕一区二区在线观看 | 日韩中文在线视频 | 欧美日韩一区二区在线观看 | 亚洲精品一区二区三区中文字幕 | 国产在线小视频 | igao视频| 久久久久久久久一区二区三区 | 婷婷五月色综合香五月 | 一级黄色大片在线 | 懂色中文一区二区在线播放 | 亚洲综合在线视频 | 国产高清在线精品一区二区三区 | 欧美一区二区三区成人 | 北条麻妃一区二区免费播放 | 狠狠夜夜| 午夜tv| 成人在线三级 | 午夜草民福利电影 | 色猫猫国产区一区二在线视频 | 成人免费在线视频 | 日韩一级av毛片 | 国产黄色大全 | 日韩精品成人 | 国产精品成人久久久久 | 一级篇| 欧美成人精品一区二区男人看 | 97视频精品 | 国产精品爱久久久久久久 | 亚洲久久视频 | 精品亚洲一区二区三区 | 91伦理片| 国产欧美在线播放 | 久久久久久久久一区二区三区 | 精品一区二区三区在线观看 | 精品视频在线免费 | a√免费视频| 一级一片在线观看 | 日本黄a三级三级三级 | 亚洲综合在 | 国产在线观看二区 | 男女羞羞羞视频午夜视频 | 国产在线小视频 | 91视频免费观看 | 精品国产一区二区三区久久久蜜臀 | 精品国产91亚洲一区二区三区www | 成人精品一区二区三区中文字幕 | 国产91一区 | 性做久久久久久久免费看 | 精品国产乱码久久久久久蜜柚 | 日韩aaa久久蜜桃av | 日本videos18高清hd下 | 亚洲国产精品99久久久久久久久 | 久久国| 精品欧美一区二区三区久久久小说 | 2019中文字幕在线观看 | 久久久久亚洲精品 | 后进极品白嫩翘臀在线视频 | 欧美一区二区三区电影 | 一区二区三区在线播放 | 欧美三级在线播放 | 91视频18| 日韩一区二区在线播放 | 久久爱9191 | 亚洲婷婷一区 | 爱爱日韩 | 北条麻妃一区二区三区在线观看 | 久久一区二区三 | 国产综合一区二区 | 成人欧美一区二区三区在线播放 | 成人av网站在线观看 | 人人干美女 | 久久青青 | 丁香五月网久久综合 | 成人精品一区二区三区电影黑人 | 国产精品一区久久久久 | 日韩一区高清视频 | hsck成人网| 久久这里只有精品23 | 91国自产精品中文字幕亚洲 | 国产免费一区二区三区 | 一区二区三区在线 | 欧 | 欧美一区二区三区视频 | 日本jizz在线观看 | 日本黄色片免费看 | 国产福利在线观看 | 亚洲欧美一区二区三区在线 | 亚洲婷婷一区 | www.成人 | 欧美三级免费观看 | 免费视频成人 | 欧美成人精品一区二区男人看 | 99久久视频 | www.xxx在线观看 | 久久av一区二区三区 | 男人的天堂一级片 | 久久久久久亚洲一区二区三区蜜臀 | a在线观看 | 国产精品中文字幕在线观看 | 国产精品久久久久久久久晋中 | 成人免费一区二区三区视频网站 | 国产成人免费在线 | 中国av在线| 亚洲人成人一区二区在线观看 | 欧美日韩伊人 | 亚洲自拍一区在线 | 在线一区二区免费 | 亚洲激情欧美 | 天天综合网7799精品 | 欧美激情欧美激情在线五月 | 成人午夜精品久久久久久久蜜臀 | 播放一级黄色片 | 能免费看的av | 裸体的日本在线观看 | 99资源 | 色噜噜视频在线观看 | 久久综合久久综合久久综合 | 欧美成人一区二区三区片免费 | 毛片免费在线 | 成av人片在线观看www | 91九色视频在线 | 九九porny88av | 国产精品国产成人国产三级 | 中文字幕视频在线播放 | 免费视频成人 | 久久2018| 操片 | 亚洲欧美一区二区三区四区 | 91亚色 | 亚洲欧美高清 | 欧美一区二区免费 | 免费亚洲成人 | 99视频免费看| 日韩视频久久 | 久久国产一区二区三区 | 免费的黄色片子 | 精品入口麻豆88视频 | 欧美区国产区 | 黄色短视频在线观看 | 久久999| 亚洲精品久久久久久久久 | 欧美男人天堂 | 日韩精品在线视频 | 天天摸夜夜摸爽爽狠狠婷婷97 | 99精品免费在线 | 天天干天天av | 福利视频一区 | 久久99精品久久久久国产越南 | 久久99国产精品久久99大师 | 天天综合91 | 国产成人天天爽高清视频 | 亚洲精品成人 | 亚洲日本韩国在线观看 | 一区二区三区国产 | 日韩精品一区二区三区中文在线 | 91亚洲免费视频 | 中国免费看的片 | 欧美与黑人午夜性猛交久久久 | 亚洲成人日韩 | 日韩欧美一级 | 免费福利网站 | 中文字幕一区二区三区精彩视频 | 欧美不卡一区二区 | 黄色日本视频 | 色婷婷影院 | 在线免费av观看 | 日韩精品av一区二区三区 | 噜噜噜在线观看免费视频日本 | 午夜激情视频在线观看 | av在线免费观看网站 | 久在线| 亚洲激情在线观看 | 国产精品久久精品 | 国产成人片 | 欧美a区| 国产一区二区亚洲 | 久久久精品久久久久 | 国产一级视频 | 亚洲一区二区三区四区 | 美女主播精品视频一二三四 | 国产精品久久久久久久免费大片 | 一色屋精品久久久久久久久久 | 欧美极品视频 | 欧美一区二区三区在线观看视频 | 午夜精品久久久久久久白皮肤 | 日韩在线色 | 在线播放91| 欧美一区在线看 | 亚洲视频在线观看免费 | 成人一区二区电影 | 欧美性猛交一区二区三区精品 | 欧美午夜视频在线观看 | 久精品视频 | 99久久久国产精品美女 | 婷婷色av| 久久久91精品国产一区二区三区 | 毛片免费网站 | 久久久精品网站 | 久久99久久99精品免视看婷婷 | a毛片毛片av永久免费 | 国产中文字幕在线 | 91新视频 | 九九免费在线观看 | va在线| 看特级毛片 | 国产婷婷精品av在线 | 亚洲成人一二区 | 狠狠的日| 久久久精品| 成人午夜毛片 | 国产精品视频99 | 日韩在线免费 | 中文字幕亚洲一区二区va在线 | 亚洲一区二区三区在线播放 | 精品国产一区二区国模嫣然 | 免费午夜电影 | 伊人激情四射 | 超碰香蕉 | 黄视频网站免费看 | 一区二区三区国产精品 | 我爱操 | 91视频入口 | 一区二区三区四区在线播放 | 日韩在线免费观看av | 亚洲最大的黄色网 | 日本中文字幕在线播放 | 97在线观看视频 | 国产成人精品一区二区 | 久热中文字幕 | theporn国产在线精品 | 中文字幕亚洲在线 | 国产精品久久久久久久久久免费看 | 亚洲视频免费观看 | 国产一级黄色av | 中文字幕在线三区 | 日韩一区二区三区在线观看 | 国产猛男猛女超爽免费视频网站 | 在线免费黄色 | 亚洲毛片在线观看 | 国产精品国产三级国产aⅴ无密码 | 亚洲欧洲一区二区 | 综合国产 | 成人精品视频在线观看 | 伊人网站 | 午夜影院在线 | 99爱视频 | 一级毛片免费 | 亚洲成人精品久久 | 视频一区二区三区在线观看 | 国产精品99 | 国产一级一级国产 | 不卡在线 | 日韩视频久久 | 成人亚洲精品久久久久 | 国产99久久精品一区二区永久免费 | 国产精品一区二区无线 | 亚洲欧洲一区二区 | 懂色一区二区三区免费观看 | 国产三级在线观看 | 日本a在线 | 操操日 | 国产欧美日韩一区二区三区 | 国产高清久久久 | 黄桃av | 福利影院在线观看 | 久一久久| 午夜亚洲福利 | 久久久久久一区 | 一级视频网站 | 99精品视频在线 | 精品视频在线观看一区二区 | 国产免费久久 | 精品国产乱码久久久久夜 | 一级视频黄色 | 免费av在线网 | 中文字幕亚洲欧美 | 久在线草| 国产精品久久久久国产a级 日韩在线二区 | 男女羞羞视频在线观看 | 久久中文视频 | 久久久久久久久久久久国产精品 | 在线观看黄免费 | 精品国产乱码久久久久久1区2区 | 亚洲成人影院在线观看 | 免费在线黄 | 久久国产精品一区二区 | 欧美日韩中文在线 | 亚洲精彩视频 | 日韩a视频 | 久久精品无码一区二区三区 | 四季久久免费一区二区三区四区 | 狠狠干很很操 | 亚洲八区| 欧美日韩成人免费 | 欧美在线网站 | 干干日日 | 成人精品国产免费网站 | 免费av片在线 | 中文字幕亚洲视频 | 日韩毛片在线视频 | 美女视频黄色免费 | 九一视频在线免费观看 | 中国av在线 | 9uu在线观看 | 成人免费毛片高清视频 | 99亚洲精品 | 99爱爱视频 | 91午夜精品一区二区三区 | 黄色在线免费观看 | 日韩精品一区在线视频 | 麻豆久久久9性大片 | 一本a道v久大 | 亚洲精品影院在线 | 一区视频在线 | 成人在线| 久草天堂 | 日韩1区 | 久久亚洲国产精品 | 久久国产传媒 | 玖玖玖精品视频 | 久久一区二区三区四区 | 高清国产一区二区三区四区五区 | 国家aaa的一级看片 操操操夜夜操 | 一区二区精品视频 | 欧美精品一区二区在线观看 | 在线激情视频 | 一区二区三区视频播放 | 97久久精品人人做人人爽50路 | 成人一区视频 | 久久久精品免费观看 | 99久久精品免费看国产免费粉嫩 | 亚洲国产成人91精品 | 国产成人精品一区二区三区四区 | 91久久| 亚洲欧美一区二区三区在线 | 精品国产精品国产偷麻豆 | 亚洲最大av网站 | 国产视频久久久 | 伊人久久精品久久亚洲一区 | www国产精品| 成人精品在线视频 | 欧美久久一级特黄毛片 | 狠狠综合久久av一区二区小说 | 免费观看视频毛片 | 国产激情在线看 | 精品天堂 | 在线视频亚洲 | 国产精品永久在线观看 | 亚洲午夜视频在线观看 | 毛片一区二区三区 | 日本五月婷婷 | 欧美视频一二 | 欧美午夜视频 | 中文字幕在线资源 | 国产精品一区二区三区在线播放 | 亚洲狠狠爱一区二区三区 | 密色视频 | 亚洲高清免费视频 | av黄色在线 | 99久久婷婷国产综合精品 | 国产高潮失禁喷水爽网站 | 免费视频一区二区 | 免费三级黄色 | 中文字幕av在线播放 | 中国特级黄色片 | 91伊人 | 最新日韩精品在线观看 | 日韩欧美国产精品一区二区三区 | 久久久久久久国产精品 | 国外成人在线视频网站 | 久久高清亚洲 | 色悠久久久 | www.国产91| 欧美久久久久 | 免费一二区 | 中午字幕在线观看 | 国产野精品久久久久久久不卡 | 亚洲精品在线视频 | 中文字幕成人免费视频 | 5060毛片| 精品中出 | 欧美a在线看 | 久久久久久久国产精品 | 国产成人精品一区二区三区视频 | 国产xxxx成人精品免费视频频 | 久久一区 | 精品一区二区久久久久久久网站 | 美女二区 | 国产免费一区二区三区四区五区 | 色婷婷综合久久久久中文一区二区 | 久草免费福利 | 黄色a视频 | 精品国产91乱码一区二区三区 | 欧美久久视频 | 久久久天堂国产精品女人 | 福利毛片| 日日日日干干干干 | 日韩亚洲视频在线观看 | 午夜不卡视频 | 欧美极品视频 | 亚洲黄色在线视频 | 久久久久久91 | 久久99视频这里只有精品 | 91性高湖久久久久久久久网站 | 97成人在线 | 亚洲伊人中文字幕 | 在线一级片 | 中国国产一级毛片 | 国产精品久久久久影院色老大 | 免费看91| 91美女在线观看 | 99精品久久久国产一区二区三 | 欧美日韩中文 | 亚洲国产精品18久久 | 91国内精品久久 | 日韩国产精品一区二区 | 97在线视频免费 | 亚洲精品久久久久久一区二区 | 国产精品一区二区av | 99爱爱视频 | 亚洲三区在线观看 | 午夜精品一区二区三区在线观看 | 亚洲精品欧美 | 秋霞在线一区 | 免费成人高清在线视频 | 99久久免费看视频 | 伊人久色| 在线亚洲精品 | 久久久久久日产精品 | 亚洲精品91 | 欧美一级免费 | 日韩高清在线一区 | 91久久 | 亚洲一区视频网站 | 国产69久久 | 欧美另类久久 | 特级淫片日本高清视频免费 | 日韩成人国产 | 午夜一区二区三区 | 九色av| 国产男人天堂 | 99精品一区二区 | 亚洲精品国产成人 | 精品国产精品国产偷麻豆 | 99久久99久久精品 | 思九九爱九九 | 日韩高清中文字幕 | 欧美激情在线精品一区二区三区 | 精品久久一区二区三区 | 吊视频一区二区三区 | 国产一区精品视频 | 一区二区三区的视频 | 中文字幕成人av | 欧美激情精品久久久久久变态 | 日日干夜夜骑 | 国产成人精品一区二区在线 | 成人精品免费视频 | 亚洲免费视频在线观看 | 日韩精品极品视频在线 | 亚洲精品国产电影 | av 一区二区三区 | 天天摸天天干 | 一级a性色生活片毛片 | 91cn在线观看 | 亚洲福利一区二区 | av午夜电影 | 亚洲视频在线一区 | 台湾av在线| 精品国产乱码久久久久久1区2区 | 久久大陆 | 亚洲视频 欧美视频 | 久久成人精品 | 亚洲第一成年免费网站 | 亚洲视频在线免费观看 | 午夜在线视频免费观看 | 久久成人综合 | 午夜视频免费 | ririsao久久精品一区 | 国产精品国产三级国产aⅴ 羞羞的视频在线 | 美女福利网站 | 久久久久99 | 中文字幕一区在线观看视频 | 欧美日韩一区二区中文字幕 | 国产在线观看一区二区 | 欧美色视频在线观看 | 欧美精品一区二区在线观看 | 亚洲中国字幕 | 中文字幕亚洲一区二区va在线 | 一级毛片免费看 | 国产欧美高清在线观看 | 欧美日韩亚洲视频 | 成人精品视频免费 | 成人亚洲区 | av在线影院 | 国产欧美高清在线观看 | 伊人婷婷| 老师的朋友2 | 国产精品美女在线观看 | 日韩a在线 | 一级a毛片| 久操草| 日韩三级电影免费观看 | 午夜午夜精品一区二区三区文 | 欧美日韩免费一区二区三区 | 免费观看电视在线高清视频 | 欧美日本一区 | 中文字幕一区二区三区乱码图片 | 免费成人在线观看视频 | 欧美成人激情视频 | 日韩在线一区二区三区 | 在线一区二区三区做爰视频网站 | 高清在线一区二区 | 中文二区 | 欧美一级片免费在线观看 | 日韩综合在线 | 一区二区三区高清 | 久久免费视频观看 | 午夜免费福利在线 | 国产精品永久在线 | 国产一二三区在线观看 | 91婷婷射 | 日本亚洲最大的色成网站www | 亚洲综合国产 | 影音先锋亚洲精品 | 免费观看一级黄色片 | 日韩一区二区三区在线观看 | 欧美日韩国产一区二区三区不卡 | 久久这里只有国产精品 | 国产精品一区二区av | 欧美一区二区三区视频 | 国产精品亚洲天堂 | 久久久久久免费免费 | 欧美一区二区三区在线 | 99免费在线播放99久久免费 | 国产成人av在线 | 国产猛男猛女超爽免费视频网站 | 亚洲国产精品一区二区第一页 | 日韩av免费在线观看 | 成人午夜性a一级毛片免费看 | 粉嫩国产精品一区二区在线观看 | 国内外成人在线视频 | 狠狠综合久久 | 日韩一区欧美一区 |