用户工具

站点工具


template:jinja-django

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

template:jinja-django [2011/02/11 04:24] (当前版本)
行 1: 行 1:
 +====== 混合使用django模板和jinja模板 ======
  
 +Django一直广受争论的地方就是它的模板功能,其中印象最深的一次是在python-cn上最初由一个与模板不太相关的主题引起的大讨论。
 +
 +见 《听一个turbogears的家伙讲django该向zope学­什么》
 +
 +http://​groups.google.com/​group/​python-cn/​browse_thread/​thread/​c32a8ba1b2e1f5f3
 +
 +争论的焦点主要集中在django的模板功能太弱,扩展的filter,tag难写,是否应该在模板中直接允许执行更多的python代码等。
 +
 +===== Django本身的观点 ​ =====
 +
 +Django模板本身从设计之初就更多的考虑到模板的使用者是 页面设计人员 而非 后台程序员 , 所以设计的尽可能简单,从设计上去限制模板的不规范使用,以便更好的区分工作责任,这点从它的模板部分文档从最初就直接分为两份,分别适合以上两种人员进行细读就可以看出来。
 +
 +===== 后台程序员的观点 ​ =====
 +
 +或许Django的使用者以后台程序员为主,所以很多人都强烈要求改进Django的模板,以便可以更方便的直接调用python代码。但是Django的开发团队对此要求始终无动于衷。于是出现了其他的一些 模板引擎,比如jinja 。
 +
 +===== jinja2 =====
 +
 +jinja的使用上和Django及其相似,都主要通过%% {{ }} %% 和{% %} 这两个东西里进行模板渲染,但是jinja允许你在模板中更多的使用python形式的代码。这在某些时候是的确是非常方便的。同时jinja自己宣称它比django的模板引擎拥有更好的性能。
 +
 +===== 在Django中使用jinja2 ​ =====
 +
 +
 +目前介绍在Django中使用jinja2的方式主要都是通过各种方式替换Django原有模板。最近看 《Pro Django》,其中第6章介绍模板的时候,提出了另一种在django中使用jinja2的方式。
 +
 +这种方式是通过自定义一个需要有相应end的tag,然后在render的时候,将此对tag中的原始内容直接传给jinja2进行处理,因此此对 tag之间的内容就可以使用jinja的语法,而其他部分仍需符合django的模板语法。这对于只需要少量使用jinja2的情况下,相对于完整替换,这种方式更省时省力,也显得更干净利落。下面就是混合使用django和jinja2的代码示例:
 +
 +==== view部分Python代码 ====
 +
 +<code python>
 +# Create your views here.  ​
 +from django.http import HttpResponse  ​
 +from django.shortcuts import render_to_response  ​
 +from django.template import RequestContext  ​
 +  ​
 +def test(request):  ​
 +    user = '​myuser'  ​
 +    seq = [1,​2,​3]  ​
 +    def sum(a,​b):  ​
 +         ​return a + b  ​
 +     ​return render_to_response('​jinja_test.html', ​  
 +                               ​{'​user':​user,​ '​seq':​seq,​ '​sum':​sum,​}, ​  
 +                               ​context_instance=RequestContext(request))  ​
 +</​code>​
 +==== 模板代码 ====
 +<code html>
 +<​!DOCTYPE HTML PUBLIC "​-//​W3C//​DTD HTML 4.01//​EN"​ "​http://​www.w3.org/​TR/​html4/​strict.dtd">  ​
 +<​html>  ​
 +    <​head>  ​
 +        <meta http-equiv="​Content-Type"​ content="​text/​html;​ charset=utf-8"​ />  ​
 +        <​title>​jinja_tag test</​title>  ​
 +    </​head>  ​
 +    <​body>  ​
 +        {%load jinja_tag%}  ​
 +        {%jinja%}  ​
 +            {% for item in seq - %}  ​
 +                {{ item }}  ​
 +            {% - endfor %}  ​
 +            <br />  ​
 +            {{ 1+1*4 }}  ​
 +            <br />  ​
 +            {{ sum(1, seq[2]) }}  ​
 +            <br />  ​
 +        {%endjinja%}  ​
 +    </​body>  ​
 +</​html>  ​
 +</​code>​
 +
 +从上面可以看到,包围在{%jinja%}和{%endjinja%}之间的代码使用jinja语法,而其他部分仍然限制在django的模板语法。
 +
 +===== 实现 =====
 +
 +其实实现这样一个jinja tag是非常容易的,具体原理可以看《pro django》原书,这里只贴下经过修改,修复了一些小bug的代码:
 +
 +<code python>
 + ​import jinja2  ​
 + from django import template  ​
 +   
 + ​register = template.Library()  ​
 +   
 + def string_from_token(token):  ​
 +     """ ​
 +     ​Converts a lexer token back into a string for use with Jinja. ​
 +     """  ​
 +     if token.token_type == template.TOKEN_TEXT:  ​
 +         ​return token.contents  ​
 +     elif token.token_type == template.TOKEN_VAR:  ​
 +         ​return '%s %s %s' % (  ​
 +             ​template.VARIABLE_TAG_START,  ​
 +             ​token.contents,  ​
 +             ​template.VARIABLE_TAG_END,  ​
 +         ​)  ​
 +     elif token.token_type == template.TOKEN_BLOCK:  ​
 +         ​return '​%s%s%s'​ % (  ​
 +             ​template.BLOCK_TAG_START,  ​
 +             ​token.contents,  ​
 +             ​template.BLOCK_TAG_END,  ​
 +         ​)  ​
 +     elif token.token_type == template.TOKEN_COMMENT:  ​
 +         ​return u''​ # Django doesn'​t store the content of comments  ​
 +  ​
 + ​@register.tag  ​
 + def jinja(parser,​ token):  ​
 +     """ ​
 +     ​Define a block that gets rendered by Jinja, rather than Django'​s templates. ​
 +     """  ​
 +     bits = token.contents.split()  ​
 +     if len(bits) != 1:  ​
 +         raise template.TemplateSyntaxError,​ "'​%s'​ tag doesn'​t take any arguments."​ % bits[0]  ​
 +   
 +     # Manually collect tokens for the tag's content, so Django'​s template  ​
 +     # parser doesn'​t try to make sense of it.  ​
 +     ​contents = []  ​
 +     while 1:  ​
 +         ​try:  ​
 +             token = parser.next_token()  ​
 +         ​except IndexError:  ​
 +             # Reached the end of the template without finding the end tag  ​
 +             raise template.TemplateSyntaxError("'​endjinja'​ tag is required."​)  ​
 +         if token.token_type == template.TOKEN_BLOCK and token.contents == '​endjinja':  ​
 +             ​break  ​
 +         ​contents.append(string_from_token(token))  ​
 +     ​contents = ''​.join(contents)  ​
 +     ​return JinjaNode(contents)  ​
 +   
 + class JinjaNode(template.Node):  ​
 +     def __init__(self,​ contents):  ​
 +         ​self.template = jinja2.Template(contents)  ​
 +   
 +     def render(self,​ context):  ​
 +         # Jinja can't use Django'​s Context objects, so we have to  ​
 +         # flatten it out to a single dictionary before using it.  ​
 +         ​jinja_context = {}  ​
 +         for layer in context:  ​
 +             for key, value in layer.items():  ​
 +                 if key not in jinja_context:  ​
 +                     ​jinja_context[key] = value  ​
 +         ​return self.template.render(jinja_context)  ​
 +</​code>​
 +===== 更好的集成 =====
 +
 +上面的HTML模板代码中,每次需要使用该 tag 的时候,都要经过 {%load jinja_tag%} 这个步骤,显得很麻烦。
 +
 +其实可以将该 tag 添加到和django同样的builtin中,那样就可以像使用内置tag一样使用该tag了。
 +
 +只需要 在 某个app目录下的 __init__.py 文件中添加以下代码就可以实现:
 +
 +<code python>
 +from django.template import add_to_builtins  ​
 +# Uncomment the next line to enable the jinja_tag as if defaulttags  ​
 +add_to_builtins('​jinja_tag.templatetags.jinja_tag'​)  ​
 +</​code>​
 +
 +===== 源代码及使用参考 =====
 +  * http://​code.google.com/​p/​django-demo-apps/​source/​browse/#​svn/​trunk/​demo/​jinja_tag
 +
 + 
template/jinja-django.txt · 最后更改: 2011/02/11 04:24 (外部编辑)