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

MD. Khairul Basar
  • 4,976
  • 14
  • 41
  • 59
Stan
  • 37,207
  • 50
  • 124
  • 185
  • https://stackoverflow.com/a/8000091/2039467 return dictionary.get(key) if dictionary else return None – Koshak01 Jul 31 '22 at 14:29

10 Answers10

461

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 }}
pyjavo
  • 1,598
  • 2
  • 23
  • 41
culebrón
  • 34,265
  • 20
  • 72
  • 110
  • 19
    [Django Custom Template Tag documentation](https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-tags), for those finding this in the future. – Jeff Aug 25 '12 at 21:26
  • 362
    Why is this not built in by default? :-( – Berislav Lopac Dec 28 '12 at 15:23
  • 12
    I think @Jeff meant [Django Custom Template Filter documentation](https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-filters) – Jorge Leitao Jul 24 '13 at 10:08
  • 7
    in Jinja2 {{ mydict[key] }} – Evgeny Nov 12 '14 at 02:45
  • 5
    Great solution. @BerislavLopac Sadly it's a wontfix with Django core developers : https://code.djangoproject.com/ticket/3371 – user Jan 11 '15 at 16:47
  • 24
    Does the filter go in views.py, some extra filters.py, or what file? – AlanSE Jun 28 '15 at 18:10
  • 1
    Is there a possibility to save the tag result in a variable? – Tom Maier Sep 10 '15 at 14:30
  • 4
    +alanse I added it as a sub-function INSIDE the views.py function in question... but see https://docs.djangoproject.com/en/1.10/howto/custom-template-tags/#code-layout – Jorge Orpinel Pérez Jun 12 '17 at 19:31
  • 1
    @brunodesthuilliers I mean in terms of the conceptual design of JSX for rendering HTML content as compared to Django templates. There's no reason something much more React-like isn't possible in Python. – Andy Jun 09 '18 at 03:01
  • I was only able to get this to work after adding a name argument based on @Yuji 'Tomita' Tomita answer: @register.filter(name='get_item') – Keith Ritter Jun 28 '18 at 05:56
  • 6
    @BerislavLopac because the Django template language sucked when it was created and it still sucks today. Friends don't let friends use Django templates. – Tobia Sep 27 '18 at 10:16
  • @MatheusJardimB Is it possible to assign the return data of tag filter to a variable or to check the return data is not empty. like, variable={{ mydict|get_item:item.NAME }} – Jibin Aug 19 '20 at 16:51
  • This worked for me. How would I pass two arguments in this way? {{ mydict_get_item:'attribute', 'second_attribute' }} does not seem to work. – Jake Mulhern Dec 15 '21 at 17:18
93

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.

Paul Whipp
  • 16,028
  • 4
  • 42
  • 54
  • 52
    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. – staggart Nov 09 '15 at 18:11
  • 1
    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. – DylanYoung Aug 25 '16 at 12:43
  • 2
    Note that this does not work if the dictionary you are trying to access contains another dictionary inside. – J0ANMM Jun 17 '17 at 19:33
  • 1
    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. – Paul Whipp Jun 18 '17 at 20:35
  • @PaulWhipp i have the same problem but the key has multi values and when im tring your answer it shows only first value . – M.Mevlevi Feb 11 '22 at 14:26
  • @M.Mevlevi I'm not sure what you mean by a key having multi values but I'm guessing that you have something like a dict with {a: [1, 2, 3]}. value would be bound to [1, 2, 3] in that case so you'd need to handle that appropriately rather than just presenting the value directly (which assumes it has a string representation). – Paul Whipp Feb 11 '22 at 20:58
  • I think this solution is still good for someone although it can't work if the value is another dictionary. – Alston Apr 14 '23 at 13:45
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 }}
Alasdair
  • 298,606
  • 55
  • 578
  • 516
Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
7

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 }}
AmiNadimi
  • 5,129
  • 3
  • 39
  • 55
  • Why `register = Library()` ? What does it do ? – MD. Khairul Basar Dec 13 '17 at 10:03
  • 2
    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. – AmiNadimi Dec 14 '17 at 10:57
3

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 %}
    
Krishna
  • 33
  • 2
  • 5
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_ }}
sexybear2
  • 21
  • 1
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 }}
NJHJ
  • 142
  • 3
  • 9
  • Is it possible to assign the value returned({{ mydict|lookup:item.name }}) to a variable – Jibin Aug 19 '20 at 17:03
  • @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. – NJHJ Aug 21 '20 at 01:46
  • @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. – weHe Sep 16 '21 at 09:25
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

cyberfly
  • 5,568
  • 8
  • 50
  • 67
0

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
msp
  • 1
  • 1
-1

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>
Yi Yang Apollo
  • 321
  • 1
  • 4
  • 1
    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]`. – Flimm Jun 18 '19 at 11:18
  • How is the answer used, inside a template? – Martins Nov 24 '20 at 14:25