Use Variables for key in Jinja Render method - python

I am trying to iterate over a dictionary and then use the dictionary items to render the template using Jinja.
for key,value in dict.values()
out = template.render(key=value)
I am using undefined=DebugUndefined so that i can use multiple template renders without overwriting the placeholders.
The issue i am facing is that Jinja expects the exact string being passed to the render and i am not able to use the dictionary key. Jinja is trying to find the text {{ key }} for replacement.
Is there a a way I can pass in a variable as the key?

You have to build the dictionary and pass it as a kwargs:
out = template.render(**{key: value})

Related

airflow rendered template becomes a string

I'm attempting to use the jinja templating to parse some json found in xcom into a dictionary. Note below that Operator and templated_field are psuedo-code.
def xcom_from_json(xcom):
xcom_loaded = json.loads(xcom)
logging.info(pformat(f'xcom loaded: {xcom_loaded}', indent=3))
return xcom_loaded
PythonOperator(python_callable=some_callable,
op_args=[f'{{{{ (ti.xcom_pull("{task_id}") | xcom_from_json)["data"]["stats"] }}}}'])
The above works, almost. In the some_callable method I get the parsed jinja, but it comes out as a stringified dict instead of a raw dict. This doesn't make sense, because you can see the structure being traversed as a dict in the jinja template. Does jinja stringify everything coming out of a template? If yes, is there a way to not do that?
A rendered jinja template is always going to return a string. What you can do instead is fetch the XCom value from within the python method instead.
def some_callable(task_id, **context):
stats = json.loads(context['ti'].xcom_pull(task_id)['data']['stats'])
PythonOperator(
...
python_callable=some_callable,
op_args=[f'{task_id}'],
provide_context=True)
Note that you must provide context, which gives the python method the same access to values a jinja template has.

jinja: access nested dict without key errors

I have a nested dict from a json api call and want to display values from deep within the dict
{{ context.city.houston.street }}
It could be that this value is not present in the dict. How can I avoid key errors in a deeply nested dict in Jinja?

How do I populate a list field variable in Ansible (jinja2, yaml) without inserting the unicode 'u' prefix?

For my python application, I am trying to create and populate a list field in a config file created and deployed by Ansible. Right now it just has one list element (phone_number_), in the future it could have more, hence being a list.
In my jinja2 yaml template I have:
FIELD_NAMES: {{field_names}}
And in my variable var yaml file I have:
field_names: ['phone_number_']
The resulting config file deployed to be server has this line produced:
FIELD_NAMES: [u'phone_number_']
I don't want that unicode "u" in there. How can I get rid of it?
There are to_json and to_yaml filters, that may be helpful.
FIELD_NAMES: {{ field_names | to_json }}
Will give you clean JSON list without unicode markers:
FIELD_NAMES: ["phone_number_"]
You have defined a list with one element:
field_names: ['phone_number_']
I see two options:
1) Reference the variable in your jinja2 template as:
FIELD_NAMES: {{field_names[0]}}
2) Define the variable as a string:
field_names: 'phone_number_'

Accessing dict elements with leading underscores in Django Templates

I am trying to access elements of a dict with keys that start with the underscore character. For example:
my_dict = {"_source": 'xyz'}
I'm trying to access them in a Django template. Obviously I realise that you can't access underscored python variables from a Django template (because they are considered private in Python) but this is a dict object where any immutable object is a valid key.
I can't access the above dict in a Django template using {{ my_dict._source }} so I assume Django is preventing it. Is that accurate?
I am kind of hoping Django does something sane with variables that start with underscore like still doing dict lookups (the first thing is supposedly tries) but refuses to do attribute lookups, method calls and list index lookups since an underscored prefixed variable would be invalid. I am quickly loosing hope though.
For the record, I know someone will suggest to just change the dict but this is actually a multi-levelled dictionary returned by the rawes library when executing REST API request on a ElasticSearch instance.
The docs mention that you can't have a variable start with an underscore:
Variable names must consist of any letter (A-Z), any digit (0-9), an underscore (but they must not start with an underscore) or a dot.
but you can easily write a custom template filter to mimic the dictionary's get method:
#register.filter(name='get')
def get(d, k):
return d.get(k, None)
and
{{ my_dict|get:"_my_key" }}
In my case, if I know the dict elements, and it's only one, I prefer to rename the dict key using pop:
my_dict['new_key'] = my_dict.pop('_old_key')
That way I get a new name on the dict, and I can access in the template without problems.

How do I access dictionary keys that contain hyphens from within a Django template?

We have a system built on a custom database, where many of the attributes are named containing hyphens, ie:
user-name
phone-number
These properties cannot be accessed in templates as follows:
{{ user-name }}
Django throws an exception for this. I'd like to avoid having to convert all of the keys (and sub-table keys) to use underscores just to work around this. Is there an easier way?
A custom template tag is probably the only way to go here if you don't want to restructure your objects. For accessing dictionaries with an arbitrary string key, the answer to this question provides a good example.
For the lazy:
from django import template
register = template.Library()
#register.simple_tag
def dictKeyLookup(the_dict, key):
# Try to fetch from the dict, and if it's not found return an empty string.
return the_dict.get(key, '')
Which you use like so:
{% dictKeyLookup your_dict_passed_into_context "phone-number" %}
If you want to access an object's attribute with an arbitrary string name, you could use the following:
from django import template
register = template.Library()
#register.simple_tag
def attributeLookup(the_object, attribute_name):
# Try to fetch from the object, and if it's not found return None.
return getattr(the_object, attribute_name, None)
Which you would use like:
{% attributeLookup your_object_passed_into_context "phone-number" %}
You could even come up with some sort of string seperator (like '__') for subattributes, but I'll leave that for homework :-)
Unfortunately, I think you may be out of luck. From the docs:
Variable names must consist of any letter (A-Z), any digit (0-9), an
underscore or a dot.
OrderedDict dictionary types support dashes:
https://docs.python.org/2/library/collections.html#ordereddict-objects
This seems to be a side effect of the implementation of OrderedDict. Notice below that the key value pairs are actually passed in as sets. I would bet that the implementation of OrderedDict doesn't use the "key" passed in the set as a true dict key thus getting around this issue.
Since this is a side-effect of the implementation of OrderedDict, it may not be something you want to rely on. But it works.
from collections import OrderedDict
my_dict = OrderedDict([
('has-dash', 'has dash value'),
('no dash', 'no dash value')
])
print( 'has-dash: ' + my_dict['has-dash'] )
print( 'no dash: ' + my_dict['no dash'] )
Result:
has-dash: has dash value
no dash: no dash value

Categories

Resources