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

Question:

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?

Asked By: Stan

||

Answers:

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 }}

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 }}
Answered By: culebrón

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.

Answered By: Paul Whipp

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_ }}
Answered By: sexybear2

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 }}
Answered By: AmiNadimi

env: django 2.1.7

view:

dict_objs[query_obj.id] = {'obj': query_obj, 'tag': str_tag}
return render(request, 'obj.html', {'dict_objs': dict_objs})

template:

{% for obj_id,dict_obj in dict_objs.items %}
<td>{{ dict_obj.obj.obj_name }}</td>
<td style="display:none">{{ obj_id }}</td>
<td>{{ forloop.counter }}</td>
<td>{{ dict_obj.obj.update_timestamp|date:"Y-m-d H:i:s"}}</td>
Answered By: Yi Yang Apollo

Since I can’t comment, let me do this in the form of an answer:
to build on culebrón’s answer or Yuji ‘Tomita’ Tomita’s answer, the dictionary passed into the function is in the form of a string, so perhaps use ast.literal_eval to convert the string to a dictionary first, like in this example.

With this edit, the code should look like this:

# code for custom template tag
@register.filter(name='lookup')
def lookup(value, arg):
    value_dict = ast.literal_eval(value)
    return value_dict.get(arg)
<!--template tag (in the template)-->
{{ mydict|lookup:item.name }}
Answered By: NJHJ

Environment: Django 2.2

  1. Example code:


    from django.template.defaulttags import register

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

I put this code in a file named template_filters.py in my project folder named portfoliomgr

  1. No matter where you put your filter code, make sure you have __init__.py in that folder

  2. Add that file to libraries section in templates section in your projectfolder/settings.py file. For me, it is portfoliomgr/settings.py



    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
                'libraries':{
                    'template_filters': 'portfoliomgr.template_filters',
                }
            },
        },
    ]

  1. In your html code load the library

    
    {% load template_filters %}
    
Answered By: Krishna

The accepted answer works fine, but I just want to add how to drill down nested value using filter chaining

mydict = {"USD": { "amount": 30 }, "JPY": { "amount": 3000 }}
currency = "JPY"
{{ mydict|get_item:currency|get_item:"amount" }}

The output will be 3000

Answered By: cyberfly