好男人天堂网,久久精品国产这里是免费,国产精品成人一区二区,男人天堂网2021,男人的天堂在线观看,丁香六月综合激情

當(dāng)前位置:首頁(yè) > 網(wǎng)站舊欄目 > 學(xué)習(xí)園地 > 設(shè)計(jì)軟件教程 > 翻譯www.djangobook.com之第十章:深入模板引擎

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

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

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

使用模板對(duì)象
最低級(jí)的使用Python模板系統(tǒng)只需兩步:
1,把原始模板代碼編輯到一個(gè)Template對(duì)象
2,使用一個(gè)給定的context調(diào)用Template對(duì)象的render()方法

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

在幕后系統(tǒng)僅僅當(dāng)你創(chuàng)建Template對(duì)象時(shí)解析一次你的原始代碼,然后由于性能的關(guān)系模板在內(nèi)部作為"節(jié)點(diǎn)"結(jié)構(gòu)存儲(chǔ)
甚至解析本身也是非常快的,大部分的解析通過(guò)調(diào)用一個(gè)單獨(dú)而簡(jiǎn)短的正則表達(dá)式來(lái)處理

渲染context
一旦你擁有一個(gè)編譯過(guò)的Template對(duì)象,你可以使用它渲染一個(gè)context或者很多context
Context構(gòu)造函數(shù)使用一個(gè)映射變量名和變量值的字典作為它的可選參數(shù)
使用context調(diào)用Template對(duì)象的render()方法來(lái)"填充"模板:
Java代碼 復(fù)制代碼
  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,數(shù)字0-9,下劃線或小數(shù)點(diǎn)組成
在模板渲染中小數(shù)點(diǎn)有特殊的意義,變量名中的小數(shù)點(diǎn)表示查詢,當(dāng)模板系統(tǒng)在變量名里遇到小數(shù)點(diǎn)時(shí),它嘗試一些
可能的選項(xiàng),例如變量{{ foo.bar }}可能擴(kuò)展為下面的任何一種:
字典查詢:foo["bar"]
屬性查詢:foo.bar
方法調(diào)用:foo.bar()
列表索引查詢:foo[bar]
模板系統(tǒng)使用可以工作的第一種查詢方式,這是短路邏輯,下面是一些例子:
Java代碼 復(fù)制代碼
  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."  

方法查詢比其它查詢類型稍微復(fù)雜一點(diǎn),下面是需要記住的一些事情:
1,在方法查詢時(shí)當(dāng)方法觸發(fā)一個(gè)異常,這個(gè)異常會(huì)一直傳播,除非異常有一個(gè)值為True的silent_variable_failure屬性
如果異常確實(shí)有這個(gè)屬性,變量將會(huì)被渲染為空字符串,例如:
Java代碼 復(fù)制代碼
  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數(shù)據(jù)庫(kù)API中的DoesNotExist異常的基類django.core.exceptions.ObjectDoesNotExist有這個(gè)屬性并且
值為True,所有如果你通過(guò)Django模型對(duì)象使用Django模板,任何DoesNotExist異常都將會(huì)靜靜的失敗
2,方法調(diào)用僅僅當(dāng)方法沒(méi)有必需的參數(shù)時(shí)才會(huì)工作,否則系統(tǒng)繼續(xù)下一個(gè)查詢類型(列表索引查詢)
3,顯然,一些方法有副作用,允許模板系統(tǒng)訪問(wèn)它們則是很愚蠢的安全漏洞
一個(gè)好例子是每個(gè)Django模型對(duì)象的delete()方法,模板系統(tǒng)不應(yīng)該允許做像這樣的事情:
Java代碼 復(fù)制代碼
  1. I will now delete this valuable data. {{ data.delete }}  

在方法上設(shè)置一個(gè)方法屬性alters_data來(lái)預(yù)防這點(diǎn),如果這個(gè)屬性為True則模板系統(tǒng)不會(huì)執(zhí)行這個(gè)方法:
Java代碼 復(fù)制代碼
  1. def sensitive_function(self):   
  2.     self.database_record.delete()   
  3. sensitive_function.alters_data = True  

例如,Django模型對(duì)象動(dòng)態(tài)生成的delete()和save()方法會(huì)自動(dòng)得到alters_data=True設(shè)置

如何處理非法變量
通常,如果變量不存在,模板系統(tǒng)會(huì)插入TEMPLATE_STRING_IF_INVALID設(shè)置,它默認(rèn)為空
只有當(dāng)TEMPLATE_STRING_IF_INVALID設(shè)置為默認(rèn)值的時(shí)候適用于非法變量的過(guò)濾器才會(huì)被使用
如果TEMPLATE_STRING_IF_INVALID被設(shè)置為其它任何值,變量過(guò)濾器都會(huì)被忽略
這個(gè)行為對(duì)if,for和regroup模板標(biāo)簽稍微不同,如果非法變量提供給這些模板標(biāo)簽,變量將為被解析為None
過(guò)濾器在這些模板標(biāo)簽中會(huì)一直對(duì)非法變量適用

和Context對(duì)象玩玩
大多數(shù)時(shí)候你將通過(guò)傳遞給Context()一個(gè)完全賦值的字典來(lái)初始化Context對(duì)象,但是一旦它初始化了,你可以使用標(biāo)準(zhǔn)
字典語(yǔ)法對(duì)Context對(duì)象添加和刪除項(xiàng):
Java代碼 復(fù)制代碼
  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對(duì)象是一個(gè)stack,你可以push()和pop()額外的context到stack中去,所有的設(shè)置操作放在stack的最高
context里,得到操作時(shí)會(huì)搜索stack(自頂向下)直到發(fā)現(xiàn)值
如果你pop()的太多的話它將觸發(fā)django.template.ContextPopException
這里是這些多級(jí)別工作的一個(gè)例子:
Java代碼 復(fù)制代碼
  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  

下面你會(huì)看到,把Context當(dāng)成stack在一些自定義模板標(biāo)簽里非常易用

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

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

每個(gè)processor按順序工作,即,如果一個(gè)processor添加一個(gè)變量到context里,第二個(gè)processor會(huì)添加一個(gè)同名的變量
第二個(gè)會(huì)覆蓋第一個(gè),默認(rèn)processors在下面解釋
你也可以給RequestContext傳遞一個(gè)額外processors的列表,使用可選的第三個(gè)參數(shù)processors
這個(gè)例子中RequestContext實(shí)例得到一個(gè)ip_address變量:
Java代碼 復(fù)制代碼
  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])  

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

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

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

Python API
Django有兩種從文件載入模板的方式:
django.template.loader.get_template(template_name)
get_template使用給定的名字返回編譯過(guò)的模板(一個(gè)Template對(duì)象)
如果模板不存在則觸發(fā)djang.template.TemplateDoesNotExist異常
django.template.loader.select_template(template_name_list)
select_template很像get_template,除了它使用模板名列表作為參數(shù)并返回列表中存在的第一個(gè)模板
例如,如果我們調(diào)用get_template('story_detail.html')并且設(shè)置了上面的TEMPLATE_DIRS,則下面是Django按順序
查找的文件:
/home/html/templates/lawrence.com/story_detail.html
/home/html/templates/default/story_detail.html
如果你調(diào)用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
當(dāng)Django找到一個(gè)存在的模板,它就是停止搜索
小貼士:
你可以使用select_template()來(lái)得到超級(jí)靈活的模板能力,例如,如果你寫了一個(gè)新聞故事并想讓一些故事?lián)碛凶远x
模板,你可以像這樣使用select_template(['story_%s_detail.html' % story.id, 'story_detail.html'])
這將允許你為一些單獨(dú)的故事使用自定義模板,并給那些沒(méi)有自定義模板的故事提供一個(gè)fallback模板

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

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

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

擴(kuò)展模板系統(tǒng)
盡管Django模板語(yǔ)言自帶一些默認(rèn)標(biāo)簽和過(guò)濾器,你可能想寫你自己的,這是很容易的
首先,在Django的app包的合適位置創(chuàng)建一個(gè)templatetags包,它應(yīng)該和models.py,views.py等在同一級(jí),例如:
Java代碼 復(fù)制代碼
  1. polls/   
  2.     models.py   
  3.     templatetags/   
  4.     views.py  

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

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

在幕后,你可以閱讀Django默認(rèn)過(guò)濾器和標(biāo)簽的源代碼來(lái)作為例子,它們分別在django/template/defaultfilters.py和
django/template/defaulttags.py,而django.contrib也包含了許多例子

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

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

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

當(dāng)你已經(jīng)寫好一個(gè)過(guò)濾器定義,你需要用你的Library實(shí)例注冊(cè)它來(lái)讓它對(duì)于Django的模板語(yǔ)言可用:
Java代碼 復(fù)制代碼
  1. register.filter('cut', cut)   
  2. register.filter('lower', lower)  

Library.filter()方法有兩個(gè)參數(shù):
1,filter的名字(字符串)
2,編譯方法(一個(gè)Python方法,而不是方法名)
如果你使用Python2.4及以上,你可以把register.filter()當(dāng)成裝飾器來(lái)使用:
Java代碼 復(fù)制代碼
  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()  

如果你像上面第二個(gè)例子一樣不寫name參數(shù),Django將使用方法名作為過(guò)濾器名

寫自定義模板標(biāo)簽
標(biāo)簽比過(guò)濾器更復(fù)雜一點(diǎn),因?yàn)闃?biāo)簽幾乎可以做任何事情

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

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

注意,這個(gè)模板標(biāo)簽重復(fù)了,Django默認(rèn)的{% now %}標(biāo)簽做了同樣的任何并且有更簡(jiǎn)潔的語(yǔ)法,這個(gè)只是一個(gè)例子
為了解析它,方法應(yīng)該得到參數(shù)并且創(chuàng)建一個(gè)Node對(duì)象:
Java代碼 復(fù)制代碼
  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])  

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

寫模板節(jié)點(diǎn)
寫自定義模板的第二步是定義一個(gè)含有render()方法的Node子類,繼續(xù)上面的例子,我們需要定義CurrentTimeNode:
Java代碼 復(fù)制代碼
  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)  

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

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

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

如果像上面第二個(gè)例子一樣不寫name參數(shù),Django將使用方法名作為標(biāo)簽名

在context里設(shè)置變量
上面的例子簡(jiǎn)單的輸出一個(gè)值,通常設(shè)置模板變量而不是輸出值會(huì)更有用,這里是一個(gè)CurrentTimeNode的更新版本,設(shè)置
一個(gè)模板變量current_time而不是輸出它:
Java代碼 復(fù)制代碼
  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()應(yīng)該一直返回字符串輸出,所以如果所有的模板標(biāo)簽做的都是設(shè)置變量,render()
應(yīng)該返回一個(gè)空字符串,這里是你怎樣使用新版本的標(biāo)簽:
Java代碼 復(fù)制代碼
  1. {% current_time "%Y-%M-%d %I:%M %p" %}   
  2. <p>The time is {{ current_time }}.</p>  

但是CurrentTimeNode2有一個(gè)問(wèn)題,變量名current_time是硬編碼的,這意味著你將需要確認(rèn)你的模板不會(huì)在別的地方
使用{{ current_time }},因?yàn)閧% current_time %}將盲目的覆蓋掉這個(gè)變量值
一個(gè)更干凈的解決方案是讓模板標(biāo)簽指定輸出變量名:
Java代碼 復(fù)制代碼
  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代碼 復(fù)制代碼
  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:  
    安徽新華電腦學(xué)校專業(yè)職業(yè)規(guī)劃師為你提供更多幫助【在線咨詢
久久国产精品自由自在| 久久国产精品自由自在| 免费国产一级特黄aa大片在线| 亚久久伊人精品青青草原2020| 99热热久久| 欧美夜夜骑 青草视频在线观看完整版 久久精品99无色码中文字幕 欧美日韩一区二区在线观看视频 欧美中文字幕在线视频 www.99精品 香蕉视频久久 | 久久久久久久网| 毛片成人永久免费视频| 国产一区国产二区国产三区| 夜夜操天天爽| 日韩中文字幕一区二区不卡| 韩国三级一区| 免费的黄色小视频| 香蕉视频三级| 国产精品免费久久| 韩国三级一区| 二级片在线观看| 精品久久久久久影院免费| 欧美激情伊人| 91麻豆精品国产自产在线观看一区| 国产伦久视频免费观看 视频| 精品视频一区二区三区免费| 国产伦精品一区二区三区在线观看| 99久久精品国产国产毛片| 国产福利免费视频| 久久精品免视看国产明星| 韩国三级视频网站| 国产成+人+综合+亚洲不卡| 久久成人亚洲| 精品国产亚洲一区二区三区| 日韩av片免费播放| 一a一级片| 亚洲第一色在线| 午夜在线亚洲| 美女免费精品视频在线观看| 可以免费看毛片的网站| 国产成a人片在线观看视频| 日韩专区第一页| 一级女性大黄生活片免费| 国产一区二区精品久| 国产极品精频在线观看| 成人影院一区二区三区| 成人在免费观看视频国产| 日韩一级黄色| 韩国毛片 免费| 久久成人亚洲| 日韩专区在线播放| 一本高清在线| 国产综合91天堂亚洲国产| 亚洲第一色在线| 日韩中文字幕在线播放| 国产亚洲精品成人a在线| 精品国产一区二区三区久久久狼| 精品国产亚洲人成在线| 超级乱淫伦动漫| 免费一级片在线观看| 尤物视频网站在线观看| 国产精品1024在线永久免费| 精品久久久久久中文字幕一区| 青青青草视频在线观看| 你懂的福利视频| 日本在线不卡视频| 色综合久久天天综合观看| 黄视频网站在线免费观看| 成人免费一级纶理片| 欧美电影免费| 欧美18性精品| 九九免费高清在线观看视频| 天天做人人爱夜夜爽2020| 亚洲天堂一区二区三区四区| 黄色免费网站在线| 国产视频一区在线| 精品久久久久久影院免费| 91麻豆精品国产自产在线| 久久精品店| 91麻豆精品国产自产在线| 久久福利影视| 日韩中文字幕一区| 国产91精品露脸国语对白| 99久久精品国产片| 成人免费观看的视频黄页| 99久久精品国产片| 欧美激情在线精品video| 亚洲 欧美 91| 久久国产一区二区| 午夜在线亚洲| 精品毛片视频| 国产伦久视频免费观看 视频| 四虎影视库国产精品一区| 99色精品| 91麻豆精品国产自产在线| 国产亚洲精品成人a在线| 欧美爱爱网| 午夜久久网| 成人影院久久久久久影院| 韩国毛片 免费| 亚洲天堂一区二区三区四区| 精品国产一区二区三区久久久蜜臀| 人人干人人草| 日本特黄一级| 高清一级淫片a级中文字幕| 欧美大片一区| 天天色成人| 久久精品免视看国产成人2021| 日韩一级黄色| 日本伦理片网站| 午夜久久网| 可以免费看毛片的网站| 美女免费精品高清毛片在线视 | 97视频免费在线| 99热热久久| 成人高清视频在线观看| 日韩专区一区| 日韩在线观看免费| 欧美18性精品| 九九久久99综合一区二区| 午夜久久网| 欧美夜夜骑 青草视频在线观看完整版 久久精品99无色码中文字幕 欧美日韩一区二区在线观看视频 欧美中文字幕在线视频 www.99精品 香蕉视频久久 | 青草国产在线| 91麻豆tv| 国产视频一区二区在线播放| 一级女性全黄久久生活片| 高清一级做a爱过程不卡视频| 成人免费网站视频ww| 国产视频一区二区在线播放| 黄色福利| 免费毛片播放| 午夜家庭影院| 韩国毛片免费大片| 色综合久久手机在线| 日本特黄特色aaa大片免费| 国产国语对白一级毛片| 天天做日日干| 亚洲精品中文一区不卡| 999精品视频在线| 精品国产亚洲人成在线| 99热精品一区| 色综合久久久久综合体桃花网| 日本久久久久久久 97久久精品一区二区三区 狠狠色噜噜狠狠狠狠97 日日干综合 五月天婷婷在线观看高清 九色福利视频 | 欧美激情一区二区三区中文字幕| 日韩男人天堂| 成人免费观看网欧美片| 精品视频一区二区三区免费| 国产成+人+综合+亚洲不卡| 久久成人综合网| 日韩在线观看视频黄| 亚州视频一区二区| 日本免费乱理伦片在线观看2018| 免费一级生活片| 日韩男人天堂| 一级女性大黄生活片免费| 精品视频在线观看一区二区| 一a一级片| 日韩在线观看视频免费| 好男人天堂网 久久精品国产这里是免费 国产精品成人一区二区 男人天堂网2021 男人的天堂在线观看 丁香六月综合激情 | 一级女性大黄生活片免费| 成人免费高清视频| 亚欧成人乱码一区二区| 成人在免费观看视频国产| 国产一区精品| 亚洲天堂一区二区三区四区| 国产一区二区精品在线观看| 国产成+人+综合+亚洲不卡| 国产极品精频在线观看| 欧美激情一区二区三区在线 | 美女免费精品高清毛片在线视 | 成人免费观看视频| 国产高清视频免费| 99久久精品国产国产毛片| 日韩av成人| 成人免费网站视频ww| 国产一级强片在线观看| 国产视频一区二区在线观看| 99久久精品费精品国产一区二区| 日韩欧美一二三区| 午夜在线影院| 国产极品白嫩美女在线观看看| 天天色色色| 国产激情视频在线观看| 91麻豆国产级在线| 精品国产一区二区三区久久久蜜臀| 国产成人精品影视| 高清一级片| 国产不卡精品一区二区三区| 你懂的在线观看视频| 亚欧成人毛片一区二区三区四区| 青青久久精品| 毛片高清| 精品视频在线观看一区二区 | 精品国产一区二区三区久久久蜜臀| 色综合久久手机在线| 欧美激情伊人| 久久国产影院| 精品视频在线看| 精品在线免费播放| 韩国三级视频网站| 久久精品店| 日日日夜夜操| 亚洲精品影院| 成人av在线播放|