How to include a template containing variables in another template?

3,127 views
Skip to first unread message

Peteris Krumins

unread,
Jan 11, 2010, 3:32:47 AM1/11/10
to Mako Templates for Python
Hey guys,

I am using Mako for my current project and here is a problem I am
facing.

Suppose I have a file "template.html." I render this template from
Python and pass it an object "info."

Now suppose I want to factor some part of HTML in "template.html" out
to another file. Let's call this file "info.html." This file uses the
"info" variable.

I tried using the <%include file="info.html"/> in "template.html" but
I got an exception that variable "info" was not defined in
"info.html." Next I tried using the "args" argument to the <%include>
directive but I was unable to find a way to pass the object "info" via
"args" argument as it only appears to be accepting strings (note: Mako
documentation does not tell much about "args" - it's something to
improve in documentation)

Finally, I found that I can do it with namespaces, but using them does
not seem natural to me.

With namespaces I managed to wrap the contents of "info.html" in a <
%def name="display_info(info)">, and then in "template.html" I would
do the following:

<%namespace import="display_info" file="info.html"/>

And then call the newly imported "display_info" info function with
"info" object.

This does not seem natural to me, so much extra work just to get the
contents of "info.html" into my "template.html".

I wonder if there really is no way to do it with plain <%include>?


Thanks!

Peteris Krumins
www.catonmat.net

Michael Bayer

unread,
Jan 11, 2010, 10:21:27 AM1/11/10
to mako-d...@googlegroups.com
Peteris Krumins wrote:
> Hey guys,
>
> I am using Mako for my current project and here is a problem I am
> facing.
>
> Suppose I have a file "template.html." I render this template from
> Python and pass it an object "info."
>
> Now suppose I want to factor some part of HTML in "template.html" out
> to another file. Let's call this file "info.html." This file uses the
> "info" variable.
>
> I tried using the <%include file="info.html"/> in "template.html" but
> I got an exception that variable "info" was not defined in
> "info.html." Next I tried using the "args" argument to the <%include>
> directive but I was unable to find a way to pass the object "info" via
> "args" argument as it only appears to be accepting strings (note: Mako
> documentation does not tell much about "args" - it's something to
> improve in documentation)

the syntax of include with args is:

<%include file="incl.html" args="x=some_expr, y=some_expr, ..."/>

the contents of "args" are evaluated in python and can be any series of
"x=y" expressions. You must put a <%page> tag in the receiving template
declaring the arguments to be received.

e.g.:

base.html:
<%
import datetime
today = datetime.datetime.now()
%>
this is base.

<%include file="incl.html" args="today=today"/>

incl.html:

<%page args="today"/>

today is: ${today}


the docs have this to say, which I'm assuming you saw - I suppose it is
expected that the contents of "args" below would be recognized as a series
of Python **kwargs:

"Include also accepts arguments which are available as <%page> arguments
in the receiving template:

<%include file="toolbar.html" args="current_section='members',
username='ed'"/>"


Peteris Krumins

unread,
Jan 11, 2010, 7:45:42 PM1/11/10
to Mako Templates for Python

Hi Michael,

Thanks for explaining these points. I had missed that paragraph on <
%page args=.../>, I also was not aware that the contents of "args" are
evaluated in Python.

I played a bit more with <%include> and found an interesting behavior,
could you plase take a look:

Suppose this is the contents of "base.html":

------------[base.html]--------------
<%include file="incl.html"/>
------------[/base.html]--------------

And this is contents of "incl.html":

------------[incl.html]--------------
Hello, ${foo.name}. You are ${foo.age} old.
------------[/incl.html]--------------

And I render the template "base.html" via the following Python code:

------------[Python code]--------------
from mako.template import Template
from mako.lookup import TemplateLookup

class Foo(object):
def __init__(self):
self.name = "member"
self.age = 25

foo = Foo()

lookup = TemplateLookup(directories=['.'])
print Template(filename="base.html", lookup=lookup).render(foo=foo)
------------[/Python code]--------------

In this case variable 'foo' somehow got passed to the included
"incl.html" and the output is "Hello, member. You are 25 years old."

But now take a look the 2nd example, where the same class Foo gets
defined and instantiated inside "base2.html":

------------[base2.html]--------------
<%
class Foo(object):
def __init__(self):
self.name = "member"
self.age = 25

foo = Foo()
%>

<%include file="incl2.html"/>
------------[/base2.html]--------------

The contents of "incl2.html" is the same as that of "incl.html":

------------[incl2.html]--------------
Hello, ${foo.name}. You are ${foo.age} old.
------------[/incl2.html]--------------

Now if "base2.html" gets rendered, Mako throws an exception:
AttributeError: 'Undefined' object has no attribute 'name'.

In this case it seems "incl2.html" did not receive the variable "foo".
(In this case it receives "foo" only if I follow your original advice
on using <%page/> in incl2.html).

Could you please explain why in the first case, where 'foo' got passed
via the render() function, it acted like a super-global, but in this
case it did not?

Thanks!


Peteris Krumins
http://www.catonmat.net

Michael Bayer

unread,
Jan 11, 2010, 8:24:18 PM1/11/10
to mako-d...@googlegroups.com

On Jan 11, 2010, at 7:45 PM, Peteris Krumins wrote:

>
> Now if "base2.html" gets rendered, Mako throws an exception:
> AttributeError: 'Undefined' object has no attribute 'name'.
>
> In this case it seems "incl2.html" did not receive the variable "foo".
> (In this case it receives "foo" only if I follow your original advice
> on using <%page/> in incl2.html).
>
> Could you please explain why in the first case, where 'foo' got passed
> via the render() function, it acted like a super-global, but in this
> case it did not?

your template compiles into a Python function called body(). The render() call puts all of its kwargs into the context, where they are globally accessible by all templates - when templates are compiled, Mako analyzes the variable names referenced in the body code without declaration (i.e. 'print b' as opposed to 'b=12'), and automatically declares these be pulled from the context when the body() function runs (i.e. b = context.get('b')). However variables you declare in a <% %> of your template are local to the body() def and are not accessible outside.

To achieve instant understanding into the whole thing, run:

print Template(filename="base.html", lookup=lookup).code


Reply all
Reply to author
Forward
0 new messages