"Eval" in templates?

747 views
Skip to first unread message

Ben Kovitz

unread,
Jul 4, 2008, 8:29:01 PM7/4/08
to Django users
Is there an easy way in Django to do the following?

In blah.html:

<p>{{ buttonDecidedAtRunTime }} blah blah blah</p>

In the Context passed to blah.html at run-time:

buttonDecidedAtRunTime: '{% include "button4.html %}'
thisPage: "/some/path"

Naturally, the included template might be button1.html,
button2.html, etc., depending on run-time decisions.

In button4.html:

<a href="{{ thisPage }}?action=button4action">html for button #4</
a>


The kicker is the {{ thisPage }} inside button4.html. I'm hoping to
pass thisPage in the context sent to blah.html. This would make the
code really simple. Of course, the code you see above will just show
the string literal '{% include "button4.html %}' right in your
browser.

What's needed is something like eval for template tags. I could
imagine:

{% eval %}{% buttonDecidedAtRunTime %}{% endeval %}

or maybe even better would be:

{% eval buttonDecidedAtRunTime %}

Is there a way to do this? If it's not already in Django, does it
look easy to implement? I haven't yet implemented a custom tag, but I
read the chapter in the book, and it doesn't look too hard. On the
other hand, I don't want to spend days mired in it if it's hard.

Or, is there a better way to do what I'm trying to do? Right now, I
see two other options: render button4.html and pass the result as the
value of buttonDecidedAtRunTime (this complicates the Python code a
bit, since thisPage must be passed to the subroutine that decides
which button to render); or use {% if %} tags in blah.html (kind of
ugly, involves difficulties with parameter-passing, and we need to do
this all over the place, though we could stuff the {% if %} logic into
an included template).


Ben Kovitz
http://decisionero.com

alex....@gmail.com

unread,
Jul 4, 2008, 8:51:39 PM7/4/08
to Django users
I don't think it would be difficult to implement, either as a block
tag, or as a regular tag with context.

Ben Kovitz

unread,
Jul 5, 2008, 9:35:08 AM7/5/08
to Django users
Thanks for the encouragement, Alex. This was so easy, it should be a
first lesson in how to write a custom tag. It took about 15 minutes
and worked the first time!


from django import template

register = template.Library()

@register.tag(name="eval")
def do_eval(parser, token):
try:
tagName, variableName = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError("%r requires a single
argument: the name of the variable to evaluate as the body of a
template" % tagName)

return EvalNode(variableName)


class EvalNode(template.Node):

def __init__(self, variableName):
self.variableName = variableName


def render(self, context):
t = template.Template(context[self.variableName])
return t.render(context)




On Jul 4, 5:51 pm, "alex.gay...@gmail.com" <alex.gay...@gmail.com>
wrote:

Ned Batchelder

unread,
Jul 5, 2008, 10:34:11 AM7/5/08
to django...@googlegroups.com
Now that you have an eval tag, maybe you don't need this, but I think Django already had a simpler solution to your problem:

Context:
    buttonDecidedAtRunTime: 'button4.html'
    thisPage: "/some/path"

blah.html:
    <p>{% include buttonDecidedAtRunTime %}</p>

The argument to the include tag doesn't need to be a literal string, it can be a value from the context.  This has the added advantage that the common "{% include " in each of your button choices is factored out and appears only once, in blah.html.

--Ned.
http://nedbatchelder.com

Ben Kovitz

unread,
Jul 6, 2008, 11:36:50 PM7/6/08
to Django users
Uh!! (Hits hand into forehead.) Yep, just including a template named
by a variable would have been much simpler.

However, it's all worked out nicely. Having seen just how easy it is
to write a custom template tag, I replaced the 'eval' tag with a
couple specialized tags, which render from another template (like an
inclusion tag, but with some processing). This enabled something like
local variables for the "subroutine" template: if you say {% mytag
userGadget %}, the Python code for the tag copies the value of
"userGadget" into "gadget" in the Context that the mytag template
sees. And that template still gets to see all the variables in the
top-level template's Context.

I pulled the same trick on three messy things, and now our templates
are ridiculously simple.

My hat is off to the designers of Django's templating system!
Beautiful, simple, efficient.

Ben
http://decisionero.com


On Jul 5, 7:34 am, Ned Batchelder <n...@nedbatchelder.com> wrote:
> Now that you have an eval tag, maybe you don't need this, but I think
> Django already had a simpler solution to your problem:
>
> Context:
> buttonDecidedAtRunTime: 'button4.html'
> thisPage: "/some/path"
>
> blah.html:
> <p>{% include buttonDecidedAtRunTime %}</p>
>
> The argument to the include tag doesn't need to be a literal string, it
> can be a value from the context. This has the added advantage that the
> common "{% include " in each of your button choices is factored out and
> appears only once, in blah.html.
>
> --Ned.http://nedbatchelder.com
Reply all
Reply to author
Forward
0 new messages