Although most of your interactions with Djangos template language will be in the role of template author, you may want to customize and extend the template engine either to make it do something it doesnt already do, or to make your job easier in some other way.
WJmyEN <a href=”http://shdawlkicouq.com/“>shdawlkicouq</a>, [url=http://xhmwoyqzgfmc.com/]xhmwoyqzgfmc[/url], [link=http://ulwsjkuwztds.com/]ulwsjkuwztds[/link], http://zozblfuuryro.com/
This chapter delves deep into the guts of Djangos template system. It covers what you need to know if you plan to extend the system or if youre just curious about how it works.
鏈珷娣卞叆閽荤爺Django鐨勬ā鏉跨郴缁熴傚鏋滀綘鎯虫墿灞曟ā鏉跨郴缁熸垨鑰呭彧鏄瀹冪殑宸ヤ綔鍘熺悊鎰熻鍒板ソ濂囷紝鏈珷娑夊強浜嗕綘闇瑕佷簡瑙g殑涓滆タ.
If youre looking to use the Django template system as part of another application (i.e., without the rest of the framework), make sure to read the Configuring the Template System in Standalone Mode section later in the chapter.
濡傛灉浣犳兂鎶奃jango鐨勬ā鐗堢郴缁熶綔涓哄彟澶栦竴涓簲鐢ㄧ▼搴忕殑涓閮ㄥ垎锛堟瘮濡傦紝浠呬娇鐢╠jango鐨勬ā鏉跨郴缁熻屼笉浣跨敤Django妗嗘灦鐨勫叾浠栭儴鍒嗭級锛岄偅浣犱竴瀹氳璇讳竴涓嬧滈厤缃嫭绔嬫ā寮忎笅鐨勬ā鐗堢郴缁熲濊繖涓鑺傘
MaxVAp <a href=”http://xsivwbewqmrn.com/“>xsivwbewqmrn</a>, [url=http://gncxzntjqedq.com/]gncxzntjqedq[/url], [link=http://fxbofzrzfowj.com/]fxbofzrzfowj[/link], http://mxccahhxsvoy.com/
First, lets quickly review a number of terms introduced in Chapter 4:
棣栧厛锛岃鎴戜滑蹇熷洖椤句竴涓嬬鍥涚珷浠嬬粛鐨勮嫢骞蹭笓涓氭湳璇
A template is a text document, or a normal Python string, that is marked up using the Django template language. A template can contain block tags and variables.
妯℃澘 鏄竴涓函鏂囨湰鏂囦欢锛屾垨鏄竴涓敤Django妯℃澘璇█鏍囪杩囩殑鏅氱殑Python瀛楃涓诧紝涓涓ā鏉垮彲浠ュ寘鍚尯鍧楁爣绛惧拰鍙橀噺銆
A block tag is a symbol within a template that does something. This definition is deliberately vague. For example, a block tag can produce content, serve as a control structure (an if statement or for loop), grab content from a database, or enable access to other template tags.
鍖哄潡鏍囩 鏄湪涓涓ā鏉块噷闈㈣捣浣滅敤鐨勭殑鏍囪锛岃繖涓畾涔夋晠鎰忚鐨勫緢鍚硦锛屾瘮濡傦紝涓涓 鍖哄潡鏍囩鍙互鐢熸垚鍐呭锛屽彲浠ヤ綔涓轰竴涓帶鍒剁粨鏋勶紙 if 璇彞鎴 for 寰幆锛夛紝 鍙互鑾峰彇鏁版嵁搴撳唴瀹癸紝鎴栬呰闂叾浠栫殑妯℃澘鏍囩銆
Block tags are surrounded by {% and %} :
鍖哄潡鏍囩琚 {% 鍜 %} 鍖呭惈锛
{% if is_logged_in %} Thanks for logging in! {% else %} Please log in. {% endif %}
A variable is a symbol within a template that outputs a value.
鍙橀噺 鏄竴涓湪妯℃澘閲岀敤鏉ヨ緭鍑哄肩殑鏍囪銆
Variable tags are surrounded by {{ and }} :
636Md6 <a href=”http://htvhlhufywbg.com/“>htvhlhufywbg</a>, [url=http://xborgohwkwvg.com/]xborgohwkwvg[/url], [link=http://wdnbqpxfpopl.com/]wdnbqpxfpopl[/link], http://lmksosgzadaw.com/
My first name is {{ first_name }}. My last name is {{ last_name }}.
A context is a name -> value mapping (similar to a Python dictionary) that is passed to a template.
context 鏄竴涓紶閫掔粰妯℃澘鐨勫悕绉板埌鍊肩殑鏄犲皠锛堢被浼糚ython瀛楀吀锛夈
A template renders a context by replacing the variable holes with values from the context and executing all block tags.
KeBCRa <a href=”http://samdayfjlhom.com/“>samdayfjlhom</a>, [url=http://tqpfosapplax.com/]tqpfosapplax[/url], [link=http://ttkbcaqikqzp.com/]ttkbcaqikqzp[/link], http://hdccvanbqbjx.com/
For more details about the basics of these terms, refer back to Chapter 4.
鍏充簬杩欎簺鍩烘湰姒傚康鏇磋缁嗙殑鍐呭锛岃鍙傝冪鍥涚珷銆
The rest of this chapter discusses ways of extending the template engine. First, though, lets take a quick look at a few internals left out of Chapter 4 for simplicity.
鏈珷鐨勫叾浣欓儴鍒嗚璁轰簡鎵╁睍妯℃澘寮曟搸鐨勬柟娉曘傞鍏堬紝鎴戜滑蹇熺殑鐪嬩竴涓嬬鍥涚珷閬楃暀鐨勫唴瀹广
When rendering a template, you need a context. Usually this is an instance of django.template.Context , but Django also comes with a special subclass, django.template.RequestContext , that acts slightly differently. RequestContext adds a bunch of variables to your template context by default things like the HttpRequest object or information about the currently logged-in user.
浣犻渶瑕佷竴娈礳ontext鏉ヨВ鏋愭ā鏉裤備竴鑸儏鍐典笅锛岃繖鏄竴涓 django.template.Context 鐨勫疄渚嬶紝涓嶈繃鍦―jango涓繕鍙互鐢ㄤ竴涓壒娈婄殑瀛愮被锛 django.template.RequestContext 锛岃繖涓繍鐢ㄨ捣鏉ョ◢寰湁浜涗笉鍚屻 RequestContext 榛樿鍦板湪妯℃澘context涓姞鍏ヤ簡涓浜涘彉閲忥紝濡 HttpRequest 瀵硅薄鎴栧綋鍓嶇櫥褰曠敤鎴风殑鐩稿叧淇℃伅銆
Use RequestContext when you dont want to have to specify the same set of variables in a series of templates. For example, consider these four views:
褰撲綘涓嶆兂鍦ㄤ竴绯讳緥妯℃澘涓兘鏄庣‘鎸囧畾涓浜涚浉鍚岀殑鍙橀噺鏃讹紝浣犲簲璇ヤ娇鐢 RequestContext 銆備緥濡傦紝鐪嬩笅闈㈢殑鍥涗釜瑙嗗浘锛
from django.template import loader, Context def view_1(request): # ... t = loader.get_template('template1.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'], 'message': 'I am view 1.' }) return t.render(c) def view_2(request): # ... t = loader.get_template('template2.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'], 'message': 'I am the second view.' }) return t.render(c) def view_3(request): # ... t = loader.get_template('template3.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'], 'message': 'I am the third view.' }) return t.render(c) def view_4(request): # ... t = loader.get_template('template4.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'], 'message': 'I am the fourth view.' }) return t.render(c)
(Note that were deliberately not using the render_to_response() shortcut in these examples were manually loading the templates, constructing the context objects and rendering the templates. Were spelling out all of the steps for the purpose of clarity.)
锛堟敞鎰忥紝鍦ㄨ繖浜涗緥瀛愪腑锛屾垜浠晠鎰 涓 浣跨敤 render_to_response() 杩欎釜蹇嵎鏂规硶锛岃岄夋嫨鎵嬪姩杞藉叆妯℃澘锛屾墜鍔ㄦ瀯閫燾ontext瀵硅薄鐒跺悗娓叉煋妯℃澘銆傛槸涓轰簡鑳藉娓呮櫚鐨勮鏄庢墍鏈夋楠ゃ傦級
Each view passes the same three variables app , user and ip_address to its template. Wouldnt it be nice if we could remove that redundancy?
姣忎釜瑙嗗浘閮界粰妯℃澘浼犲叆浜嗕笁涓浉鍚岀殑鍙橀噺锛 app 銆 user 鍜 ip_address 銆傚鏋滄垜浠兘鎶婅繖浜涘啑浣欏幓鎺変細涓嶄細鐪嬭捣鏉ユ洿濂斤紵
RequestContext and context processors were created to solve this problem. Context processors let you specify a number of variables that get set in each context automatically without you having to specify the variables in each render_to_response() call. The catch is that you have to use RequestContext instead of Context when you render a template.
鍒涘缓 RequestContext 鍜 context澶勭悊鍣 灏辨槸涓轰簡瑙e喅杩欎釜闂銆侰ontext澶勭悊鍣ㄥ厑璁镐綘璁剧疆涓浜涘彉閲忥紝瀹冧滑浼氬湪姣忎釜context涓嚜鍔ㄨ璁剧疆濂斤紝鑰屼笉蹇呮瘡娆¤皟鐢 render_to_response() 鏃堕兘鎸囧畾銆傝鐐瑰氨鏄紝褰撲綘娓叉煋妯℃澘鏃讹紝浣犺鐢 RequestContext 鑰屼笉鏄 Context 銆
The most low-level way of using context processors is to create some processors and pass them to RequestContext . Heres how the above example could be written with context processors:
鏈鐩存帴鐨勫仛娉曟槸鐢╟ontext澶勭悊鍣ㄦ潵鍒涘缓涓浜涘鐞嗗櫒骞朵紶閫掔粰 RequestContext 銆備笂闈㈢殑渚嬪瓙鍙互鐢╟ontext processors鏀瑰啓濡備笅锛
from django.template import loader, RequestContext def custom_proc(request): "A context processor that provides 'app', 'user' and 'ip_address'." return { 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'] } def view_1(request): # ... t = loader.get_template('template1.html') c = RequestContext(request, {'message': 'I am view 1.'}, processors=[custom_proc]) return t.render(c) def view_2(request): # ... t = loader.get_template('template2.html') c = RequestContext(request, {'message': 'I am the second view.'}, processors=[custom_proc]) return t.render(c) def view_3(request): # ... t = loader.get_template('template3.html') c = RequestContext(request, {'message': 'I am the third view.'}, processors=[custom_proc]) return t.render(c) def view_4(request): # ... t = loader.get_template('template4.html') c = RequestContext(request, {'message': 'I am the fourth view.'}, processors=[custom_proc]) return t.render(c)
Lets step through this code:
鎴戜滑鏉ラ氳涓涓嬩唬鐮侊細
First, we define a function custom_proc . This is a context processor it takes an HttpRequest object and returns a dictionary of variables to use in the template context. Thats all it does.
棣栧厛锛屾垜浠畾涔変竴涓嚱鏁 custom_proc 銆傝繖鏄竴涓猚ontext澶勭悊鍣紝瀹冩帴鏀朵竴涓 HttpRequest 瀵硅薄锛岀劧鍚庤繑鍥炰竴涓瓧鍏革紝杩欎釜瀛楀吀涓寘鍚簡鍙互鍦ㄦā鏉縞ontext涓娇鐢ㄧ殑鍙橀噺銆傚畠灏卞仛浜嗚繖涔堝銆
Weve changed the four view functions to use RequestContext instead of Context . There are two differences in how the context is constructed. One, RequestContext requires the first argument to be an HttpRequest object the one that was passed into the view function in the first place (request ). Two, RequestContext takes an optional processors argument, which is a list or tuple of context processor functions to use. Here, we pass in custom_proc , the custom processor we defined above.
鎴戜滑鍦ㄨ繖鍥涗釜瑙嗗浘鍑芥暟涓敤 RequestContext 浠f浛浜 Context 銆傚湪context瀵硅薄鐨勬瀯寤轰笂鏈変袱涓笉鍚岀偣銆備竴锛 RequestContext 鐨勭涓涓弬鏁伴渶瑕佷紶閫掍竴涓 HttpRequest 瀵硅薄锛屽氨鏄紶閫掔粰瑙嗗浘鍑芥暟鐨勭涓涓弬鏁帮紙 request 锛夈備簩锛 RequestContext 鏈変竴涓彲閫夌殑鍙傛暟 processors 锛岃繖鏄竴涓寘鍚玞ontext澶勭悊鍣ㄥ嚱鏁扮殑list鎴栬卼uple銆傚湪杩欓噷锛屾垜浠紶閫掍簡鎴戜滑涔嬪墠瀹氫箟鐨勫嚱鏁 curstom_proc 銆
Each view no longer has to include app , user or ip_address in its context construction, because those are provided by custom_proc .
姣忎釜瑙嗗浘鐨刢ontext缁撴瀯閲屼笉鍐嶅寘鍚 app 銆 user 銆 ip_address 绛夊彉閲忥紝鍥犱负杩欎簺鐢 custom_proc 鍑芥暟鎻愪緵浜嗐
Each view still has the flexibility to introduce any custom template variables it might need. In this example, the message template variable is set differently in each view.
O5akXl <a href=”http://xrdylvofgyks.com/“>xrdylvofgyks</a>, [url=http://jpknyzrymcvm.com/]jpknyzrymcvm[/url], [link=http://kanomsrvtkmd.com/]kanomsrvtkmd[/link], http://avajxtraqqjg.com/
In Chapter 4, we introduced the render_to_response() shortcut, which saves you from having to call loader.get_template() , then create a Context , then call the render() method on the template. In order to demonstrate the lower-level workings of context processors, the above examples didnt use render_to_response() , . But its possible and preferable to use context processors with render_to_response() . Do this with the context_instance argument, like so:
鍦ㄧ鍥涚珷锛屾垜浠粙缁嶄簡 render_to_response() 杩欎釜蹇嵎鏂瑰紡锛屽畠鍙互鐪佹帀璋冪敤 loader.get_template() ,鐒跺悗鍒涘缓涓涓 Context 瀵硅薄锛屾渶鍚庡啀璋冪敤妯℃澘瀵硅薄鐨 render() 鏂规硶銆備负浜嗚瑙ontext澶勭悊鍣ㄥ簳灞傛槸濡備綍宸ヤ綔鐨勶紝鍦ㄤ笂闈㈢殑渚嬪瓙涓垜浠病鏈変娇鐢 render_to_response() 銆備絾鏄缓璁夋嫨 render_to_response() 浣滀负context鐨勫鐞嗗櫒銆傚儚杩欐牱锛屼娇鐢 context_instance 鍙傛暟锛
from django.shortcuts import render_to_response from django.template import RequestContext def custom_proc(request): "A context processor that provides 'app', 'user' and 'ip_address'." return { 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'] } def view_1(request): # ... return render_to_response('template1.html', {'message': 'I am view 1.'}, context_instance=RequestContext(request, processors=[custom_proc])) def view_2(request): # ... return render_to_response('template2.html', {'message': 'I am the second view.'}, context_instance=RequestContext(request, processors=[custom_proc])) def view_3(request): # ... return render_to_response('template3.html', {'message': 'I am the third view.'}, context_instance=RequestContext(request, processors=[custom_proc])) def view_4(request): # ... return render_to_response('template4.html', {'message': 'I am the fourth view.'}, context_instance=RequestContext(request, processors=[custom_proc]))
Here, weve trimmed down each views template rendering code to a single (wrapped) line.
鍦ㄨ繖锛屾垜浠皢姣忎釜瑙嗗浘鐨勬ā鏉挎覆鏌撲唬鐮佸啓鎴愪簡涓涓崟琛屻
This is an improvement, but, evaluating the conciseness of this code, we have to admit were now almost overdosing on the other end of the spectrum. Weve removed redundancy in data (our template variables) at the cost of adding redundancy in code (in the processors call). Using context processors doesnt save you much typing if you have to type processors all the time.
铏界劧杩欐槸涓绉嶆敼杩涳紝浣嗘槸锛岃鑰冭檻涓涓嬭繖娈典唬鐮佺殑绠娲佹э紝鎴戜滑鐜板湪涓嶅緱涓嶆壙璁ょ殑鏄湪 鍙﹀ 涓鏂归潰鏈変簺杩囧垎浜嗐傛垜浠互浠g爜鍐椾綑锛堝湪 processors 璋冪敤涓級鐨勪唬浠锋秷闄や簡鏁版嵁涓婄殑鍐椾綑锛堟垜浠殑妯℃澘鍙橀噺锛夈傜敱浜庝綘涓嶅緱涓嶄竴鐩撮敭鍏 processors 锛屾墍浠ヤ娇鐢╟ontext澶勭悊鍣ㄥ苟娌℃湁鍑忓皯澶鐨勬墦瀛楁鏁般
For that reason, Django provides support for global context processors. The TEMPLATE_CONTEXT_PROCESSORS setting designates which context processors should always be applied to RequestContext . This removes the need to specify processors each time you use RequestContext .
Django鍥犳鎻愪緵瀵 鍏ㄥ眬 context澶勭悊鍣ㄧ殑鏀寔銆 TEMPLATE_CONTEXT_PROCESSORS 鎸囧畾浜 鎬绘槸 浣跨敤鍝簺 context processors 銆傝繖鏍峰氨鐪佸幓浜嗘瘡娆′娇鐢 RequestContext 閮芥寚瀹 processors 鐨勯夯鐑_^銆
By default, TEMPLATE_CONTEXT_PROCESSORS is set to the following:
ilR94N <a href=”http://bxcyktdovtcx.com/“>bxcyktdovtcx</a>, [url=http://svkmchcyqtpi.com/]svkmchcyqtpi[/url], [link=http://orlgauoznlpg.com/]orlgauoznlpg[/link], http://hcmltazhxlgv.com/
TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.auth', 'django.core.context_processors.debug', 'django.core.context_processors.i18n', 'django.core.context_processors.media', )
This setting is a tuple of callables that use the same interface as our custom_proc function above functions that take a request object as their argument and return a dictionary of items to be merged into the context. Note that the values in TEMPLATE_CONTEXT_PROCESSORS are specified as strings , which means the processors are required to be somewhere on your Python path (so you can refer to them from the setting).
杩欎釜璁剧疆鏄竴涓彲璋冪敤鍑芥暟鐨凾uple锛屽叾涓殑姣忎釜鍑芥暟浣跨敤浜嗗拰涓婃枃涓垜浠殑 custom_proc 鐩稿悓鐨勬帴鍙o細鎺ユ敹涓涓猺equest瀵硅薄浣滀负鍙傛暟锛岃繑鍥炰竴涓寘鍚簡灏嗚鍚堝苟鍒癱ontext涓殑椤圭殑瀛楀吀銆傝娉ㄦ剰 TEMPLATE_CONTEXT_PROCESSORS 涓殑鍊兼槸浠 strings 鐨勫舰寮忕粰鍑虹殑锛岃繖鎰忓懗鐫杩欎簺澶勭悊鍣ㄥ繀椤诲湪浣犵殑python璺緞涓殑鏌愬锛堣繖鏍蜂綘鎵嶈兘鍦ㄨ缃腑寮曠敤瀹冧滑锛
Each processor is applied in order. That is, if one processor adds a variable to the context and a second processor adds a variable with the same name, the second will override the first.
RkbRhS <a href=”http://ssvglzgqktrs.com/“>ssvglzgqktrs</a>, [url=http://tzwexwjpftqx.com/]tzwexwjpftqx[/url], [link=http://siituwbkjtet.com/]siituwbkjtet[/link], http://nyplxetpyqkq.com/
Django provides a number of simple context processors, including the ones that are enabled by default:
Django鎻愪緵浜嗗嚑涓畝鍗曠殑context澶勭悊鍣紝鏈変簺鍦ㄩ粯璁ゆ儏鍐典笅琚惎鐢ㄧ殑銆
If TEMPLATE_CONTEXT_PROCESSORS contains this processor, every RequestContext will contain these variables:
cZgBaF <a href=”http://wqijjqhtuafe.com/“>wqijjqhtuafe</a>, [url=http://ymyewzllgpwg.com/]ymyewzllgpwg[/url], [link=http://ynrosouewqcv.com/]ynrosouewqcv[/link], http://taptrifhfbme.com/
user : A django.contrib.auth.models.User instance representing the current logged-in user (or an AnonymousUser instance, if the client isnt logged in).
user 锛氫竴涓 django.contrib.auth.models.User 瀹炰緥锛屾弿杩颁簡褰撳墠鐧诲綍鐢ㄦ埛锛堟垨鑰呬竴涓 AnonymousUser 瀹炰緥锛屽鏋滃鎴风娌℃湁鐧诲綍锛夈
messages : A list of messages (as strings) for the current logged-in user. Behind the scenes, this variable calls request.user.get_and_delete_messages() for every request. That method collects the users messages and deletes them from the database.
messages 锛氫竴涓綋鍓嶇櫥褰曠敤鎴风殑娑堟伅鍒楄〃锛堝瓧绗︿覆锛夈傚湪鍚庡彴锛屽姣忎竴涓姹傝繖涓彉閲忛兘璋冪敤 request.user.get_and_delete_messages() 鏂规硶銆傝繖涓柟娉曟敹闆嗙敤鎴风殑娑堟伅鐒跺悗鎶婂畠浠粠鏁版嵁搴撲腑鍒犻櫎銆
perms : An instance of django.core.context_processors.PermWrapper , which represents the permissions the current logged-in user has.
perms 锛 django.core.context_processors.PermWrapper 鐨勪竴涓疄渚嬶紝鍖呭惈浜嗗綋鍓嶇櫥褰曠敤鎴锋湁鍝簺鏉冮檺銆
See Chapter 12 for more information on users, permissions, and messages.
鍏充簬users銆乸ermissions鍜宮essages鐨勬洿澶氬唴瀹硅鍙傝冪12绔犮
This processor pushes debugging information down to the template layer. If TEMPLATE_CONTEXT_PROCESSORS contains this processor, every RequestContext will contain these variables:
ae2cpK <a href=”http://zekedeavaywg.com/“>zekedeavaywg</a>, [url=http://kqzsnzwpeoko.com/]kqzsnzwpeoko[/url], [link=http://ncoswfhxxpgm.com/]ncoswfhxxpgm[/link], http://vrswcfxjqbnl.com/
debug : The value of your DEBUG setting (either True or False ). You can use this variable in templates to test whether youre in debug mode.
debug 锛氫綘璁剧疆鐨 DEBUG 鐨勫硷紙 True 鎴 False 锛夈備綘鍙互鍦ㄦā鏉块噷闈㈢敤杩欎釜鍙橀噺娴嬭瘯鏄惁澶勫湪debug妯″紡涓嬨
sql_queries : A list of {'sql': ..., 'time': ...} dictionaries representing every SQL query that has happened so far during the request and how long it took. The list is in the order in which the queries were issued.
sql_queries 锛氬寘鍚被浼间簬 {'sql': ..., 'time': ...} 鐨勫瓧鍏哥殑涓涓垪琛紝 璁板綍浜嗚繖涓姹傛湡闂寸殑姣忎釜SQL鏌ヨ浠ュ強鏌ヨ鎵鑰楄垂鐨勬椂闂淬傝繖涓垪琛ㄦ槸鎸夌収璇锋眰椤哄簭杩涜鎺掑垪鐨勩
Because debugging information is sensitive, this context processor will only add variables to the context if both of the following conditions are true:
鐢变簬璋冭瘯淇℃伅姣旇緝鏁忔劅锛屾墍浠ヨ繖涓猚ontext澶勭悊鍣ㄥ彧鏈夊綋鍚屾椂婊¤冻涓嬮潰涓や釜鏉′欢鐨勬椂鍊欐墠鏈夋晥锛
The DEBUG setting is True .
VB156E <a href=”http://xsstdaarnodo.com/“>xsstdaarnodo</a>, [url=http://sjvwzwjuntje.com/]sjvwzwjuntje[/url], [link=http://rllxfurtgmdb.com/]rllxfurtgmdb[/link], http://liaaqrivlsgl.com/
The request came from an IP address in the INTERNAL_IPS setting.
璇锋眰鐨刬p搴旇鍖呭惈鍦 INTERNAL_IPS 鐨勮缃噷闈€
If this processor is enabled, every RequestContext will contain these variables:
濡傛灉杩欎釜澶勭悊鍣ㄥ惎鐢紝姣忎釜 RequestContext 灏嗗寘鍚笅闈㈢殑鍙橀噺锛
LANGUAGES : The value of the LANGUAGES setting.
LANGUAGES 锛 LANGUAGES 閫夐」鐨勫笺
LANGUAGE_CODE : request.LANGUAGE_CODE if it exists; otherwise, the value of the LANGUAGE_CODE setting.
LANGUAGE_CODE 锛氬鏋 request.LANGUAGE_CODE 瀛樺湪锛屽氨绛変簬瀹冿紱鍚﹀垯锛岀瓑鍚屼簬 LANGUAGE_CODE 璁剧疆銆
Appendix E provides more information about these two settings.
闄勫綍E鎻愪緵浜嗘湁鍏宠繖涓や釜璁剧疆鐨勬洿澶氱殑淇℃伅銆
If this processor is enabled, every RequestContext will contain a variable request , which is the current HttpRequest object. Note that this processor is not enabled by default; you have to activate it.
zEZko4 <a href=”http://davktzvegpga.com/“>davktzvegpga</a>, [url=http://dvvitkvkrqhj.com/]dvvitkvkrqhj[/url], [link=http://zmxecatgkrei.com/]zmxecatgkrei[/link], http://qxqjkagkmvbm.com/
Here are a few tips for rolling your own:
缂栧啓澶勭悊鍣ㄧ殑涓浜涘缓璁細
Make each context processor responsible for the smallest subset of functionality possible. Its easy to use multiple processors, so you might as well split functionality into logical pieces for future reuse.
浣挎瘡涓猚ontext澶勭悊鍣ㄥ畬鎴愬敖鍙兘灏忕殑鍔熻兘銆 浣跨敤澶氫釜澶勭悊鍣ㄦ槸寰堝鏄撶殑锛屾墍浠ヤ綘鍙互鏍规嵁閫昏緫鍧楁潵鍒嗚В鍔熻兘浠ヤ究灏嗘潵閲嶇敤銆
Keep in mind that any context processor in TEMPLATE_CONTEXT_PROCESSORS will be available in every template powered by that settings file, so try to pick variable names that are unlikely to conflict with variable names your templates might be using independently. As variable names are case-sensitive, its not a bad idea to use all caps for variables a processor provides.
瑕佹敞鎰 TEMPLATE_CONTEXT_PROCESSORS 閲岀殑context processor 灏嗕細鍦 姣忎釜 妯℃澘涓湁鏁堬紝鎵浠ヨ鍙橀噺鐨勫懡鍚嶄笉瑕佸拰妯℃澘鐨勫彉閲忓啿绐併 鍙橀噺鍚嶆槸澶у皬鍐欐晱鎰熺殑锛屾墍浠rocessor鐨勫彉閲忓叏鐢ㄥぇ鍐欐槸涓笉閿欑殑涓绘剰銆
It doesnt matter where on the filesystem they live, as long as theyre on your Python path so you can point to them from the TEMPLATE_CONTEXT_PROCESSORS setting. With that said, the convention is to save them in a file called context_processors.py within your app or project.
鍙瀹冧滑瀛樻斁鍦ㄤ綘鐨凱ython鐨勬悳绱㈣矾寰勪腑锛屽畠浠斁鍦ㄥ摢涓墿鐞嗚矾寰勫苟涓嶉噸瑕侊紝杩欐牱浣犲彲浠ュ湪 TEMPLATE_CONTEXT_PROCESSORS 璁剧疆閲屾寚鍚戝畠浠 涔熷氨鏄锛屼綘瑕佹妸瀹冧滑鏀惧湪app鎴栬卲roject鐩綍閲屽悕涓 context_processors.py 鐨勬枃浠躲
Generally, youll store templates in files on your filesystem, but you can use custom template loaders to load templates from other sources.
涓鑸鏉ワ紝浣犱細鎶婃ā鏉夸互鏂囦欢鐨勬柟寮忓瓨鍌ㄥ湪鏂囦欢绯荤粺涓紝浣嗘槸浣犱篃鍙互浣跨敤鑷畾涔夌殑 template loaders 浠庡叾浠栨潵婧愬姞杞芥ā鏉裤
Django has two ways to load templates:
Django鏈変袱绉嶆柟娉曞姞杞芥ā鏉
django.template.loader.get_template(template_name) : get_template returns the compiled template (a Template object) for the template with the given name. If the template doesnt exist, a TemplateDoesNotExist exception will be raised.
django.template.loader.get_template(template_name) 锛 get_template 鏍规嵁缁欏畾鐨勬ā鏉垮悕绉拌繑鍥炰竴涓凡缂栬瘧鐨勬ā鏉匡紙涓涓 Template 瀵硅薄锛夈傚鏋滄ā鏉夸笉瀛樺湪锛屽氨瑙﹀彂 TemplateDoesNotExist 鐨勫紓甯搞
django.template.loader.select_template(template_name_list) : select_template is just like get_template , except it takes a list of template names. Of the list, it returns the first template that exists. If none of the templates exist, a TemplateDoesNotExist exception will be raised.
django.template.loader.select_template(template_name_list) 锛 select_template 寰堝儚 get_template 锛屼笉杩囧畠鏄互妯℃澘鍚嶇О鐨勫垪琛ㄤ綔涓哄弬鏁扮殑锛屽苟涓斿畠杩斿洖绗竴涓瓨鍦ㄧ殑妯℃澘銆傚鏋滄ā鏉块兘涓嶅瓨鍦紝灏嗕細瑙﹀彂 TemplateDoesNotExist 寮傚父銆
As covered in Chapter 4, each of these functions by default uses your TEMPLATE_DIRS setting to load templates. Internally, however, these functions actually delegate to a template loader for the heavy lifting.
姝e鍦ㄧ鍥涚珷涓墍鎻愬埌鐨勶紝榛樿鎯呭喌涓嬭繖浜涘嚱鏁颁娇鐢 TEMPLATE_DIRS 鐨勮缃潵杞藉叆妯℃澘銆備絾鏄紝鍦ㄥ唴閮ㄨ繖浜涘嚱鏁板彲浠ユ寚瀹氫竴涓ā鏉垮姞杞藉櫒鏉ュ畬鎴愯繖浜涚箒閲嶇殑浠诲姟銆
Some of loaders are disabled by default, but you can activate them by editing the TEMPLATE_LOADERS setting. TEMPLATE_LOADERS should be a tuple of strings, where each string represents a template loader. These template loaders ship with Django:
涓浜涘姞杞藉櫒榛樿琚鐢紝浣嗘槸浣犲彲浠ラ氳繃缂栬緫 TEMPLATE_LOADERS 璁剧疆鏉ユ縺娲诲畠浠 TEMPLATE_LOADERS 搴斿綋鏄竴涓瓧绗︿覆鐨勫厓缁勶紝鍏朵腑姣忎釜瀛楃涓查兘琛ㄧず涓涓ā鏉垮姞杞藉櫒銆傝繖浜涙ā鏉垮姞杞藉櫒闅廌jango涓璧峰彂甯冦
django.template.loaders.filesystem.load_template_source : This loader loads templates from the filesystem, according to TEMPLATE_DIRS . It is enabled by default.
django.template.loaders.filesystem.load_template_source : 杩欎釜鍔犺浇鍣ㄦ牴鎹 TEMPLATE_DIRS 鐨勮缃粠鏂囦欢绯荤粺鍔犺浇妯℃澘銆傚湪榛樿鎯呭喌涓嬭繖涓姞杞藉櫒琚惎鐢.
django.template.loaders.app_directories.load_template_source : This loader loads templates from Django applications on the filesystem. For each application in INSTALLED_APPS , the loader looks for a templates subdirectory. If the directory exists, Django looks for templates there.
django.template.loaders.app_directories.load_template_source : 杩欎釜鍔 杞藉櫒浠庢枃浠剁郴缁熶笂鐨凞jango搴旂敤涓姞杞芥ā鏉裤傚 INSTALLED_APPS 涓殑姣忎釜搴旂敤锛岃繖涓姞 杞藉櫒浼氭煡鎵句竴涓 templates 瀛愮洰褰曘傚鏋滆繖涓洰褰曞瓨鍦紝Django灏卞湪閭i噷瀵绘壘妯℃澘銆
This means you can store templates with your individual applications, making it easy to distribute Django applications with default templates. For example, if INSTALLED_APPS contains ('myproject.polls', 'myproject.music') , then get_template('foo.html') will look for templates in this order:
杩欐剰鍛崇潃浣犲彲浠ユ妸妯℃澘鍜屼綘鐨勫簲鐢ㄤ竴璧蜂繚瀛橈紝浠庤屼娇寰桪jango搴旂敤鏇村鏄撳拰榛樿妯℃澘涓璧峰彂甯冦備緥濡傦紝濡傛灉 INSTALLED_APPS 鍖呭惈 ('myproject.polls','myproject.music') 锛岄偅涔 get_template('foo.html') 浼氭寜杩欎釜椤哄簭鏌ユ壘妯℃澘锛
/path/to/myproject/polls/templates/foo.html
/path/to/myproject/polls/templates/foo.html
/path/to/myproject/music/templates/foo.html
zS7gE0 <a href=”http://xihwijxziqzw.com/“>xihwijxziqzw</a>, [url=http://latxmxlseqcl.com/]latxmxlseqcl[/url], [link=http://fwlitcrfihxf.com/]fwlitcrfihxf[/link], http://yxtsdtenuwcv.com/
Note that the loader performs an optimization when it is first imported: it caches a list of which INSTALLED_APPS packages have a templates subdirectory.
璇锋敞鎰忓姞杞藉櫒鍦ㄩ娆¤瀵煎叆鐨勬椂鍊欎細鎵ц涓涓紭鍖栵細瀹冧細缂撳瓨涓涓垪琛紝杩欎釜鍒楄〃鍖呭惈浜 INSTALLED_APPS 涓甫鏈 templates 瀛愮洰褰曠殑鍖呫
This loader is enabled by default.
杩欎釜鍔犺浇鍣ㄩ粯璁ゅ惎鐢ㄣ
django.template.loaders.eggs.load_template_source : This loader is just like app_directories , except it loads templates from Python eggs rather than from the filesystem. This loader is disabled by default; youll need to enable it if youre using eggs to distribute your application.
django.template.loaders.eggs.load_template_source : 杩欎釜鍔犺浇鍣ㄧ被浼 app_directories 锛屽彧涓嶈繃瀹冧粠Python eggs鑰屼笉鏄枃浠剁郴缁熶腑鍔犺浇妯℃澘銆傝繖涓姞杞藉櫒榛樿琚鐢紱濡傛灉浣犱娇鐢╡ggs鏉ュ彂甯冧綘鐨勫簲鐢紝閭d箞浣犲氨闇瑕佸惎鐢ㄥ畠銆
Django uses the template loaders in order according to the TEMPLATE_LOADERS setting. It uses each loader until a loader finds a match.
Django鎸夌収 TEMPLATE_LOADERS 璁剧疆涓殑椤哄簭浣跨敤妯℃澘鍔犺浇鍣ㄣ傚畠閫愪釜浣跨敤姣忎釜鍔犺浇鍣ㄧ洿鑷虫壘鍒颁竴涓尮閰嶇殑妯℃澘銆
Now that you understand a bit more about the internals of the template system, lets look at how to extend the system with custom code.
鏃㈢劧浣犲凡缁忓妯℃澘绯荤粺鐨勫唴骞曚簡瑙e浜嗕竴浜涳紝璁╂垜浠潵鐪嬬湅濡備綍浣跨敤鑷畾涔夌殑浠g爜鏉ユ嫇灞曡繖涓郴缁熷惂銆
Most template customization comes in the form of custom template tags and/or filters. Although the Django template language comes with many built-in tags and filters, youll probably assemble your own libraries of tags and filters that fit your own needs. Fortunately, its quite easy to define your own functionality.
缁濆ぇ閮ㄥ垎鐨勬ā鏉垮畾鍒舵槸浠ヨ嚜瀹氫箟鏍囩/杩囨护鍣ㄧ殑鏂瑰紡鏉ュ畬鎴愮殑銆傚敖绠jango妯℃澘璇█鑷甫浜嗚澶氬唴寤烘爣绛惧拰杩囨护鍣紝浣嗘槸浣犲彲鑳借繕鏄渶瑕佺粍寤轰綘鑷繁鐨勬爣绛惧拰杩囨护鍣ㄥ簱鏉ユ弧瓒充綘鐨勯渶瑕併傚垢杩愮殑鏄紝瀹氫箟浣犺嚜宸辩殑鍔熻兘闈炲父瀹规槗銆
Whether youre writing custom tags or filters, the first thing to do is to create a template library a small bit of infrastructure Django can hook into.
涓嶇鏄啓鑷畾涔夋爣绛捐繕鏄繃婊ゅ櫒锛岀涓浠惰鍋氱殑浜嬫槸缁 template library 鍒涘缓浣緿jango鑳藉鍕惧叆鐨勬満鍒躲
Creating a template library is a two-step process:
鍒涘缓涓涓ā鏉垮簱鍒嗕袱姝ヨ蛋锛
First, decide which Django application should house the template library. If youve created an app via manage.py startapp , you can put it in there, or you can create another app solely for the template library.
绗竴锛屽喅瀹氬摢涓狣jango搴旂敤搴斿綋鎷ユ湁杩欎釜妯℃澘搴撱傚鏋滀綘閫氳繃 manage.py startapp 鍒涘缓浜嗕竴涓簲鐢紝浣犲彲浠ユ妸瀹冩斁鍦ㄩ偅閲岋紝鎴栬呬綘鍙互涓烘ā鏉垮簱鍗曠嫭鍒涘缓涓涓簲鐢ㄣ
Whichever route you take, make sure to add the app to your INSTALLED_APPS setting. Well explain this shortly.
鏃犺浣犻噰鐢ㄤ綍绉嶆柟寮忥紝璇风‘淇濇妸浣犵殑搴旂敤娣诲姞鍒 INSTALLED_APPS 涓傛垜浠◢鍚庝細瑙i噴杩欎竴鐐广
Second, create a templatetags directory in the appropriate Django applications package. It should be on the same level as models.py , views.py , and so forth. For example:
绗簩锛屽湪閫傚綋鐨凞jango搴旂敤鍖呴噷鍒涘缓涓涓 templatetags 鐩綍銆傝繖涓洰褰曞簲褰撳拰 models.py 銆 views.py 绛夊浜庡悓涓灞傛銆備緥濡傦細
books/ __init__.py models.py templatetags/ views.py
Create two empty files in the templatetags directory: an __init__.py file (to indicate to Python that this is a package containing Python code) and a file that will contain your custom tag/filter definitions. The name of the latter file is what youll use to load the tags later. For example, if your custom tags/filters are in a file called poll_extras.py , youd write the following in a template:
鍦 templatetags 涓垱寤轰袱涓┖鏂囦欢锛氫竴涓 __init__.py 锛堝憡璇塒ython杩欐槸 涓涓寘鍚簡Python浠g爜鐨勫寘锛夊拰涓涓敤鏉ュ瓨鏀句綘鑷畾涔夌殑鏍囩/杩囨护鍣ㄥ畾涔夌殑鏂囦欢銆傜浜屼釜鏂囦欢 鐨勫悕瀛楃◢鍚庡皢鐢ㄦ潵鍔犺浇鏍囩銆備緥濡傦紝濡傛灉浣犵殑鑷畾涔夋爣绛/杩囨护鍣ㄥ湪涓涓彨浣 poll_extras.py 鐨勬枃浠朵腑锛屼綘闇瑕佸湪妯℃澘涓啓鍏ュ涓嬪唴瀹癸細
{% load poll_extras %}
The {% load %} tag looks at your INSTALLED_APPS setting and only allows the loading of template libraries within installed Django applications. This is a security feature; it allows you to host Python code for many template libraries on a single computer without enabling access to all of them for every Django installation.
{% load %} 鏍囩妫鏌 INSTALLED_APPS 涓殑璁剧疆锛屼粎鍏佽鍔犺浇宸插畨瑁呯殑Django搴旂敤绋嬪簭涓殑妯℃澘搴撱傝繖鏄竴涓畨鍏ㄧ壒鎬с傚畠鍙互璁╀綘鍦ㄤ竴鍙扮數鑴戜笂閮ㄧ讲寰堝鐨勬ā鏉垮簱鐨勪唬鐮侊紝鑰屽張涓嶇敤鎶婂畠浠毚闇茬粰姣忎竴涓狣jango瀹夎銆
If you write a template library that isnt tied to any particular models/views, its valid and quite normal to have a Django application package that contains only a templatetags package. Theres no limit on how many modules you put in the templatetags package. Just keep in mind that a {% load %} statement will load tags/filters for the given Python module name, not the name of the application.
濡傛灉浣犲啓浜嗕竴涓笉鍜屼换浣曟ā鍨/瑙嗗浘鍏宠仈鐨勬ā鏉垮簱锛岄偅涔堝緱鍒颁竴涓粎鍖呭惈 templatetags 鍖呯殑Django搴旂敤绋嬪簭鍖呮槸瀹屽叏姝e父鐨勩傚浜庡湪 templatetags 鍖呬腑鏀剧疆澶氬皯涓ā鍧楁病鏈夊仛浠讳綍鐨勯檺鍒躲傞渶瑕佷簡瑙g殑鏄細 {% load %} 璇彞浼氫负鎸囧畾鐨凱ython妯″潡鍚嶏紙鑰岄潪搴旂敤绋嬪簭鍚嶏級鍔犺浇鏍囩鎴栬繃婊ゅ櫒銆
Once youve created that Python module, youll just have to write a bit of Python code, depending on whether youre writing filters or tags.
涓鏃﹀垱寤轰簡Python妯″潡锛屼綘鍙渶鏍规嵁鏄缂栧啓杩囨护鍣ㄨ繕鏄爣绛炬潵鐩稿簲鐨勭紪鍐欎竴浜汸ython浠g爜銆
To be a valid tag library, the module must contain a module-level variable named register that is a template.Library instance. This template.Library instance is the data structure in which all the tags and filters are registered. So, near the top of your module, insert the following:
瑕佹垚涓烘湁鏁堢殑鏍囩搴擄紝妯″潡蹇呴』鍖呭惈涓涓ā鍧楃骇鐨勫彉閲忥細 register 锛岃繖鏄竴涓 template.Library 鐨勫疄渚嬨傝繖涓 template.Library 瀹炰緥鏄寘鍚墍鏈夊凡娉ㄥ唽鐨勬爣绛惧強杩囨护鍣ㄧ殑鏁版嵁缁撴瀯銆傚洜姝わ紝鍦ㄦā鍧楃殑椤堕儴浣嶇疆鎻掑叆涓嬭堪浠g爜锛
from django import template register = template.Library()
Note
澶囨敞
For a good number of examples, read the source code for Djangos default filters and tags. Theyre in django/template/defaultfilters.py and django/template/defaulttags.py , respectively. Some applications in django.contrib also contain template libraries.
璇烽槄璇籇jango榛樿鐨勮繃婊ゅ櫒鍜屾爣绛剧殑婧愮爜锛岄偅閲屾湁澶ч噺鐨勪緥瀛愩備粬浠垎鍒负锛 django/template/defaultfilters.py 鍜 django/template/defaulttags.py 銆傛煇浜涘簲鐢ㄧ▼搴忓湪 django.contrib 涓篃鍖呭惈妯℃澘搴撱
Once youve created this register variable, youll use it to create template filters and tags.
鍒涘缓 register 鍙橀噺鍚庯紝浣犲氨鍙互浣跨敤瀹冩潵鍒涘缓妯℃澘鐨勮繃婊ゅ櫒鍜屾爣绛句簡銆
Custom filters are just Python functions that take one or two arguments:
鑷畾涔夎繃婊ゅ櫒灏辨槸鏈変竴涓垨涓や釜鍙傛暟鐨凱ython鍑芥暟:
The value of the variable (input)
(杈撳叆)鍙橀噺鐨勫
The value of the argument, which can have a default value or be left out altogether
鍙傛暟鐨勫硷紝 鍙互鏄粯璁ゅ兼垨鑰呭畬鍏ㄧ暀绌
For example, in the filter {{ var|foo:"bar" }} , the filter foo would be passed the contents of the variable var and the argument "bar" .
渚嬪锛屽湪杩囨护鍣 {{ var|foo:"bar" }} 涓 锛岃繃婊ゅ櫒 foo 浼氳浼犲叆鍙橀噺 var 鍜屽弬鏁 bar 鐨勫唴瀹广
Filter functions should always return something. They shouldnt raise exceptions, and they should fail silently. If theres an error, they should return either the original input or an empty string, whichever makes more sense.
杩囨护鍣ㄥ嚱鏁板簲璇ユ绘湁杩斿洖鍊硷紝鑰屼笖涓嶈兘瑙﹀彂寮傚父锛屽畠浠兘搴旇闈欓潤鐨勫け璐ャ傚鏋滄湁涓涓敊璇彂鐢燂紝瀹冧滑瑕佷箞杩斿洖鍘熷鐨勮緭鍏ュ瓧绗︿覆锛岃涔堣繑鍥炵┖鐨勫瓧绗︿覆锛屾棤璁哄摢涓兘鍙互銆
Heres an example filter definition:
杩欓噷鏄竴浜涘畾涔夎繃婊ゅ櫒鐨勪緥瀛愶細
def cut(value, arg): "Removes all values of arg from the given string" return value.replace(arg, '')
And heres an example of how that filter would be used:
杩欓噷鏄竴浜涘浣曚娇鐢ㄨ繃婊ゅ櫒鐨勪緥瀛愶細
{{ somevariable|cut:"0" }}
Most filters dont take arguments. In this case, just leave the argument out of your function:
澶у鏁拌繃婊ゅ櫒骞朵笉闇瑕佸弬鏁般備笅闈㈢殑渚嬪瓙鎶婂弬鏁颁粠浣犵殑鍑芥暟涓嬁鎺変簡锛
def lower(value): # Only one argument. "Converts a string into all lowercase" return value.lower()
When youve written your filter definition, you need to register it with your Library instance, to make it available to Djangos template language:
褰撲綘鍦ㄥ畾涔変綘鐨勮繃婊ゅ櫒鏃讹紝浣犻渶瑕佺敤 Library 瀹炰緥鏉ユ敞鍐屽畠锛岃繖鏍峰氨鑳介氳繃Django鐨勬ā鏉胯瑷鏉ヤ娇鐢ㄤ簡锛
register.filter('cut', cut) register.filter('lower', lower)
The Library.filter() method takes two arguments:
Library.filter() 鏂规硶闇瑕佷袱涓弬鏁帮細
The name of the filter (a string)
杩囨护鍣ㄧ殑鍚嶇О锛堜竴涓瓧涓诧級
The filter function itself
杩囨护鍣ㄥ嚱鏁版湰韬
If youre using Python 2.4 or above, you can use register.filter() as a decorator instead:
濡傛灉浣犱娇鐢ㄧ殑鏄疨ython 2.4鎴栨洿鏂帮紝浣犲彲浠ヤ娇鐢 register.filter() 浣滀负涓涓楗板櫒锛
@register.filter(name='cut') def cut(value, arg): return value.replace(arg, '') @register.filter def lower(value): return value.lower()
If you leave off the name argument, as in the second example, Django will use the functions name as the filter name.
鍍忕浜屼釜渚嬪瓙涓紝濡傛灉浣犱笉浣跨敤 name 鍙傛暟锛岄偅涔圖jango灏嗕細浣跨敤鍑芥暟鍚嶄綔涓鸿繃婊ゅ櫒鐨勫悕瀛椼
Here, then, is a complete template library example, supplying the cut filter:
涓嬮潰鏄竴涓畬鏁寸殑妯℃澘搴撶殑渚嬪瓙锛屾彁渚涗簡涓涓 cut 杩囨护鍣細
from django import template register = template.Library() @register.filter(name='cut') def cut(value, arg): return value.replace(arg, '')
Tags are more complex than filters, because tags can do nearly anything.
鏍囩瑕佹瘮杩囨护鍣ㄥ鏉備簺锛屾爣绛惧嚑涔庤兘鍋氫换浣曚簨鎯呫
Chapter 4 describes how the template system works in a two-step process: compiling and rendering. To define a custom template tag, you need to tell Django how to manage both steps when it gets to your tag.
绗洓绔犳弿杩颁簡妯℃澘绯荤粺鐨勪袱姝ュ鐞嗚繃绋嬶細缂栬瘧鍜屽憟鐜般備负浜嗚嚜瀹氫箟涓涓繖鏍风殑妯℃澘鏍囩锛屼綘闇瑕佸憡璇塂jango褰撻亣鍒颁綘鐨勬爣绛炬椂鎬庢牱杩涜杩欒繃绋嬨
When Django compiles a template, it splits the raw template text into nodes . Each node is an instance of django.template.Node and has a render() method. Thus, a compiled template is simply a list of Node objects.
褰揇jango缂栬瘧涓涓ā鏉挎椂锛屽畠灏嗗師濮嬫ā鏉垮垎鎴愪竴涓釜 鑺傜偣 銆傛瘡涓妭鐐归兘鏄 django.template.Node 鐨勪竴涓疄渚嬶紝骞朵笖鍏峰 render() 鏂规硶銆備簬鏄紝涓涓凡缂栬瘧鐨勬ā鏉垮氨鏄 Node 瀵硅薄鐨勪竴涓垪琛ㄣ
When you call render() on a compiled template, the template calls render() on each Node in its node list, with the given context. The results are all concatenated together to form the output of the template. Thus, to define a custom template tag, you specify how the raw template tag is converted into a Node (the compilation function) and what the nodes render() method does.
褰撲綘璋冪敤涓涓凡缂栬瘧妯℃澘鐨 render() 鏂规硶鏃讹紝妯℃澘灏变細鐢ㄧ粰瀹氱殑context鏉ヨ皟鐢ㄦ瘡涓湪瀹冪殑鑺傜偣鍒楄〃涓婄殑鑺傜偣鐨 render() 鏂规硶銆傛墍浠ワ紝涓轰簡瀹氫箟涓涓嚜瀹氫箟鐨勬ā鏉挎爣绛撅紝浣犻渶瑕佹槑纭繖涓ā鏉挎爣绛捐浆鎹负涓涓 Node 锛堝凡缂栬瘧鐨勫嚱鏁帮級鍜岃繖涓猲ode鐨 render() 鏂规硶銆
In the sections that follow, we cover all the steps in writing a custom tag.
鍦ㄤ笅闈㈢殑绔犺妭涓紝鎴戜滑灏嗚缁嗚В璇村啓涓涓嚜瀹氫箟鏍囩鏃剁殑鎵鏈夋楠ゃ
For each template tag it encounters, the template parser calls a Python function with the tag contents and the parser object itself. This function is responsible for returning a Node instance based on the contents of the tag.
褰撻亣鍒颁竴涓ā鏉挎爣绛撅紙template tag锛夋椂锛屾ā鏉胯В鏋愬櫒灏变細鎶婃爣绛惧寘鍚殑鍐呭锛屼互鍙婃ā鏉胯В鏋愬櫒鑷繁浣滀负鍙傛暟璋冪敤涓涓猵ython鍑芥暟銆傝繖涓嚱鏁拌礋璐h繑鍥炰竴涓拰褰撳墠妯℃澘鏍囩鍐呭鐩稿搴旂殑鑺傜偣锛圢ode锛夌殑瀹炰緥銆
For example, lets write a template tag, {% current_time %} , that displays the current date/time, formatted according to a parameter given in the tag, in strftime syntax (see http://www.djangoproject.com/r/python/strftime/ ). Its a good idea to decide the tag syntax before anything else. In our case, lets say the tag should be used like this:
渚嬪锛屽啓涓涓樉绀哄綋鍓嶆棩鏈熺殑妯℃澘鏍囩锛歿% current_time %}锛岃鏍囩浼氭牴鎹弬鏁版寚瀹氱殑 strftime 鏍煎紡锛堝弬瑙侊細 http://www.djangoproject.com/r/python/strftime/ 锛夋樉绀哄綋鍓嶆椂闂淬傚湪缁х画鍋氬叾瀹冧簨鎯呬互鍓嶏紝鍏堝喅瀹氭爣绛剧殑璇硶鏄竴涓ソ涓绘剰銆傚湪鎴戜滑鐨勪緥瀛愰噷锛岃鏍囩灏嗕細鍍忚繖鏍疯浣跨敤锛
<p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>
Note
澶囨敞
Yes, this template tag is redundantDjangos default {% now %} tag does the same task with simpler syntax. This template tag is presented here just for example purposes.
娌¢敊, 杩欎釜妯℃澘鏍囩鏄浣欑殑锛孌jango榛樿鐨 {% now %} 鐢ㄦ洿绠鍗曠殑璇硶瀹屾垚浜嗗悓鏍风殑宸ヤ綔銆傝繖涓ā鏉挎爣绛惧湪杩欓噷鍙槸浣滀负涓涓緥瀛愩
The parser for this function should grab the parameter and create a Node object:
杩欎釜鍑芥暟鐨勫垎鏋愬櫒浼氳幏鍙栧弬鏁板苟鍒涘缓涓涓 Node 瀵硅薄:
from django import template def do_current_time(parser, token): try: # split_contents() knows not to split quoted strings. tag_name, format_string = token.split_contents() except ValueError: msg = '%r tag requires a single argument' % token.contents[0] raise template.TemplateSyntaxError(msg) return CurrentTimeNode(format_string[1:-1])
Theres actually a lot going here:
鍏跺疄杩欏効鍖呭惈浜嗕笉灏戜笢瑗匡細
parser is the template parser object. We dont need it in this example.
parser 鏄ā鏉垮垎鏋愬櫒瀵硅薄锛屽湪杩欎釜渚嬪瓙涓垜浠病鏈変娇鐢ㄥ畠銆
token.contents is a string of the raw contents of the tag. In our example, its 'current_time "%Y-%m-%d %I:%M %p"' .
token.contents 鏄寘鍚湁鏍囩鍘熷鍐呭鐨勫瓧绗︿覆銆傚湪鎴戜滑鐨勪緥瀛愪腑锛屽畠鏄 'current_time "%Y-%m-%d %I:%M %p"' 銆
The token.split_contents() method separates the arguments on spaces while keeping quoted strings together. Avoid using token.contents.split() (which just uses Pythons standard string-splitting semantics). Its not as robust, as it naively splits on all spaces, including those within quoted strings.
token.split_contents() 鏂规硶鎸夌┖鏍兼媶鍒嗗弬鏁板悓鏃朵繚璇佸紩鍙蜂腑鐨勫瓧绗︿覆鍦ㄤ竴璧枫傚簲璇ラ伩鍏嶄娇鐢 token.contents.split() 锛堜粎鏄娇鐢≒ython鐨勬爣鍑嗗瓧绗︿覆鎷嗗垎锛夛紝瀹冧笉澶熷仴澹紝鍥犱负瀹冨彧鏄畝鍗曠殑鎸夌収 鎵鏈 绌烘牸杩涜鎷嗗垎锛屽寘鎷偅浜涘紩鍙峰紩璧锋潵鐨勫瓧绗︿覆涓殑绌烘牸銆
This function is responsible for raising django.template.TemplateSyntaxError , with helpful messages, for any syntax error.
杩欎釜鍑芥暟璐熻矗鎶涘嚭 django.template.TemplateSyntaxError 锛屽悓鏃舵彁渚涙墍鏈夎娉曢敊璇殑鏈夌敤淇℃伅銆
Dont hard-code the tags name in your error messages, because that couples the tags name to your function. token.split_contents()[0] will always be the name of your tageven when the tag has no arguments.
涓嶈鎶婃爣绛惧悕绉扮‖缂栫爜鍦ㄤ綘鐨勯敊璇俊鎭腑锛屽洜涓鸿繖鏍蜂細鎶婃爣绛惧悕绉板拰浣犵殑鍑芥暟鑰﹀悎鍦ㄤ竴璧枫 token.split_contents()[0] 鎬讳細鏄 鏄綘鐨勬爣绛剧殑鍚嶇О锛屽嵆浣挎爣绛炬病鏈夊弬鏁般
The function returns a CurrentTimeNode (which well create shortly) containing everything the node needs to know about this tag. In this case, it just passes the argument "%Y-%m-%d %I:%M %p" . The leading and trailing quotes from the template tag are removed with format_string[1:-1] .
杩欎釜鍑芥暟杩斿洖涓涓 CurrentTimeNode 锛堢◢鍚庢垜浠皢鍒涘缓瀹冿級锛屽畠鍖呭惈浜嗚妭鐐归渶瑕佺煡閬撶殑鍏充簬杩欎釜鏍囩鐨勫叏閮ㄤ俊鎭傚湪杩欎釜渚嬪瓙涓紝瀹冨彧鏄紶閫掍簡鍙傛暟 "%Y-%m-%d %I:%M %p" 銆傛ā鏉挎爣绛惧紑澶村拰缁撳熬鐨勫紩鍙蜂娇鐢 format_string[1:-1] 闄ゅ幓銆
Template tag compilation functions must return a Node subclass; any other return value is an error.
妯℃澘鏍囩缂栬瘧鍑芥暟 蹇呴』 杩斿洖涓涓 Node 瀛愮被锛岃繑鍥炲叾瀹冨奸兘鏄敊鐨勩
The second step in writing custom tags is to define a Node subclass that has a render() method. Continuing the preceding example, we need to define CurrentTimeNode :
缂栧啓鑷畾涔夋爣绛剧殑绗簩姝ュ氨鏄畾涔変竴涓嫢鏈 render() 鏂规硶鐨 Node 瀛愮被銆傜户缁墠闈㈢殑渚嬪瓙锛屾垜浠渶瑕佸畾涔 CurrentTimeNode 锛
import datetime class CurrentTimeNode(template.Node): def __init__(self, format_string): self.format_string = format_string def render(self, context): now = datetime.datetime.now() return now.strftime(self.format_string)
These two functions (__init__ and render ) map directly to the two steps in template processing (compilation and rendering). Thus, the initialization function only needs to store the format string for later use, and the render() function does the real work.
杩欎袱涓嚱鏁帮紙 __init__ 鍜 render 锛変笌妯℃澘澶勭悊涓殑涓ゆ锛堢紪璇戜笌娓叉煋锛夌洿鎺ュ搴斻傝繖鏍凤紝鍒濆鍖栧嚱鏁颁粎浠呴渶瑕佸瓨鍌ㄥ悗闈㈣鐢ㄥ埌鐨勬牸寮忓瓧绗︿覆锛岃 render() 鍑芥暟鎵嶅仛鐪熸鐨勫伐浣溿
Like template filters, these rendering functions should fail silently instead of raising errors. The only time that template tags are allowed to raise errors is at compilation time.
涓庢ā鏉胯繃婊ゅ櫒涓鏍凤紝杩欎簺娓叉煋鍑芥暟搴旇鎹曡幏閿欒锛岃屼笉鏄姏鍑洪敊璇傛ā鏉挎爣绛惧彧鑳藉湪缂栬瘧鐨勬椂鍊欐墠鑳芥姏鍑洪敊璇
Finally, you need to register the tag with your modules Library instance. Registering custom tags is very similar to registering custom filters (as explained above). Just instantiate a template.Library instance and call its tag() method. For example:
鏈鍚庯紝浣犻渶瑕佺敤浣犵殑妯″潡 Library 瀹炰緥娉ㄥ唽杩欎釜鏍囩銆傛敞鍐岃嚜瀹氫箟鏍囩涓庢敞鍐岃嚜瀹氫箟杩囨护鍣ㄩ潪甯哥被浼硷紙濡傚墠鏂囨墍杩帮級銆傚疄渚嬪寲涓涓 template.Library 瀹炰緥鐒跺悗璋冪敤瀹冪殑 tag() 鏂规硶銆備緥濡傦細
register.tag('current_time', do_current_time)
The tag() method takes two arguments:
tag() 鏂规硶闇瑕佷袱涓弬鏁:
The name of the template tag (string). If this is left out, the
妯℃澘鏍囩鐨勫悕瀛楋紙瀛楃涓诧級銆傚鏋滆閬楁紡鐨勮瘽锛屽皢浼氫娇鐢ㄧ紪璇戝嚱鏁扮殑鍚嶅瓧銆
name of the compilation function will be used.
The compilation function.
缂栬瘧鍑芥暟銆
As with filter registration, it is also possible to use register.tag as a decorator in Python 2.4 and above:
鍜屾敞鍐岃繃婊ゅ櫒绫讳技锛屼篃鍙互鍦≒ython2.4鍙婂叾浠ヤ笂鐗堟湰涓娇鐢 register.tag 淇グ锛
@register.tag(name="current_time") def do_current_time(parser, token): # ... @register.tag def shout(parser, token): # ...
If you leave off the name argument, as in the second example, Django will use the functions name as the tag name.
濡傛灉浣犲儚鍦ㄧ浜屼釜渚嬪瓙涓偅鏍峰拷鐣 name 鍙傛暟鐨勮瘽锛孌jango浼氫娇鐢ㄥ嚱鏁板悕绉颁綔涓烘爣绛惧悕绉般
The previous sections example simply returned a value. Often its useful to set template variables instead of returning values. That way, template authors can just use the variables that your template tags set.
鍓嶄竴鑺傜殑渚嬪瓙鍙槸绠鍗曠殑杩斿洖涓涓笺傚緢澶氭椂鍊欒缃竴涓ā鏉垮彉閲忚岄潪杩斿洖鍊间篃寰堟湁鐢ㄣ傞偅鏍凤紝妯℃澘浣滆呭氨鍙兘浣跨敤浣犵殑妯℃澘鏍囩鎵璁剧疆鐨勫彉閲忋
To set a variable in the context, use dictionary assignment on the context object in the render() method. Heres an updated version of CurrentTimeNode that sets a template variable, current_time , instead of returning it:
瑕佸湪涓婁笅鏂囦腑璁剧疆鍙橀噺锛屽湪 render() 鍑芥暟鐨刢ontext瀵硅薄涓婁娇鐢ㄥ瓧鍏歌祴鍊笺傝繖閲屾槸涓涓慨鏀硅繃鐨 CurrentTimeNode 锛屽叾涓瀹氫簡涓涓ā鏉垮彉閲 current_time 锛屽苟娌℃湁杩斿洖瀹冿細
class CurrentTimeNode2(template.Node): def __init__(self, format_string): self.format_string = format_string def render(self, context): now = datetime.datetime.now() context['current_time'] = now.strftime(self.format_string) return ''
Note that render() returns an empty string. render() should always return a string, so if all the template tag does is set a variable, render() should return an empty string.
娉ㄦ剰 render() 杩斿洖浜嗕竴涓┖瀛楃涓层 render() 搴斿綋鎬绘槸杩斿洖涓涓瓧绗︿覆锛屾墍浠ュ鏋滄ā鏉挎爣绛惧彧鏄璁剧疆鍙橀噺锛 render() 灏卞簲璇ヨ繑鍥炰竴涓┖瀛楃涓层
Heres how youd use this new version of the tag:
浣犲簲璇ヨ繖鏍蜂娇鐢ㄨ繖涓柊鐗堟湰鐨勬爣绛撅細
{% current_time2 "%Y-%M-%d %I:%M %p" %} <p>The time is {{ current_time }}.</p>
But theres a problem with CurrentTimeNode2 : the variable name current_time is hard-coded. This means youll need to make sure your template doesnt use {{ current_time }} anywhere else, because {% current_time2 %} will blindly overwrite that variables value.
浣嗘槸 CurrentTimeNode2 鏈変竴涓棶棰: 鍙橀噺鍚 current_time 鏄‖缂栫爜鐨勩傝繖鎰忓懗鐫浣犲繀椤荤‘瀹氫綘鐨勬ā鏉垮湪鍏跺畠浠讳綍鍦版柟閮戒笉浣跨敤 {{ current_time }} 锛屽洜涓 {% current_time2 %} 浼氱洸鐩殑瑕嗙洊璇ュ彉閲忕殑鍊笺
A cleaner solution is to make the template tag specify the name of the variable to be set, like so:
涓绉嶆洿绠娲佺殑鏂规鏄敱妯℃澘鏍囩鏉ユ寚瀹氶渶瑕佽瀹氱殑鍙橀噺鐨勫悕绉帮紝灏卞儚杩欐牱锛
{% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %} <p>The current time is {{ my_current_time }}.</p>
To do so, youll need to refactor both the compilation function and the Node class, as follows:
涓烘锛屼綘闇瑕侀噸鏋勭紪璇戝嚱鏁板拰 Node 绫伙紝濡備笅鎵绀猴細
import re class CurrentTimeNode3(template.Node): def __init__(self, format_string, var_name): self.format_string = format_string self.var_name = var_name def render(self, context): now = datetime.datetime.now() context[self.var_name] = now.strftime(self.format_string) return '' def do_current_time(parser, token): # This version uses a regular expression to parse tag contents. try: # Splitting by None == splitting by spaces. tag_name, arg = token.contents.split(None, 1) except ValueError: msg = '%r tag requires arguments' % token.contents[0] raise template.TemplateSyntaxError(msg) m = re.search(r'(.*?) as (\w+)', arg) if m: fmt, var_name = m.groups() else: msg = '%r tag had invalid arguments' % tag_name raise template.TemplateSyntaxError(msg) if not (fmt[0] == fmt[-1] and fmt[0] in ('"', "'")): msg = "%r tag's argument should be in quotes" % tag_name raise template.TemplateSyntaxError(msg) return CurrentTimeNode3(fmt[1:-1], var_name)
Now do_current_time() passes the format string and the variable name to CurrentTimeNode3 .
鐜板湪 do_current_time() 鎶婃牸寮忓瓧绗︿覆鍜屽彉閲忓悕浼犻掔粰 CurrentTimeNode3 銆
Template tags can work as blocks containing other tags (think {% if %} , {% for %} , etc.). To create a template tag like this, use parser.parse() in your compilation function.
妯℃澘鏍囩鍙互鍍忓寘鍚叾瀹冩爣绛剧殑鍧椾竴鏍峰伐浣滐紙鎯虫兂 {% if %} 銆 {% for %} 绛夛級銆 瑕佸垱寤轰竴涓繖鏍风殑妯℃澘鏍囩锛屽湪浣犵殑缂栬瘧鍑芥暟涓娇鐢 parser.parse() 銆
Heres how the standard {% comment %} tag is implemented:
鏍囧噯鐨 {% comment %} 鏍囩鏄繖鏍峰疄鐜扮殑锛
def do_comment(parser, token): nodelist = parser.parse(('endcomment',)) parser.delete_first_token() return CommentNode() class CommentNode(template.Node): def render(self, context): return ''
parser.parse() takes a tuple of names of block tags to parse until. It returns an instance of django.template.NodeList , which is a list of all Node objects that the parser encountered before it encountered any of the tags named in the tuple.
parser.parse() 鎺ユ敹涓涓寘鍚簡闇瑕佸垎鏋愬潡鏍囩鍚嶇殑鍏冪粍浣滀负鍙傛暟. 瀹冭繑鍥炰竴涓 django.template.NodeList 瀹炰緥, 瀹冩槸涓涓寘鍚簡鎵鏈 Node 瀵硅薄鐨勫垪琛,杩欎簺瀵硅薄浠h〃浜嗗垎鏋愬櫒鍦ㄩ亣鍒板厓缁勪腑浠讳竴鏍囩鍚嶄箣 鍓 鐨勫唴瀹.
So in the preceding example, nodelist is a list of all nodes between {% comment %} and {% endcomment %} , not counting {% comment %} and {% endcomment %} themselves.
鍥犳鍦ㄥ墠闈㈢殑渚嬪瓙涓紝 nodelist 鏄湪 {% comment %} 鍜 {% endcomment %} 涔嬮棿鎵鏈夎妭鐐圭殑鍒楄〃锛屼笉鍖呮嫭 {% comment %} 鍜 {% endcomment %} 鑷韩銆
After parser.parse() is called, the parser hasnt yet consumed the {% endcomment %} tag, so the code needs to explicitly call parser.delete_first_token() to prevent that tag from being processed twice.
鍦 parser.parse() 琚皟鐢ㄤ箣鍚庯紝鍒嗘瀽鍣ㄨ繕娌℃湁娓呴櫎 {% endcomment %} 鏍囩锛屽洜姝や唬鐮侀渶瑕佹樉寮忓湴璋冪敤 parser.delete_first_token() 鏉ラ槻姝㈣鏍囩琚鐞嗕袱娆°
Then CommentNode.render() simply returns an empty string. Anything between {% comment %} and {% endcomment %} is ignored.
涔嬪悗 CommentNode.render() 鍙槸绠鍗曞湴杩斿洖涓涓┖瀛楃涓层傚湪 {% comment %} 鍜 {% endcomment %} 涔嬮棿鐨勬墍鏈夊唴瀹归兘琚拷鐣ャ
In the previous example, do_comment() discarded everything between {% comment %} and {% endcomment %} . Its also possible to do something with the code between block tags instead.
鍦ㄥ墠涓涓緥瀛愪腑锛 do_comment() 鎶涘純浜嗗湪 {% comment %} 鍜 {% endcomment %} 涔嬮棿鐨勬墍鏈夊唴瀹广傚悓鏍凤紝涔熷彲浠ュ鍧楁爣绛句箣闂寸殑浠g爜杩涜澶勭悊銆
For example, heres a custom template tag, {% upper %} , that capitalizes everything between itself and {% endupper %} :
渚嬪锛岃繖涓嚜瀹氫箟妯℃澘鏍囩锛 {% upper %} 锛屽畠鎶婅嚜宸卞拰 {% endupper %} 涔嬮棿鐨勬墍鏈夊唴瀹归兘鍙樻垚澶у啓锛
{% upper %} This will appear in uppercase, {{ your_name }}. {% endupper %}
As in the previous example, well use parser.parse() . This time, we pass the resulting nodelist to Node :
灏卞儚鍓嶉潰鐨勪緥瀛愪竴鏍凤紝鎴戜滑灏嗕娇鐢 parser.parse() 銆傝繖娆★紝鎴戜滑灏嗕骇鐢熺殑 nodelist 浼犻掔粰 Node 锛
@register.tag def do_upper(parser, token): nodelist = parser.parse(('endupper',)) parser.delete_first_token() return UpperNode(nodelist) class UpperNode(template.Node): def __init__(self, nodelist): self.nodelist = nodelist def render(self, context): output = self.nodelist.render(context) return output.upper()
The only new concept here is self.nodelist.render(context) in UpperNode.render() . This simply calls render() on each Node in the node list.
杩欓噷鍞竴鐨勪竴涓柊姒傚康鏄 UpperNode.render() 涓殑 self.nodelist.render(context) 銆傚畠瀵硅妭鐐瑰垪琛ㄤ腑鐨勬瘡涓 Node 绠鍗曠殑璋冪敤 render() 銆
For more examples of complex rendering, see the source code for {% if %} , {% for %} , {% ifequal %} , and {% ifchanged %} . They live in django/template/defaulttags.py .
鏇村鐨勫鏉傛覆鏌撶ず渚嬭鏌ョ湅 django/template/defaulttags.py 涓殑 {% if %} 銆 {% for %} 銆 {% ifequal %} 鍜 {% ifchanged %} 鐨勪唬鐮併
Many template tags take a single argumenta string or a template variable referenceand return a string after doing some processing based solely on the input argument and some external information. For example, the current_time tag we wrote earlier is of this variety. We give it a format string, and it returns the time as a string.
璁稿妯℃澘鏍囩鎺ユ敹鍗曚竴鐨勫瓧绗︿覆鍙傛暟鎴栬呬竴涓ā鏉垮彉閲 寮曠敤,鐒跺悗鐙珛鍦版牴鎹緭鍏ュ彉閲忓拰涓浜涘叾瀹冨閮ㄤ俊鎭繘琛屽鐞嗗苟杩斿洖涓涓瓧绗︿覆. 渚嬪, 鎴戜滑鍏堝墠鍐欑殑 current_time 鏍囩灏辨槸杩欐牱涓涓緥瀛. 鎴戜滑缁欏畠鏍煎紡瀛楃涓, 鐒跺悗瀹冩妸鏃堕棿浣滀负瀛楃涓茶繑鍥.
To ease the creation of these types of tags, Django provides a helper function, simple_tag . This function, which is a method of django.template.Library , takes a function that accepts one argument, wraps it in a render function and the other necessary bits mentioned previously, and registers it with the template system.
涓轰簡绠鍖栬繖绫绘爣绛撅紝Django 鎻愪緵浜嗕竴涓府鍔╁嚱鏁帮細 simple_tag 銆傝繖涓嚱鏁版槸 django.template.Library 鐨勪竴涓柟娉曪紝瀹冩帴鍙椾竴涓彧鏈変竴涓弬鏁扮殑鍑芥暟浣滃弬鏁帮紝鎶婂畠鍖呰鍦 render 鍑芥暟鍜屼箣鍓嶆彁鍙婅繃鐨勫叾浠栫殑蹇呰鍗曚綅涓紝鐒跺悗閫氳繃妯℃澘绯荤粺娉ㄥ唽鏍囩銆
Our earlier current_time function could thus be written like this:
鎴戜滑涔嬪墠鐨勭殑 current_time 鍑芥暟浜庢槸鍙互鍐欐垚杩欐牱锛
def current_time(format_string): return datetime.datetime.now().strftime(format_string) register.simple_tag(current_time)
In Python 2.4, the decorator syntax also works:
鍦≒ython 2.4涓紝涔熷彲浠ヤ娇鐢ㄤ慨楗拌娉曪細
@register.simple_tag def current_time(token): ...
A couple of things to notice about the simple_tag helper function are as follows:
鏈夊叧 simple_tag 杈呭姪鍑芥暟锛岄渶瑕佹敞鎰忎笅闈竴浜涗簨鎯咃細
Only the (single) argument is passed into our function.
浼犻掔粰鎴戜滑鐨勫嚱鏁扮殑鍙湁锛堝崟涓級鍙傛暟銆
Checking for the required number of arguments has already been done by the time our function is called, so we dont need to do that.
鍦ㄦ垜浠殑鍑芥暟琚皟鐢ㄧ殑鏃跺欙紝妫鏌ュ繀闇鍙傛暟涓暟鐨勫伐浣滃凡缁忓畬鎴愪簡锛屾墍浠ユ垜浠笉闇瑕佸啀鍋氳繖涓伐浣溿
The quotes around the argument (if any) have already been stripped away, so we receive a plain string.
鍙傛暟涓よ竟鐨勫紩鍙凤紙濡傛灉鏈夌殑璇濓級宸茬粡琚埅鎺変簡锛屾墍浠ユ垜浠細鎺ユ敹鍒颁竴涓櫘閫氬瓧绗︿覆銆
Another common template tag is the type that displays some data by rendering another template. For example, Djangos admin interface uses custom template tags to display the buttons along the bottom of the add/change form pages. Those buttons always look the same, but the link targets change depending on the object being edited. Theyre a perfect case for using a small template that is filled with details from the current object.
鍙﹀涓绫诲父鐢ㄧ殑妯℃澘鏍囩鏄氳繃娓叉煋 鍏朵粬 妯℃澘鏄剧ず鏁版嵁鐨勩傛瘮濡傝锛孌jango鐨勫悗鍙扮鐞嗙晫闈紝瀹冧娇鐢ㄤ簡鑷畾涔夌殑妯℃澘鏍囩鏉ユ樉绀烘柊澧/缂栬緫琛ㄥ崟椤甸潰涓嬮儴鐨勬寜閽傞偅浜涙寜閽湅璧锋潵鎬绘槸涓鏍风殑锛屼絾鏄摼鎺ュ嵈闅忕潃鎵缂栬緫鐨勫璞$殑涓嶅悓鑰屾敼鍙樸傝繖灏辨槸涓涓娇鐢ㄥ皬妯℃澘寰堝ソ鐨勪緥瀛愶紝杩欎簺灏忔ā鏉垮氨鏄綋鍓嶅璞$殑璇︾粏淇℃伅銆
These sorts of tags are called inclusion tags . Writing inclusion tags is probably best demonstrated by example. Lets write a tag that produces a list of choices for a simple multiple-choice Poll object. Well use the tag like this:
杩欎簺鎺掑簭鏍囩琚О涓 鍖呭惈鏍囩 銆傚浣曞啓鍖呭惈鏍囩鏈濂介氳繃涓句緥鏉ヨ鏄庛傛垜浠潵鍐欎竴涓彲浠ョ敓鎴愪竴涓夐」鍒楄〃鐨勫閫夐」瀵硅薄 Poll 銆傛爣绛捐繖鏍蜂娇鐢細
{% show_results poll %}
The result will be something like this:
缁撴灉灏嗕細鍍忎笅闈㈣繖鏍凤細
<ul> <li>First choice</li> <li>Second choice</li> <li>Third choice</li> </ul>
First, we define the function that takes the argument and produces a dictionary of data for the result. Notice that we need to return only a dictionary, not anything more complex. This will be used as the context for the template fragment:
棣栧厛锛屾垜浠畾涔変竴涓嚱鏁帮紝閫氳繃缁欏畾鐨勫弬鏁扮敓鎴愪竴涓瓧鍏稿舰寮忕殑缁撴灉銆 闇瑕佹敞鎰忕殑鏄紝鎴戜滑鍙渶瑕佽繑鍥炲瓧鍏哥被鍨嬬殑缁撴灉灏辫浜嗭紝瀹冨皢琚敤鍋氭ā鏉跨墖鏂殑context銆 (璇戞敞锛歞ict 鐨 key 浣滀负鍙橀噺鍚嶅湪妯℃澘涓浣跨敤)
def show_books_for_author(author): books = author.book_set.all() return {'books': books}
Next, we create the template used to render the tags output. Following our example, the template is very simple:
鎺ヤ笅鏉ワ紝鎴戜滑鍒涘缓鐢ㄤ簬娓叉煋鏍囩杈撳嚭鐨勬ā鏉裤傚湪鎴戜滑鐨勪緥瀛愪腑锛屾ā鏉垮緢绠鍗曪細
<ul> {% for book in books %} <li> {{ book }} </li> {% endfor %} </ul>
Finally, we create and register the inclusion tag by calling the inclusion_tag() method on a Library object.
鏈鍚庯紝鎴戜滑閫氳繃瀵逛竴涓 Library 瀵硅薄浣跨敤 inclusion_tag() 鏂规硶鏉ュ垱寤哄苟娉ㄥ唽杩欎釜鍖呭惈鏍囩銆
Following our example, if the preceding template is in a file called polls/result_snippet.html , we register the tag like this:
鍦ㄦ垜浠殑渚嬪瓙涓紝濡傛灉鍏堝墠鐨勬ā鏉垮湪 polls/result_snippet.html 鏂囦欢涓紝閭d箞鎴戜滑杩欐牱娉ㄥ唽鏍囩锛
register.inclusion_tag('books/books_for_author.html')(show_books_for_author)
As always, Python 2.4 decorator syntax works as well, so we could have instead written this:
鍜屽線甯镐竴鏍凤紝鎴戜滑涔熷彲浠ヤ娇鐢≒ython 2.4涓殑淇グ璇硶锛屾墍浠ユ垜浠繕鍙互杩欎箞鍐欙細
@register.inclusion_tag('books/books_for_author.html') def show_books_for_author(show_books_for_author): ...
Sometimes, your inclusion tags need access to values from the parent templates context. To solve this, Django provides a takes_context option for inclusion tags. If you specify takes_context in creating a template tag, the tag will have no required arguments, and the underlying Python function will have one argument: the template context as of when the tag was called.
鏈夋椂鍊欙紝浣犵殑鍖呭惈鏍囩闇瑕佽闂埗妯℃澘鐨刢ontext銆備负浜嗚В鍐宠繖涓棶棰橈紝Django鎻愪緵浜嗕竴涓 takes_context 閫夐」銆傚鏋滀綘鍦ㄥ垱寤烘ā鏉挎爣绛炬椂锛屾寚鏄庝簡杩欎釜閫夐」锛岃繖涓爣绛惧氨涓嶉渶瑕佸弬鏁帮紝骞朵笖涓嬮潰鐨凱ython鍑芥暟浼氬甫涓涓弬鏁帮細灏辨槸褰撹繖涓爣绛捐璋冪敤鏃剁殑妯℃澘context銆
For example, say youre writing an inclusion tag that will always be used in a context that contains home_link and home_title variables that point back to the main page. Heres what the Python function would look like:
渚嬪锛屼綘姝e湪鍐欎竴涓寘鍚爣绛撅紝璇ユ爣绛惧寘鍚湁鎸囧悜涓婚〉鐨 home_link 鍜 home_title 鍙橀噺銆侾ython鍑芥暟浼氬儚杩欐牱锛
@register.inclusion_tag('link.html', takes_context=True) def jump_link(context): return { 'link': context['home_link'], 'title': context['home_title'], }
Note
澶囨敞
The first parameter to the function must be called context .
鍑芥暟鐨勭涓涓弬鏁 蹇呴』 鏄 context 銆
The template link.html might contain the following:
妯℃澘 link.html 鍙兘鍖呭惈涓嬮潰鐨勪笢瑗匡細
Jump directly to <a href="{{ link }}">{{ title }}</a>.
Then, anytime you want to use that custom tag, load its library and call it without any arguments, like so:
鐒跺悗鎮ㄦ兂浣跨敤鑷畾涔夋爣绛炬椂锛屽氨鍙互鍔犺浇瀹冪殑搴擄紝鐒跺悗涓嶅甫鍙傛暟鍦拌皟鐢ㄥ畠锛屽氨鍍忚繖鏍凤細
{% jump_link %}
Djangos built-in template loaders (described in the Inside Template Loading section above) will usually cover all your template-loading needs, but its pretty easy to write your own if you need special loading logic. For example, you could load templates from a database, or directly from a Subversion repository using Subversions Python bindings, or (as shown shortly) from a ZIP archive.
Djangos 鍐呯疆鐨勬ā鏉垮姞杞藉櫒锛堝湪鍏堝墠鐨勬ā鏉垮姞杞藉唴骞曠珷鑺傛湁鍙欒堪锛夐氬父浼氭弧瓒充綘鐨勬墍鏈夌殑妯℃澘鍔犺浇闇姹傦紝浣嗘槸濡傛灉浣犳湁鐗规畩鐨勫姞杞介渶姹傜殑璇濓紝缂栧啓鑷繁鐨勬ā鏉垮姞杞藉櫒涔熶細鐩稿綋绠鍗曘傛瘮濡傦細浣犲彲浠ヤ粠鏁版嵁搴撳姞杞芥ā鏉匡紝鎴栬呬娇鐢 Subversions鐨凱ython瀹炵幇鐩存帴浠嶴ubversion搴撳姞杞芥ā鏉匡紝鍐嶆垨鑰咃紙绋嶅悗灞曠ず锛変粠zip鏂囦欢鍔犺浇妯℃澘銆
A template loaderthat is, each entry in the TEMPLATE_LOADERS setting is expected to be a callable with this interface:
涓涓ā鏉垮姞杞藉櫒锛屼篃灏辨槸 TEMPLATE_LOADERS 涓殑姣忎竴椤癸紝閮借鑳借涓嬮潰杩欎釜鎺ュ彛鎵璋冪敤锛
load_template_source(template_name, template_dirs=None)
The template_name argument is the name of the template to load (as passed to loader.get_template() or loader.select_template() ), and template_dirs is an optional list of directories to search instead of TEMPLATE_DIRS .
鍙傛暟 template_name 鏄墍鍔犺浇妯℃澘鐨勫悕绉 (鍜屼紶閫掔粰 loader.get_template() 鎴栬 loader.select_template() 涓鏍), 鑰 template_dirs 鏄竴涓彲閫夌殑鍖呭惈闄ゅ幓 TEMPLATE_DIRS 涔嬪鐨勬悳绱㈢洰褰曞垪琛ㄣ
If a loader is able to successfully load a template, it should return a tuple: (template_source, template_path) . Here, template_source is the template string that will be compiled by the template engine, and template_path is the path the template was loaded from. That path might be shown to the user for debugging purposes, so it should quickly identify where the template was loaded from.
濡傛灉鍔犺浇鍣ㄨ兘澶熸垚鍔熷姞杞戒竴涓ā鏉, 瀹冨簲褰撹繑鍥炰竴涓厓缁勶細 (template_source, template_path) 銆傚湪杩欓噷鐨 template_source 灏辨槸灏嗚妯℃澘寮曟搸缂栬瘧鐨勭殑妯℃澘瀛楃涓诧紝鑰 template_path 鏄鍔犺浇鐨勬ā鏉跨殑璺緞銆傜敱浜庨偅涓矾寰勫彲鑳戒細鍑轰簬璋冭瘯鐩殑鏄剧ず缁欑敤鎴凤紝鍥犳瀹冨簲褰撳緢蹇殑鎸囨槑妯℃澘浠庡摢閲屽姞杞借屾潵銆
If the loader is unable to load a template, it should raise django.template.TemplateDoesNotExist .
濡傛灉鍔犺浇鍣ㄥ姞杞芥ā鏉垮け璐ワ紝閭d箞灏变細瑙﹀彂 django.template.TemplateDoesNotExist 寮傚父銆
Each loader function should also have an is_usable function attribute. This is a Boolean that informs the template engine whether this loader is available in the current Python installation. For example, the eggs loader (which is capable of loading templates from Python eggs) sets is_usable to False if the pkg_resources module isnt installed, because pkg_resources is necessary to read data from eggs.
姣忎釜鍔犺浇鍑芥暟閮藉簲璇ユ湁涓涓悕涓 is_usable 鐨勫嚱鏁板睘鎬с傝繖涓睘鎬ф槸涓涓竷灏斿硷紝鐢ㄤ簬鍛婄煡妯℃澘寮曟搸杩欎釜鍔犺浇鍣ㄦ槸鍚﹀湪褰撳墠瀹夎鐨凱ython涓彲鐢ㄣ備緥濡傦紝濡傛灉 pkg_resources 妯″潡娌℃湁瀹夎鐨勮瘽锛宔ggs鍔犺浇鍣紙瀹冭兘澶熶粠python eggs涓姞杞芥ā鏉匡級灏卞簲璇ユ妸 is_usable 璁句负 False 锛屽洜涓哄繀椤婚氳繃 pkg_resources 鎵嶈兘浠巈ggs涓鍙栨暟鎹
An example should help clarify all of this. Heres a template loader function that can load templates from a ZIP file. It uses a custom setting, TEMPLATE_ZIP_FILES , as a search path instead of TEMPLATE_DIRS , and it expects each item on that path to be a ZIP file containing templates:
涓涓緥瀛愬彲浠ユ竻鏅板湴闃愭槑涓鍒囥傝繖鍎挎槸涓涓ā鏉垮姞杞藉嚱鏁帮紝瀹冨彲浠ヤ粠ZIP鏂囦欢涓姞杞芥ā鏉裤傚畠浣跨敤浜嗚嚜瀹氫箟鐨勮缃 TEMPLATE_ZIP_FILES 鏉ュ彇浠d簡 TEMPLATE_DIRS 鐢ㄤ綔鏌ユ壘璺緞锛屽苟涓斿畠鍋囪鍦ㄦ璺緞涓婄殑姣忎竴涓枃浠堕兘鏄寘鍚ā鏉跨殑ZIP鏂囦欢锛
import zipfile from django.conf import settings from django.template import TemplateDoesNotExist def load_template_source(template_name, template_dirs=None): """Template loader that loads templates from a ZIP file.""" template_zipfiles = getattr(settings, "TEMPLATE_ZIP_FILES", []) # Try each ZIP file in TEMPLATE_ZIP_FILES. for fname in template_zipfiles: try: z = zipfile.ZipFile(fname) source = z.read(template_name) except (IOError, KeyError): continue z.close() # We found a template, so return the source. template_path = "%s:%s" % (fname, template_name) return (source, template_path) # If we reach here, the template couldn't be loaded raise TemplateDoesNotExist(template_name) # This loader is always usable (since zipfile is included with Python) load_template_source.is_usable = True
The only step left if we want to use this loader is to add it to the TEMPLATE_LOADERS setting. If we put this code in a package called mysite.zip_loader , then we add mysite.zip_loader.load_template_source to TEMPLATE_LOADERS .
鎴戜滑瑕佹兂浣跨敤瀹冿紝杩樺樊鏈鍚庝竴姝ワ紝灏辨槸鎶婂畠鍔犲叆鍒 TEMPLATE_LOADERS 銆傚鏋滄垜浠妸杩欓儴鍒嗕唬鐮佹斁鍒颁竴涓彨鍋 mysite.zip_loader 鐨勫寘涓紝鎴戜滑灏遍渶瑕佹妸 mysite.zip_loader.load_template_source 鍔犲叆鍒 TEMPLATE_LOADERS 涓幓銆
Djangos admin interface includes a complete reference of all template tags and filters available for a given site. Its designed to be a tool that Django programmers give to template developers. To see it, go to the admin interface and click the Documentation link at the upper right of the page.
Django绠$悊鐣岄潰鍖呭惈涓涓畬鏁寸殑鍙傝冭祫鏂欙紝閲岄潰鏈夋墍鏈夌殑鍙互鍦ㄧ壒瀹氱綉绔欎笂浣跨敤鐨勬ā鏉挎爣绛惧拰杩囨护鍣ㄣ傚畠璁捐鐨勫垵琛锋槸Django绋嬪簭鍛樻彁渚涚粰妯℃澘寮鍙戜汉鍛樼殑涓涓伐鍏枫備綘鍙互鐐瑰嚮绠$悊椤甸潰鍙充笂瑙掔殑鏂囨。閾炬帴鏉ユ煡鐪嬭繖浜涜祫鏂欍
The reference is divided into four sections: tags, filters, models, and views. The tags and filters sections describe all the built-in tags (in fact, the tag and filter references in Chapter 4 come directly from those pages) as well as any custom tag or filter libraries available.
鍙傝冭鏄庡垎涓4涓儴鍒嗭細鏍囩銆佽繃婊ゅ櫒銆佹ā鍨嬪拰瑙嗗浘銆 鏍囩 鍜 杩囨护鍣 閮ㄥ垎鎻忚堪浜嗘墍鏈夊唴缃殑鏍囩锛堝疄闄呬笂锛岀4绔犱腑鐢ㄥ埌鐨勬爣绛惧拰杩囨护鍣ㄩ兘鐩存帴鏉ユ簮浜庨偅鍑犻〉锛変互鍙婁竴浜涘彲鐢ㄧ殑鑷畾涔夋爣绛惧拰杩囨护鍣ㄥ簱銆
The views page is the most valuable. Each URL in your site has a separate entry here. If the related view includes a docstring, clicking the URL will show you the following:
瑙嗗浘 椤甸潰鏄渶鏈変环鍊肩殑銆傜綉绔欎腑鐨勬瘡涓猆RL閮藉湪杩欏効鏈夌嫭绔嬬殑鍏ュ彛銆傚鏋滅浉鍏崇殑瑙嗗浘鍖呭惈涓涓 鏂囨。瀛楃涓诧紝 鐐瑰嚮URL锛屼綘灏变細鐪嬪埌锛
The name of the view function that generates that view
鐢熸垚鏈鍥剧殑瑙嗗浘鍑芥暟鐨勫悕瀛
A short description of what the view does
瑙嗗浘鍔熻兘鐨勪竴涓畝鐭弿杩
The context, or a list of variables available in the views template
涓婁笅鏂囨垨涓涓鍥炬ā鏉夸腑鍙敤鐨勫彉閲忕殑鍒楄〃
The name of the template or templates that are used for that view
瑙嗗浘浣跨敤鐨勬ā鏉跨殑鍚嶅瓧
For a detailed example of view documentation, read the source code for Djangos generic object_list view, which is in django/views/generic/list_detail.py .
瑕佹兂鏌ョ湅鍏充簬瑙嗗浘鏂囨。鐨勬洿璇︾粏鐨勪緥瀛愶紝璇烽槄璇籇jango鐨勯氱敤 object_list 瑙嗗浘閮ㄥ垎鐨勬簮浠g爜锛屽畠浣嶄簬 django/views/generic/list_detail.py 鏂囦欢涓
Because Django-powered sites usually use database objects, the models pages describe each type of object in the system along with all the fields available on that object.
閫氬父鎯呭喌涓嬶紝鐢盌jango鏋勫缓鐨勭綉绔欓兘浼氫娇鐢ㄦ暟鎹簱瀵硅薄锛 妯″瀷 椤甸潰鎻忚堪浜嗙郴缁熶腑鎵鏈夌被鍨嬬殑瀵硅薄锛屼互鍙婅瀵硅薄瀵瑰簲鐨勬墍鏈夊彲鐢ㄥ瓧娈点
Taken together, the documentation pages should tell you every tag, filter, variable, and object available to you in a given template.
鎬讳箣锛岃繖浜涙枃妗e憡璇変綘鍦ㄦā鏉夸腑鐨勬墍鏈夊彲鐢ㄧ殑鏍囩銆佽繃婊ゅ櫒銆佸彉閲忓拰瀵硅薄銆
Note
澶囨敞
This section is only of interest to people trying to use the template system as an output component in another application. If you are using the template system as part of a Django application, the information presented here doesnt apply to you.
杩欓儴鍒嗗彧閽堝浜庡鍦ㄥ叾浠栧簲鐢ㄤ腑浣跨敤妯$増绯荤粺浣滀负杈撳嚭缁勪欢鎰熷叴瓒g殑浜恒傚鏋滀綘鏄湪Django搴旂敤涓娇鐢ㄦā鐗堢郴缁燂紝璇风暐杩囨閮ㄥ垎銆
Normally, Django will load all the configuration information it needs from its own default configuration file, combined with the settings in the module given in the DJANGO_SETTINGS_MODULE environment variable. But if youre using the template system independently of the rest of Django, the environment variable approach isnt very convenient, because you probably want to configure the template system in line with the rest of your application rather than dealing with settings files and pointing to them via environment variables.
閫氬父锛孌jango浼氫粠瀹冪殑榛樿閰嶇疆鏂囦欢鍜岀敱 DJANGO_SETTINGS_MODULE 鐜鍙橀噺鎵鎸囧畾鐨勬ā鍧椾腑鍔犺浇瀹冮渶瑕佺殑鎵鏈夐厤缃俊鎭備絾鏄綋浣犳兂鍦ㄩ潪Django搴旂敤涓娇鐢ㄦā鐗堢郴缁熺殑鏃跺欙紝閲囩敤鐜鍙橀噺骞朵笉鏄緢濂界殑鏂规硶銆傛瘮璧蜂负妯$増绯荤粺鍗曠嫭閲囩敤閰嶇疆鏂囦欢骞剁敤鐜鍙橀噺鏉ユ寚鍚戝畠锛屼綘鍙兘鏇村笇鏈涜兘澶熷湪浣犵殑搴旂敤涓噰鐢ㄤ竴鑷寸殑閰嶇疆鏂规硶鏉ラ厤缃ā鐗堢郴缁熷拰鍏朵粬閮ㄥ垎
To solve this problem, you need to use the manual configuration option described fully Appendix E. In a nutshell, you need to import the appropriate pieces of the template system and then, before you call any of the template functions, call django.conf.settings.configure() with any settings you wish to specify.
涓轰簡瑙e喅杩欎釜闂锛屼綘闇瑕佷娇鐢ㄩ檮褰旹涓墍鎻忚堪鐨勬墜鍔ㄩ厤缃夐」銆傜畝鍗曟潵璇达紝浣犻渶瑕佸紩鍏ュ悎閫傜殑妯℃澘绯荤粺锛屽苟涓斿湪璋冪敤浠讳綍妯℃澘鍑芥暟 涔嬪墠 璋冪敤 django.conf.settings.configure() 鏉ユ寚瀹氫换浣曚綘鎯宠鐨勮缃
You might want to consider setting at least TEMPLATE_DIRS (if you are going to use template loaders), DEFAULT_CHARSET (although the default of utf-8 is probably fine), and TEMPLATE_DEBUG . All available settings are described in Appendix E, and any setting starting with TEMPLATE_ is of obvious interest.
浣犲彲鑳戒細鑰冭檻鑷冲皯瑕佽缃 TEMPLATE_DIRS 锛堝鏋滀綘鎵撶畻浣跨敤妯℃澘鍔犺浇鍣級锛 DEFAULT_CHARSET 锛堝敖绠¢粯璁ょ殑 utf-8 缂栫爜鐩稿綋濂界敤锛夛紝浠ュ強 TEMPLATE_DEBUG 銆傛墍鏈夊彲鐢ㄧ殑閫夐」閮藉湪闄勫綍E涓缁嗘弿杩帮紝鎵鏈変互 TEMPLATE_ 寮澶寸殑閫夐」閮藉彲鑳戒娇浣犳劅鍏磋叮鐨勩
So far this book has assumed that the content youre displaying is HTML. This isnt a bad assumption for a book about Web development, but at times youll want to use Django to output other data formats.
杩勪粖涓烘锛屾湰涔﹀亣瀹氭偍鎯冲睍绀虹殑鍐呭涓篐TML銆傚浜庝竴涓湁鍏砏eb寮鍙戠殑涔︽潵璇达紝杩欎笉鏄竴涓 涓嶅ソ鐨勫亣璁撅紝浣嗘湁鏃朵綘鎯崇敤Django杈撳嚭鍏朵粬鏁版嵁鏍煎紡銆
The next chapter describes how you can use Django to produce images, PDFs, and any other data format you can imagine.
涓嬩竴绔犲皢璁茶В濡備綍浣跨敤Django鐢熸垚鍥惧儚銆丳DF銆佽繕鏈変綘鍙互鎯冲埌鐨勫叾浠栨暟鎹牸寮忋
鍏充簬鏈瘎娉ㄧ郴缁
鏈珯浣跨敤涓婁笅鏂囧叧鑱旂殑璇勬敞绯荤粺鏉ユ敹闆嗗弽棣堜俊鎭備笉鍚屼簬涓鑸鏁寸珷鍋氳瘎娉ㄧ殑鍋氭硶锛 鎴戜滑鍏佽浣犲姣忎竴涓嫭绔嬬殑鈥滄枃鏈潡鈥濆仛璇勬敞銆備竴涓滄枃鏈潡鈥濈湅璧锋潵鏄繖鏍风殑锛
涓涓滄枃鏈潡鈥濇槸涓涓钀斤紝涓涓垪琛ㄩ」锛屼竴娈典唬鐮侊紝鎴栬呭叾浠栦竴灏忔鍐呭銆 浣犻変腑瀹冧細楂樹寒搴︽樉绀:
瑕佸鏂囨湰鍧楀仛璇勬敞锛屼綘鍙渶瑕佺偣鍑诲畠鏃佽竟鐨勬爣璇嗗潡:
鎴戜滑浼氫粩缁嗛槄璇绘瘡涓瘎璁猴紝濡傛灉鍙兘鐨勮瘽鎴戜滑涔熶細鎶婅瘎娉ㄨ冭檻鍒版湭鏉ョ殑鐗堟湰涓幓:
濡傛灉浣犳効鎰忎綘鐨勮瘎娉ㄨ閲囩敤锛岃纭繚鐣欎笅浣犵殑鍏ㄥ悕 (娉ㄦ剰涓嶆槸鏄电О鎴栫畝绉帮級
Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful
YAHOO.ext
library. Thanks also to Yahoo for YUI itself.