在Python中使用Mako模版库的简单教程
Mako是一个高性能的Python模板库,它的语法和API借鉴了很多其他的模板库,如Django、Jinja2等等。
基本用法
创建模板并渲染它的最基本的方法是使用Template类:
frommako.templateimportTemplate t=Template('helloworld!') printt.render()
传给Template的文本参数被编译为一个Python模块。模块包含一个render_body()函数,它产生模板的输出。调用render()方法时,Mako建立了一个模板的运行环境,并调用render_body()函数,把输出保存到缓冲,返回它的字符串内容。
render_body()函数中可以访问一个变量集。可以向render()方法发送额外的关键词参数来指定这些变量:
frommako.templateimportTemplate t=Template('hello,${name}!') printt.render(name='yeolar')
render()方法使Mako创建一个Context对象,它存储模板可以访问的所有变量和一个用来保存输出的缓冲。也可以自己创建Context,用render_context()方法使模板用它来渲染:
frommako.templateimportTemplate frommako.runtimeimportContext fromStringIOimportStringIO t=Template('hello,${name}!') buf=StringIO() c=Context(buf,name='yeolar') t.render_context(c) printbuf.getValue()
使用文件模板
Template也可以从文件加载模板,使用filename参数:
frommako.templateimportTemplate t=Template(filename='/docs/tpl.txt') printt.render()
为了提高性能,从文件加载的Template还可以在文件系统中将生成的模块缓存为一般的Python模块文件(.py文件),这通过添加module_directory参数实现:
frommako.templateimportTemplate t=Template(filename='/docs/tpl.txt',module_directory='/tmp/mako_modules') printt.render()
上面的代码渲染后,会创建一个/tmp/mako_modules/docs/tpl.txt.py文件,其中包含模块的源代码。下次同样参数的Template创建时,自动重用这个模块文件。
使用TemplateLookup
到现在的例子都是有关单个Template对象的用法。如果模板中的代码要定位其他模板资源,需要某种使用URI来找到它们的方法。这种需求是由TemplateLookup类来达到的。这个类通过传入一个模板查找目录的列表来构造,然后作为关键词参数传给Template对象:
frommako.templateimportTemplate frommako.lookupimportTemplateLookup lookup=TemplateLookup(directories=['/docs']) t=Template('<%includefile="header.txt"/>helloword!',lookup=lookup)
上面创建的模板中包含文件header.txt。为了查找header.txt,传了一个TemplateLookup对象给它。
通常,应用会以文本文件形式在文件系统上存储大部分或全部的模板。一个真正的应用会直接从TemplateLookup取得它的模板,使用get_template()方法,它接受需要的模板的URI作为参数:
frommako.templateimportTemplate frommako.lookupimportTemplateLookup lookup=TemplateLookup(directories=['/docs'],module_directory='/tmp/mako_modules') defserve_template(t_name,**kwargs): t=lookup.get_template(t_name) printt.render(**kwargs)
上面的例子中我们创建了一个TemplateLookup,它从/docs目录中查找模板,并把所有的模块文件存储到/tmp/mako_modules目录中。通过将传入的URI附加到每个查找目录来定位模板,如传递/etc/beans/info.txt,将查找文件/docs/etc/beans/info.txt,如果没找到将抛出TopLevelNotFound异常。
当定位到模板的时候,传给get_template()调用的URI也会作为Template的uri属性。Template使用这个URI来得到模块文件的名字,因此上面的例子中对/etc/beans/info.txt会创建模块文件/tmp/mako_modules/etc/beans/info.txt.py。
设置收集的大小
TemplateLookup还满足将内存中缓存的模板总数设为一个固定的值。默认情况TemplateLookup大小是不限的。可以用collection_size参数指定一个固定值:
lookup=TemplateLookup(directories=['/docs'],module_directory='/tmp/mako_modules',collection_size=500)
上面的lookup将模板加载到内存中的上限是500个。之后,它将使用LRU策略来清理替换模板。
设置文件系统检查
TemplateLookup的另一个重要标志是filesystem_checks。默认为True,每次get_template()方法返回一个模板,会比较原始模板文件的修改时间和模板的最近加载时间,如果文件更新,就重新加载和编译模板。在生产系统中,将filesystem_checks设为False能获得一些性能的提升。
使用Unicode和编码
Template和TemplateLookup可以设置output_encoding和encoding_errors参数来将输出编码为Python支持的编码格式:
frommako.templateimportTemplate frommako.lookupimportTemplateLookup lookup=TemplateLookup(directories=['/docs'],output_encoding='utf-8',encoding_errors='replace') t=lookup.get_template('foo.txt') printt.render()
使用Python3时,如果设置了output_encoding,render()方法将返回一个bytes对象,否则返回string。
render_unicode()方法返回模板输出为Pythonunicode对象,Python3为string:
printt.render_unicode()
上面的方法没有输出编码的参数,可以自行编码:
printt.render_unicode().encode('utf-8','replace')
注意Mako中模板的底层输出流是PythonUnicode对象。
处理异常
模板异常可能发生在两个地方。一个是当你查找、解析和编译模板的时候,一个是运行模板的时候。模板运行中发生的异常会正常在产生问题的Python代码处抛出。Mako有自己的一组异常类,它们主要用于模板构造的查找和编译阶段。Mako提供了一些库例程用来对异常栈提供Mako的信息,并将异常输出为文本或HTML格式。Python文件名、行号和代码片段会被转换为Mako模板文件名、行号和代码片段。Mako模板模块的行会被转换为原始的模板文件对应行。
text_error_template()和html_error_template()函数用于格式化异常跟踪。它们使用sys.exc_info()来得到最近抛出的异常。这些处理器的用法像下面这样:
frommakoimportexceptions try: t=lookup.get_template(uri) printt.render() except: printexceptions.text_error_template().render() 或者渲染为HTML: frommakoimportexceptions try: t=lookup.get_template(uri) printt.render() except: printexceptions.html_error_template().render()
html_error_template()模板接受两个选项:指定full=False只渲染HTML的一节,指定css=False关闭默认的样式表。如:
printexceptions.html_error_template().render(full=False)
HTML渲染函数也可以用format_exceptions标志加到Template中。这种情况下,模板在渲染阶段的任何异常在输出中的结果都会替换为html_error_template()的输出:
t=Template(filename='/foo/bar',format_exceptions=True) printt.render()
注意上面模板的编译阶段发生在构造Template时,没有定义输出流。因此查找、解析、编译阶段发生的异常正常情况下不会被处理,而是传播下去。渲染前的追溯不包括Mako形式的行,这意味着渲染前和渲染中发生的异常会用不同的方式处理,因此try/except可能更常用。
错误模板函数使用的底层对象是RichTraceback对象。这个对象也可以直接用来提供自定义的错误视图。下面是一个用法的样例:
frommako.exceptionsimportRichTraceback try: t=lookup.get_template(uri) printt.render() except: traceback=RichTraceback() for(filename,lineno,function,line)intraceback.traceback: print'File%s,line%s,in%s'%(filename,lineno,function) printline,'\n' print'%s:%s'%(str(traceback.error.__class__.__name__),traceback.error)
集成Mako
在Django中集成Mako
通过Django的中间件可以集成Mako。首先需要安装django-mako模块。
在Django项目的settings.py文件中,修改MIDDLEWARE_CLASSES,添加djangomako.middleware.MakoMiddleware。使用render_to_response()函数即可使用:
fromdjangomako.shortcutsimportrender_to_response defhello_view(request): returnrender_to_response('hello.txt',{'name':'yeolar'})
在Tornado中集成Mako
在Tornado中可以直接使用Mako,下面是一个使用示例:
importtornado.web importmako.lookup importmako.template LOOK_UP=mako.lookup.TemplateLookup( directories=[TEMPLATE_PATH],module_directory='/tmp/mako', output_encoding='utf-8',encoding_errors='replace') classBaseHandler(tornado.web.RequestHandler): definitialize(self,lookup=LOOK_UP): '''Settemplatelookupobject,DefalutisLOOK_UP''' self._lookup=lookup defrender_string(self,filename,**kwargs): '''Overriderender_stringtousemakotemplate. Liketornadorender_stringmethod,thismethod alsopassrequesthandlerenvironmenttotemplateengine. ''' try: template=self._lookup.get_template(filename) env_kwargs=dict( handler=self, request=self.request, current_user=self.current_user, locale=self.locale, _=self.locale.translate, static_url=self.static_url, xsrf_form_html=self.xsrf_form_html, reverse_url=self.application.reverse_url, ) env_kwargs.update(kwargs) returntemplate.render(**env_kwargs) except: #exceptionhandler pass defrender(self,filename,**kwargs): self.finish(self.render_string(filename,**kwargs))