What is the correct way to use another templating system from within
web.py. My first attempt results in correctly generated mako output
wrapped in <pre> tags.
How should I be calling the mako template?
--- code.py ---
import web
urls = (
'/(.*)', 'hello'
)
from mako.lookup import TemplateLookup
mylookup = TemplateLookup(directories=['templates/'],
module_directory='mako_modules')
def serve_template(templatename, **kwargs):
mytemplate = mylookup.get_template(templatename)
print mytemplate.render(**kwargs)
class hello:
def GET(self, name):
i = web.input(times=1)
if not name:
name = 'world'
serve_template('hello.html', name=name, times=int(i.times))
if __name__ == "__main__":
web.run(urls, globals())
---
--- templates/hello.html ---
<ol>
% for n in range(times):
<li> ${n} ${name} </li>
% endfor
</ol>
----
Thanks,
Ryan
--
Ryan Tracey
Citizen: The World
I think templator approach is the best.
from mako.lookup import TemplateLookup
class mako_render:
def __init__(path):
self._lookup = TemplateLookup(directories=[path],
module_directory='mako_modules')
def __getattr__(self, name):
path = name + '.html'
t = self._lookup.get_template(path)
t.__call__ = t.render
return t
render = mako_render('templates/')
class hello:
def GET(self, name):
i = web.input(times=1)
if not name:
name = 'world'
print render.hello(name=name, times=int(i.times))
I have no preferences as yet. Just in the process of converting a
web.py 0.1 app into a web.py 0.2 app and hit some snags with templator
-- probably just my own conditioning that needs to change ;-)
Do you reckon there'll be a performance hit associated with using a
third-party templating system?
> from mako.lookup import TemplateLookup
>
> class mako_render:
> def __init__(path):
> self._lookup = TemplateLookup(directories=[path],
> module_directory='mako_modules')
>
> def __getattr__(self, name):
> path = name + '.html'
> t = self._lookup.get_template(path)
> t.__call__ = t.render
> return t
>
> render = mako_render('templates/')
>
> class hello:
> def GET(self, name):
> i = web.input(times=1)
> if not name:
> name = 'world'
> print render.hello(name=name, times=int(i.times))
Thanks very much Anand, I'll give this a try.
Cheers,
Not at all. Templetor isn't really integrated with the rest of web.py
much; using an external one shouldn't make a difference. Actually I
think Mako is faster than the current version of Templetor.
Thanks Adam. I think I'll try get my head around both. Horses for
courses and all that.
Hi Anand
I tried out the method you suggested but had to make a few changes get
it to work (in my environment?) For instance, despite the "t.__call__
= t.render" in __getattr__ I cannot call render.hello(...). I can, as
expected, call render.hello.__call__(...). I got around this by having
__getattr__ return t.render instead of just t. Also, to get around the
problem of web.py wrapping the template's output in <pre> tags I call
web.header(...) before I call render.hello(...). Here is what I am
doing:
from mako.lookup import TemplateLookup
class mako_render:
def __init__(self, path):
self._lookup = TemplateLookup(directories=[path],
module_directory='mako_modules')
def __getattr__(self, name):
path = name + '.html'
t = self._lookup.get_template(path)
#t.__call__ = t.render
return t.render
render = mako_render('templates/')
class hello:
def GET(self, name):
i = web.input(times=1)
if not name:
name = 'world'
web.header("Content-Type", "text/html; charset=utf-8")
print render.hello(name=name, times=int(i.times))
Can you see any way I could get around having to use the web.header
method? By returning the method (t.render) and not the object (t) am I
necessitating the use of web.header? I dunno, my OO knowledge is very
poor, for instance I was blown away by __gettr__ and had to contrive a
simple case to understand it. Don't laugh:
In [7]: class Foo:
...: def __init__(self, action):
...: self.action = action
...: def __getattr__(self, thing):
...: return thing +' '+ self.action
...:
In [8]: f = Foo('barks')
In [9]: f.dog
Out[9]: 'dog barks'
Thanks again for the suggestion -- I now know a bit more than I did on Friday!
Cheers,
Ryan
I've been using mako recently for its feature set and caching
abilities so wanted to integrate it into the new web.py v0.3. This is
what I came up with based on Anands example. It's fairly well tested
against the latest webpy.dev branch.
Add to the bottom of web/template.py (or refactor to your pleasing):
class render_mako:
"""
A web.py-ish interface to the Mako templating engine.
See http://webpy.org/, http://makotemplates.org/
"""
def __init__(self, path='.', ext='.html', compile_path=None,
_lookup=None):
"""
Create a new mako template renderer.
Example:
render = web.template.render_mako('templates/')
render.something(var='value')
Common usage:
web.render = web.template.render_mako('templates/')
Parameters:
path: The directory in which to look for templates. (default:
'.')
ext: Extension to add to template files. (default: '.html')
compile_path: Compile templates to specified directory.
(default: None = no caching)
"""
import mako.template, mako.lookup
# Enable rendering mako template objects by calling
mako.template.Template.__call__ =
mako.template.Template.render
self.path = path
self.ext = ext
if _lookup is None:
_lookup = mako.lookup.TemplateLookup(
directories=[path],
module_directory=compile_path)
self._lookup = _lookup
def __str__(self):
return repr(self)
def __repr__(self):
return '<web.template.render_mako path=\'%(path)s\' ext=\'%
(ext)s\'>' % self.__dict__
def __getattr__(self, name):
"""
Tries to return a template of the given attribute name, or a
new
render object rooted in the attribute named directory, or
throws
a KeyError.
"""
import os.path
template = os.path.join(
self.path[len(self._lookup.directories[0]):],
name + self.ext)
path = os.path.join(self.path, name)
if self._lookup.has_template(template):
return self._lookup.get_template(template)
elif os.path.isdir(path):
return render_mako(path, ext=self.ext,
_lookup=self._lookup)
else:
raise KeyError("Template or directory '%s' does not
exist." % name)
A little example (not thoroughly tested):
hello.py:
import web
web.render = web.template.render_mako(
path='docs/', compile_path='var/cache/templates')
urls = (
'/(.*)?', 'hello',
)
class queue:
def GET(self, name=None):
web.header('Content-Type', 'text/html')
if name != None:
return web.render.hello.name(locals())
else:
return web.render.hello.noname()
def hello_app():
return web.application(urls, globals())
docs/layout.html:
<html><head><title>Hello Example</title></head><body>${next.body()}</
body></html>
docs/hello/name.html:
<%inherit file="/layout.html"/>
Hi ${name}!
docs/hello/noname.html:
<%inherit file="/layout.html"/>
Hello!
I also use a small start-up script to make reloading work:
start.py:
import web
from hello import hello_app
if __name__ == "__main__":
import sys
middleware = []
app = hello_app()
if '--debug' in sys.argv:
sys.argv.remove('--debug')
middleware.append(web.reloader)
app.internalerror = web.debugerror
app.run(*middleware)
using `./start.py --debug` helps.
TODO:
- Maybe using the render_mako method should automatically include the
Content-Type header?
- I can see this turning into a templating API. :-D
Regards,
Samuel Cochran
On Sep 3, 3:05 am, "Ryan Tracey" <ryan.tra...@gmail.com> wrote:
> On 31/08/07, Ryan Tracey <ryan.tra...@gmail.com> wrote:
>
> Hi Ryan,
>
> I've been using mako recently for its feature set and caching
> abilities so wanted to integrate it into the new web.py v0.3. This is
> what I came up with based on Anands example. It's fairly well tested
> against the latest webpy.dev branch.
>
> Add to the bottom of web/template.py (or refactor to your pleasing):
No need to refactor. 0.3 will support interface to various templating
engines.
It have added a version, supporting cheetah, genshi and mako.
If you have any other favorite templating engine, please let me know,
preferably with a patch to support that.
The interface will look very similar to web.py templating system.
Some examples:
render = web.contrib.template.render_mako(directories=
['templates'])
render = web.contrib.template.render_genshi(['templates/'])