Django template how to look up a dictionary value with a variable

mydict = {"key1":"value1", "key2":"value2"}

The regular way to lookup a dictionary value in a Django template is {{ mydict.key1 }}, {{ mydict.key2 }}. What if the key is a loop variable? ie:

{% for item in list %} # where item has an attribute NAME
  {{ mydict.item.NAME }} # I want to look up mydict[item.NAME]
{% endfor %}

mydict.item.NAME fails. How to fix this?


Solution 1:

Write a custom template filter:

from django.template.defaulttags import register
...
@register.filter
def get_item(dictionary, key):
    return dictionary.get(key)

(I use .get so that if the key is absent, it returns none. If you do dictionary[key] it will raise a KeyError then.)

usage:

{{ mydict|get_item:item.NAME }}

Solution 2:

Fetch both the key and the value from the dictionary in the loop:

{% for key, value in mydict.items %}
    {{ value }}
{% endfor %}

I find this easier to read and it avoids the need for special coding. I usually need the key and the value inside the loop anyway.

Solution 3:

You can't by default. The dot is the separator / trigger for attribute lookup / key lookup / slice.

Dots have a special meaning in template rendering. A dot in a variable name signifies a lookup. Specifically, when the template system encounters a dot in a variable name, it tries the following lookups, in this order:

  • Dictionary lookup. Example: foo["bar"]
  • Attribute lookup. Example: foo.bar
  • List-index lookup. Example: foo[bar]

But you can make a filter which lets you pass in an argument:

https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-filters

@register.filter(name='lookup')
def lookup(value, arg):
    return value[arg]

{{ mydict|lookup:item.name }}

Solution 4:

For me creating a python file named template_filters.py in my App with below content did the job

# coding=utf-8
from django.template.base import Library

register = Library()


@register.filter
def get_item(dictionary, key):
    return dictionary.get(key)

usage is like what culebrón said :

{{ mydict|get_item:item.NAME }}

Solution 5:

I had a similar situation. However I used a different solution.

In my model I create a property that does the dictionary lookup. In the template I then use the property.

In my model: -

@property
def state_(self):
    """ Return the text of the state rather than an integer """
    return self.STATE[self.state]

In my template: -

The state is: {{ item.state_ }}