324
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?

1

12 Answers 12

481

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 }} 
Sign up to request clarification or add additional context in comments.

13 Comments

Django Custom Template Tag documentation, for those finding this in the future.
Why is this not built in by default? :-(
Does the filter go in views.py, some extra filters.py, or what file?
@BerislavLopac because the Django template language sucked when it was created and it still sucks today. Friends don't let friends use Django templates.
|
100

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.

7 Comments

He did not ask to enumerate a dict (as you show) - he asked to get the dict's value given a variable key. Your proposal does not provide solution.
It is a solution (just very inefficient) since you can enumerate the items of the dict and then match with the key from the list.
Note that this does not work if the dictionary you are trying to access contains another dictionary inside.
If your values are dicts, you can include another for loop to process their keys and values but is likely that the complexity is taking you towards it being worth using a custom filter as described in @culebron's answer.
@PaulWhipp i have the same problem but the key has multi values and when im tring your answer it shows only first value .
|
50

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

2 Comments

I would still use return value.get(arg) because that would not throw a KeyError exception if the key is not present.
return value.get(arg, None)
8

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

2 Comments

Why register = Library() ? What does it do ?
If you want all your templates to know about your new filter, then you have to register it under django.template.base.Library class. by register = Library() we instantiate that class and use filter function annotator inside it to reach our need.
4

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

Comments

2

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

Comments

1

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

3 Comments

Is it possible to assign the value returned({{ mydict|lookup:item.name }}) to a variable
@Jibin I am not sure what you mean by your question. Perhaps my code was confusing; I have corrected it and added comments since then.
@Jibin coming from grails/gsp and other template languages I had the same question - but one needs to think different in django: you can do that before you render the template. When you create the context for the template in the view you can just add transient properties and (I believe) even methods to your model objects and access those from the template - great for all adhoc stuff you need just in that template and gives very readable template code.
1

After the 11 years later.

You can use this doc for 4.2 https://docs.djangoproject.com/en/4.2/howto/custom-template-tags/

views.py

from django.template.defaultfilters import register @register.simple_tag def example_tag(var_1, var_2, *args, **kwargs): args_1 = kwargs["args_1"] args_2 = kwargs["args_2"] return args_1+" "+ args_2 

example.html

{% example_tag variable_1 variable_2 args_1="hi" args_2="hello" %} 

output

hi hello 

Comments

0

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

Comments

0

Please don't. Arbitrary dictionary lookups have not been implemented by Django, but for good reasons.

The main reason not to do this, is that logic in the template that needs this, is often business logic. Such logic does not belong to the template, but should be moved to the view.

Another problem is that Django's template engine is not very efficient: it requires a lot of context resolutions, and rendering logic, meaning that such dictionary lookups are thus not "for free", these will slow down rendering, especially if it is done in a loop.

Often it will also require resolving the variable that is the key, and this can make debugging more tricky, since it is possible that the result of the context resolution is not what one would expect. For example if the variable is not found, it will use the string_if_invalid value [Django-doc], which is not per se what one might intend, and thus the behavior could produce a result, but a wrong result.

Finally if an error occurs, it makes debugging harder, and if something goes wrong, it is possible that this is passed silently, whereas it is often better that in case something fails, it is at least logged somewhere.

If you need to perform arbitrary subscripting or attribute lookups, that means you should move the logic from the template to the view. Indeed, in that case you prepare the data in the view, for example here with:

def my_view(request): my_list = ['key2'] my_dict = {'key1': 'value1', 'key2': 'value2'} result = [my_dict[k] for k in my_list] return render(request, 'name-of-template.html', {'result': result})

and thus then process result in the template, not the logic with my_dict and my_list.

Comments

0

If your dictionary has a fixed set of keys, then Pythons relatively new dataclasses offers an elegant way to do this.

Create a dataclass for your dictionary

@dataclass class MyObject: key_1: str key_2: str 

Then warp your dictionary in it before passing it to the template. So probably in get_conetxt_data().

context.update({"mydict" :MyObject(**mydict)}) 

Then you can access the values with the dot notaion in the template.

{{ mydict.key_1 }} 

Comments

-2

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> 

2 Comments

The template code {{ dict_obj.obj.obj_name }} is in this case equivalent to Python code dict_obj["obj"]["obj_name"], however, the question is about the equivalent of dict_obj[obj][obj_name].
How is the answer used, inside a template?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.