Jinja: Impossível iterar sobre o objeto com o elemento "itens"

Criado em 9 jan. 2017  ·  10Comentários  ·  Fonte: pallets/jinja

Um servidor está retornando este JSON:
{
"itens": [1,2,3]
...
}

Uma tentativa de iterar sobre 'itens' produz:
{% para el em data.items%}
....
TypeError: o objeto 'builtin_function_or_method' não é iterável

Acima acontece porque Python dict tem uma função interna "items ()".

Como temporário. Como alternativa, instalei um gancho e renomeei as chaves na resposta JSON de "itens" para "__items" e passei isso para Jinja2. Nesse ponto, estou iterando o atributo renomeado bem:
{% para el em dados .__ itens%}

Idealmente, o Jinja2 deve ignorar as funções integradas no for-loop ou na instrução if, a menos que seja especificamente chamado com uma notação function ().

Comentários muito úteis

Você pode usar {% for el in data['items'] %} para priorizar o item. Isso também é abordado na documentação. http://jinja.pocoo.org/docs/2.9/templates/#variables

Todos 10 comentários

Você pode usar {% for el in data['items'] %} para priorizar o item. Isso também é abordado na documentação. http://jinja.pocoo.org/docs/2.9/templates/#variables

Ele falhará se a resposta JSON ocasionalmente não incluir os 'itens' (digamos que seja um atributo opcional da resposta). Nesse caso, {% for el in data ['items']%} também produzirá:
TypeError: o objeto 'builtin_function_or_method' não é iterável

... já que os itens são sempre definidos (como uma função embutida).

E isso é um bug, IMO. Os dados ['itens'] nunca devem colidir com os embutidos. Em outras palavras, se você deseja iterar de forma limpa um atributo opcional de "itens", você é forçado a renomeá-lo. Idealmente, não deveria ser o caso.

Por que a resposta JSON não inclui itens? Não tenho certeza se vejo o problema aqui. Certamente você sabe com antecedência quais são seus dados. Você também pode verificar as chaves em modelos com o operador in nos dicionários.

Alterar esse comportamento está completamente fora de questão porque milhares de modelos seriam corrompidos se removêssemos esse suporte de atributo / item.

Não estou sugerindo abandonar nada, apenas para torná-lo mais rígido.

O problema hoje é que quando você usa a notação de pesquisa explícita: data['items'] ela encontra uma função de dicionário embutida e não deveria . Python não faz isso, nem deveria jinja. Se for necessário chamar o builtin (ou qualquer função), você tem a notação de chamada para ele: data.items()

Não tenho certeza se seria muito claro para o caso especial dict.items lá. Especialmente porque ainda falharia para tipos personalizados do tipo dict ou talvez até OrderedDict . Acho que é melhor foo['items'] nesses casos. Não é um comportamento muito bonito, mas consistente.

A única "correção / hack" possível que eu poderia imaginar para manipular isso de uma maneira melhor é verificar se um atributo simples (ou seja, não uma chamada de função) é usado em uma iteração e se você está tentando iterar sobre algo que não é iterável, mas pode ser chamado . Nesse caso, tentar o item dificilmente quebrará qualquer código existente. No entanto, a implementação de tal comportamento em Jinja provavelmente seria extremamente terrível;)

Por que este é um caso especial? Estou pedindo que Jinja se comporte exatamente como o Python. Especificamente, foo ['items'] deve gerar None se não houver nenhum "item" de chave inserido pelo usuário nele. Hoje, Jinja volta a encontrar a função incorporada de mesmo nome. Então, basta fazer o que Python faz:

>>> print {}.get('items')
None

A propósito, eu também acredito que a notação alternativa "foo.items" deve ser tratada muito bem também, já que você precisa usar "()" para chamar um chamável, mas se você precisar de alguma compatibilidade com versões anteriores para foo.items para encontrar uma tecla ou chamar uma função automaticamente, então eu acho que isso é uma pena.

A lógica Jinja para ['x'] e .x é esta:

  • Verifique se o item / attr (qualquer que seja sua sintaxe em Python) existe, se sim, use-o
  • Verifique se o attr / item (o "oposto" do que sua sintaxe faria no Python) existe, se sim, use-o
  • Falha se nenhum existir

Portanto, x.items verifica o atributo e, desde que exista, ele o usa. Essa lógica funciona assim, não importa o concurso. Melhorar isso exigiria que a lógica de pesquisa de atributo mudasse dependendo de seu contexto. E mesmo assim, isso resultaria em situações embaraçosas como este código que funcionaria:

{% for x in mydict.items %}

vs este código que não funcionaria

{% set items = mydict.items %}
{% for x in items %}

E manter um tratamento especial neste caso seria simplesmente impossível sem rastrear de onde uma variável veio.

@slisznia, você está essencialmente pedindo que o Jinja2 mude completamente o sistema de pesquisa de atributos que está em vigor há cerca de 7 anos neste momento.

Ah, eu nem vi a sugestão de foo['bar'] não fazer uma pesquisa de atributo. Com certeza é uma má ideia! O que há de errado com você simplesmente usar for item in data.get('items', []) ?

@ThiefMaster o mydict.get('items', []) funciona com certeza, e obrigado por apontar outro método além de renomear chaves.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

jp-costa picture jp-costa  ·  5Comentários

hvnsweeting picture hvnsweeting  ·  4Comentários

glasserc picture glasserc  ·  4Comentários

samatjain picture samatjain  ·  5Comentários

harobed picture harobed  ·  6Comentários