Django template como procurar um valor de dicionário com uma variável

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

A maneira regular para procurar um valor dicionário em um modelo de Django é {{ mydict.key1 }}, {{ mydict.key2 }}. E se a chave for uma variável de loop? ou seja:

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

mydict.item.NAMEfalha. Como consertar isto?

Stan
fonte

Respostas:

362

Escreva um filtro de modelo personalizado:

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

(Eu uso .getpara que, se a chave estiver ausente, ela não retorne nenhuma. Se você fizer dictionary[key], aumentará um KeyErrorentão.)

uso:

{{ mydict|get_item:item.NAME }}
culebrón
fonte
16
Documentação do Django Custom Template Tag , para aqueles que encontrarem isso no futuro.
25412 Jeff
281
Por que isso não é incorporado por padrão? :-(
Berislav Lopac
11
Acho @ Jeff significava Django modelo personalizado documentação Filtro
Jorge Leitao
5
em Jinja2 {{mydict [key]}}
Evgeny
9
O filtro entra em views.py, em alguns filtros.py extras ou em qual arquivo?
AlanSE
56

Busque a chave e o valor do dicionário no loop:

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

Acho isso mais fácil de ler e evita a necessidade de codificação especial. Eu geralmente preciso da chave e do valor dentro do loop de qualquer maneira.

Paul Whipp
fonte
28
Ele não pediu para enumerar um ditado (como você mostra) - ele pediu para obter o valor do ditado com uma chave variável. Sua proposta não fornece solução.
Staggart
É uma solução (apenas muito ineficiente), pois você pode enumerar os itens do dict e depois combinar com a chave da lista.
precisa saber é o seguinte
1
Observe que isso não funciona se o dicionário que você está tentando acessar contiver outro dicionário.
J0ANMM
Se seus valores são dictos, você pode incluir outro loop for para processar suas chaves e valores, mas é provável que a complexidade esteja levando você a valer a pena usar um filtro personalizado, conforme descrito na resposta do @ culebron.
Paul Whipp
37

Você não pode, por padrão. O ponto é o separador / gatilho da pesquisa de atributo / pesquisa de chave / fatia.

Pontos têm um significado especial na renderização do modelo. Um ponto no nome de uma variável significa uma pesquisa. Especificamente, quando o sistema de modelos encontra um ponto em um nome de variável, ele tenta as seguintes pesquisas, nesta ordem:

  • Pesquisa de dicionário. Exemplo: foo ["bar"]
  • Pesquisa de atributo. Exemplo: foo.bar
  • Pesquisa de índice de lista. Exemplo: foo [bar]

Mas você pode criar um filtro que permita passar um argumento:

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 }}
Yuji 'Tomita' Tomita
fonte
1
Eu ainda usaria return value.get(arg)porque isso não geraria uma exceção KeyError se a chave não estiver presente.
slajma 16/01
3

Para mim, criar um arquivo python nomeado template_filters.pyno meu aplicativo com o conteúdo abaixo fez o trabalho

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

register = Library()


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

o uso é como o que o culebrón disse:

{{ mydict|get_item:item.NAME }}
AmiNadimi
fonte
Por que register = Library()? O que isso faz ?
MD. Khairul Basar
2
Se você deseja que todos os seus modelos saibam sobre seu novo filtro, é necessário registrá-lo na django.template.base.Libraryclasse. por register = Library()que instanciar a classe e uso filterfunção anotador dentro dele para chegar a nossa necessidade.
AmiNadimi
2

Eu tive uma situação semelhante. No entanto, eu usei uma solução diferente.

No meu modelo, crio uma propriedade que faz a pesquisa no dicionário. No modelo, então uso a propriedade

No meu modelo: -

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

No meu modelo: -

The state is: {{ item.state_ }}
sexybear2
fonte
1

Como não posso comentar, deixe-me fazer isso na forma de uma resposta:
para aproveitar a resposta de culebrón ou a resposta de Yuji 'Tomita' Tomita , o dicionário passado para a função está na forma de uma string, portanto, use ast. literal_eval para converter a string em um dicionário primeiro, como neste exemplo .

Com esta edição, o código deve ficar assim:

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

{{ mydict|lookup:item.name }}
Queimadura
fonte
0

Ambiente: Django 2.2

  1. Código de exemplo:


    from django.template.defaulttags import register

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

Coloquei esse código em um arquivo chamado template_filters.py na minha pasta do projeto chamada portfoliomgr

  1. Não importa onde você colocou o código do filtro, verifique se possui __init__.py nessa pasta

  2. Adicione esse arquivo à seção de bibliotecas na seção de modelos no arquivo projectfolder / settings.py. Para mim, é 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. No seu código html, carregue a biblioteca

    
    {% load template_filters %}
Krishna
fonte
-2

env: django 2.1.7

Visão:

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

modelo:

{% 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
fonte
1
O código do modelo {{ dict_obj.obj.obj_name }}é, nesse caso, equivalente ao código Python dict_obj["obj"]["obj_name"], no entanto, a pergunta é sobre o equivalente a dict_obj[obj][obj_name].
Flimm