38

I am not able to append add a new entry into a dictionary object while using jinja2 template.

For example, here I am using jinja2 template and I have created a data variable which is a dictionary. And after checking some if condition I want to append location attribute to the data object, e.g.:

{%- set data = {
                  'name' : node.Name,
                  'id' : node.id,
               }
-%}

{% if node.location !="" %}
    data.append({'location': node.location}) 
{% endif %}

However I could not find a way to achieve this and am getting the UndefinedError:

jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'append'

Has anyone faced this issue or could provide a reference to solve this?

I searched the web but could not find a solution i.e. how to achieve adding an entry to the dict object in the Jinja.

I have referred following and other web resources:

  1. http://cewing.github.io/training.codefellows/assignments/day22/jinja2_walkthrough.html
  2. In Jinja2 whats the easiest way to set all the keys to be the values of a dictionary?
  3. https://github.com/saltstack/salt/issues/27494
β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
hemant_maverik
  • 539
  • 1
  • 4
  • 9

5 Answers5

30

Without the jinja2.ext.do extension, you can do this:

{% set x=my_dict.__setitem__("key", "value") %}

Disregard the x variable and use the dictionary which is now updated.

This also works for len() (__len__()), str() (__str__()), repr() (__repr__()) and many similar things.

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
  • hmm, does not work, with ```myString="{% set data={} %}{% set data2=data.__setitem__('key','value') %}{{ data2['key'] }}"; a=Environment(loader=BaseLoader()).from_string(myString); a.render()``` the output is `''` – K. Frank Feb 10 '19 at 00:53
  • @K.Frank, the answer says to disregard the variable that you assign to. `{% set data={} %}{% set x=data.__setitem__('key', 'value') %}{{ data['key'] }}` – sleblanc May 27 '19 at 22:05
  • 4
    I get `access to attribute '__setitem__' of 'dict' object is unsafe` when trying this (Python3, running [dbt](https://www.getdbt.com/)=0.14.2) – dsz Oct 03 '19 at 23:42
18

Dictionaries do not have the append method. You can add a key-value pair like this though:

{% do data.update({'location': node.location}) %}

or

{% do data['location']=node.location %} 
β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
alpert
  • 4,500
  • 1
  • 17
  • 27
  • 2
    Thanks Alpert! Though after using {% do data['location']=node.location %} or {% do data.update({'location': node.location}) %} I still got the TemplateSyntaxError, but after some searching on web I discovered my jinja environment was missing the jinja2.ext.do extention because of which that "do" tag was throwing the syntax error. Now it is working fine :) Thanks again!! – hemant_maverik Apr 28 '16 at 06:27
  • This link helped as well : http://stackoverflow.com/questions/2703013/how-can-i-modify-merge-jinja2-dictionaries – hemant_maverik Apr 28 '16 at 06:40
  • 10
    The first `{% do data['location'] = node.location %}` does not work for me, perhaps because `data['location'] = node.location` is not actually an expression and does not work with `do`, which expects expressions? – Kye W Shi Jan 02 '18 at 20:17
14

Without the do extension:

{%- set _ = dict.update({c.name: c}) -%}

Works in base Jinja2 on Python 3, where the __setitem__ solutions give me:

access to attribute 'setitem' of 'dict' object is unsafe

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
dsz
  • 4,542
  • 39
  • 35
  • FYI - this works well for updating saltstack pillars with jinja 2 prior to state execution with python 3 clients. – Peter M Jan 10 '20 at 20:56
13

Key takeaways:

  1. Dictionary does not support append().
  2. You can add the new item to the data dictionary by using the {% do ... %} tag:
    {% do data.update({'location': node.location}) %}
    
  3. However, for the do tag to work properly you need to add the jinja2.ext.do extension to your Jinja environment.
β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
hemant_maverik
  • 539
  • 1
  • 4
  • 9
3

Without any of the extensions:

{% set d1 = {'a':'b', 'c':'d'} %}
{% set d2 = {'e':'f'} %}

{{ dict(d1, **d2) }}

Basically you merge the two dicts to create a new one. As this doesn't use any extensions, it also works in Home Assistant.

palenpet
  • 31
  • 1