ChatGPT解决这个技术问题 Extra ChatGPT

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?


p
pyjavo

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

Django Custom Template Tag documentation, for those finding this in the future.
Why is this not built in by default? :-(
in Jinja2 {{ mydict[key] }}
Does the filter go in views.py, some extra filters.py, or what file?
P
Paul Whipp

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.


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 .
A
Alasdair

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

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)
A
AmiNadimi

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

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.
s
sexybear2

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

K
Krishna

Environment: Django 2.2

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

No matter where you put your filter code, make sure you have __init__.py in that folder 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',
                }
            },
        },
    ]

In your html code load the library {% load template_filters %}


N
NJHJ

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

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.
Y
Yi Yang Apollo

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>

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?