Search code examples
pythondjangodjango-templates

Why isn't a nested-dot lookup evaluating in this Django template?


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 (test.0.ndx) returns nothing, but I expect [4, 5, 6]. Note that ndx evaluates to bar.


Solution

  • 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.