views:

89

answers:

4

According to The Django Book, Django's templating system supports nested dot lookups:

Dot lookups can be nested multiple levels deep. For instance, the following example uses {{ person.name.upper }}, which translates into a dictionary lookup (person['name']), then a method call (upper()): '{{ person.name.upper }} is {{ person.age }} years old.'

Are there goblins with this approach not widely covered in the documentation? I am having problems with nested dot lookups -- here's a minimal example:

views.py:

test = [{'foo': [1, 2, 3], 'bar': [4, 5, 6]}, {'baz': [7, 8, 9]}]
ndx = 'bar'
t = loader.get_template('meh.html')
c = Context({'test': test,
             'ndx': ndx,})
return HttpResponse(t.render(c))

meh.html template:

<pre>
   {{ test }}
   {{ test.0 }}
   {{ test.0.ndx }}
</pre>

Resulting HTML:

<pre>
[{'foo': [1, 2, 3], 'bar': [4, 5, 6]}, {'baz': [7, 8, 9]}]
 {'foo': [1, 2, 3], 'bar': [4, 5, 6]}

</pre>

The nested lookup of a dictionary key within a list element returns nothing, when I expect [4, 5, 6].

J.J.

+6  A: 

I think the problem is that you are expecting ndx to be evaluated when that simply never happens. Have you tried this:

{{ test.0.bar }}

I think that will do what you're looking for.

Are there goblins with this approach...?

Sort of, but they aren't the ones you're talking about, and I don't think it's because of nesting, or at least, it doesn't get worse after you get one level deep. What I mean by this is that all lookup parameters are literals. There's no way to change that. So while you might be able to develop custom template tags and pass them literals or variables to evaluate, you're really out of luck if you want to directly access some member of a variable based on the evaluated value of another value. (You could possibly write a template tag for this, but it won't work in all desired situations and is possibly more complicated than it's worth.)

For whatever it's worth, this looks like a pretty intentional facet of the templating language. I invite you to consider how the accessor should know whether {{ foo.bar }} should be read as foo[bar] or foo['bar']. It doesn't seem possible to make a meaningful judgment without complicating the syntax and that's something that django's template design is adamant about avoiding.

David Berger
Thanks for answering the question I *should* have been asking. :-)
J.J.
+1  A: 

David is right: ndx won't be evaluated to get a key, it will be used literally as a key. You can define a new template tag to do what you want, here's a simple one: http://www.djangosnippets.org/snippets/1412/

Ned Batchelder
+1  A: 

To expand on David's answer, Django's template system doesn't allow you to use the value of a context variable as a key. So, in your example

{{ test.0.ndx }}

is actually looking for the key "ndx" in the first item in the test context variable.

If you need this functionality, you need to implement it yourself, as a template filter. This ticket has more information, including the Django devs' reasoning behind the omission of this feature and an example implementation of the template filter you're looking for.

Will McCutchen
+1  A: 

I'd suggest either (and I agree with David's interpretation of your problem):

{{ test.0.bar }} # as david mentioned, or

ndx=test[0]['bar'] # in views
{{ ndx }} # in template
dagoof