How to make context variable dynamic in django template - django

Is it possible to append 2 context variables together into a single context variable that is calculated at run-time?
For example: if I have {{contextA}} (which equals 1) and {{contextB}} (which equals 'building') and I wanted to add these together to get context {{1building}} How would I do this?
I have tried:
{{contextA + contextB}}
{{{{contextA}} + {{contextB}}}}
{{contextA |add: contextB}}
{{contextA contextB}}
I suppose this is not possible and this needs to be done in the view using python, but it would be ideal if I could just combine context variables in the template.

U can use {% with template tag as follows (docs here -> https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#with)
{% with newvar=contextA|add:contextB %}
{{ newvar }}
{% endwith %}
newvar will have a new value if into a forloop where contextA or contextB change it's value.
As you want to show the value of a context variable with it's name equal to newvar value, the way to accomplish it is to create a custom template tag as follows:
#register.simple_tag(takes_context=True)
def dynvarvalue(context, dynvarname):
""" Returns the value of dynvarname into the context """
return context.get(dynvarname, None)
I did a small proof of concept:
{% with 1building='1 building value' contextA='1' contextB='building' %}
{% dynvarvalue contextA|add:contextB %}
{% endwith %}
Which produces the following output, which I think is what you are asking for:
1 building value
Hope this helps.
Note: take into account that if both variables can be transformed to an integer, they will not be concatenated and will be summed as docs says (https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#add)
Note2: I think there are some security caveats to take into account doing this.

Related

Django Template Tags and Meta-Programming: Is there any way of modifying a variable's name before calling its context?

Suppose in views.py I have a variable (i.e., changing) number of forms or types of objects in my context. (I'll just use the word 'forms' for simplicity).
context = {
'form_0': form_0,
'form_1': form_1,
'form_2': form_2,
'form_3': form_3,
# ... and so forth
}
Let's suppose that I have no way of knowing how many forms are in my context at any given time. Is it at all possible to do the following with template tags:
{% for i in (number of forms in context) %}
{{ form_i }} <-- where i is equal to the number i in the loop -->
{% endfor %}
Here the end result would translate to:
{{ form_0 }}
{{ form_1 }}
{{ form_2 }}
... and so forth
I'm doubtful that this is at all possible, but if it is, I would find it quite helpful.
Referencing by name, as in constructing a string that contains the name, is usually considered an unsafe approach: it can be harder than one thinks to make an algorithm that constructs the name correctly. Forthermore say that you do that and later remove form_3, then your algorithm will likely stop at 2, but perhaps there are still other forms there, but you forgot about the algorithm fetching the forms.
It is better here to pass a collection of objects, for example a list:
context = {
'forms': [form_0, form_1, form_2, form_3]
}
then we can render this in the template with:
{% for form_i in forms %}
{{ form_i }}
{% endfor %}
If you thus add an extra form to the list, then that form will be part of the iterations in the template, and thus Django will render that form (or whatever you put in the list).
If you need acces to some specific forms as well, you can pass then trhough another name as well, for example:
context = {
'forms': [form_0, form_1, form_2, form_3],
# if we need specific items of form_2 in the template,
# we can pass it under a specific name as well
'form_2': form_2
}
so now we can both enumerate over the forms to render these, but if form_2 has some interesting data we need to handle in the template as well, we can still use {{ form_2.non_field_errors }} for example.

django - how to pass multiple values to a templatetag

I am trying to pass multiple parameters to my template tag:
#register.filter
def val_color(val, min_val):
if val >= min_val:
return 'red'
return 'black'
template:
{% for x in data.vals %}
<font color="x|data.min_val|val_color">x</font>
{% endfor %}
this approach does not work.
Any ideas how to do this?
Note that it would be too messy if I have to convert x numbers into objects with a value and the min_val properties so I am hoping there is a proper solution for this issue.
It is not clear what are you trying to do. In your function I don't see any usage of min_val.
But let me give an example how filters work.
Here is example of filter tags
#register.filter
def keyvalue(dict, key):
"""Filter to fetch a dict's value by a variable as key"""
return dict.get(key, '')
Usage of filter tag
{{ weekday_dict|keyvalue:var }}
Here weekday_dict is dict and 'var' is key I want to access. In keyvalue filter tag weekday_dict is first argument dict and var is second argument.
To pass multiple arguments you can refer to link
In short you can not easily pass multiple arguments in filter tag. You can pass it as comma seperated value, or pass them using multiple filters as given by one of answeres at link
#register.filter(name='one_more')
def one_more(_1, _2):
return _1, _2
def your_filter(_1_2, _3)
_1, _2 = _1_2
print "now you have three arguments, enjoy"
{{ _1|one_more:_2|your_filter:_3 }}
Update:
As I can seen in your updated question. You do not need to pass multiple arguments
Your filter tags is defined as:
#register.filter
def val_color(val, min_val):
if val >= min_val:
return 'red'
return 'black'
To use this tag you can update your template code to
{% for x in data.vals %}
<font color="{{ x|val_color:data.min_val }}">{{ x }}</font>
{% endfor %}
You can also set some default value to second argument and then you don't need to pass min value for default cases. Also don't forget to load filter before using them. For more details on tags refer to link

Django template variable containing template tag, ex {{ {% some_tag %} }}

I have a template that receives a list context variable, tags_list. I need to iterate over this list 'inserting' the tags in the template something like this:
{% for tag in tags_list %}
{{ tag.tag }}
{% endfor %}
When this renders it returns the text value of tag.tag, "{% tagxxx %}", not the rendered tag.
How can I cause the template render to render the value of a context variable? Alternately, is there a filter, a sort of inverse verbatim, that will cause the value of a context variable to be rendered?
Updated background
tags_list is created by a fairly sophisticated process involving exec of some user provided text from a table/model field. The relevant portion of the real template looks like this:
{% for graph_row in graph_rows %}
<div class="row">
{% for graph in graph_row %}
<div class="col-md-{{ graph.width }}">
{{ graph.graph }}
</div>
{% endfor %}
</div>
{% endfor %}
The graph values look like this: {'graph':'{% piechart data1 %}', 'width':3}
Note that the order of entries in the context variable graph_rows is significant as is order of graph(s) in the row as that determines the placement of graphs on the page. Preserving this order is essential for the scheme to work correctly.
Currently, the view function simply does an {% include ... %} to get the template segment above to render in the correct order. This approach is simple and clean.
I could, as has been suggested, perform a template render within the view function but that complicates the design a bit and I'd hoped to avoid doing that if there is an easy way to trigger a render of {{ graph.graph }}. Note, as well, by moving the render into the view I loose the ability to easily take the template from arbitrary places, in particular table fields.
One of the great things about Django is the library of solution and code snippets. Sadly, they aren't a well organized and easy to find as one might wish. Nevertheless, a bit of google found a number of solutions of the general form
{% render tag.tag %}
Here are links to several:
render_as_template template tag
Allow template tags in a Flatpage's content
render_as_template.py
I'll use the general approach cleaned up a bit for error checking.
As an aside, the technique strikes me as generally useful and might be appropriate for inclusion in the standard tags.
Update 3/28/2014
After looking at the above and several others this is what I used from render_as_template template tag. There is a useful comment here.
from django import template
from django.template import Template, Variable, TemplateSyntaxError
register = template.Library()
class RenderAsTemplateNode(template.Node):
def __init__(self, item_to_be_rendered):
self.item_to_be_rendered = Variable(item_to_be_rendered)
def render(self, context):
try:
actual_item = self.item_to_be_rendered.resolve(context)
return Template(actual_item).render(context)
except template.VariableDoesNotExist:
return ''
def render_as_template(parser, token):
bits = token.split_contents()
if len(bits) !=2:
raise TemplateSyntaxError("'%s' takes only one argument"
" (a variable representing a template to render)" % bits[0])
return RenderAsTemplateNode(bits[1])
render_as_template = register.tag(render_as_template)
This gets part of the way to a solution. Unfortunately custom template tags, in my case
{% pie_chart %} are not available to render within the class RenderAsTemplateNode.
I've not tested this but it appears that this stack overflow question, Django - replacing built-in templatetag by custom tag for a whole site without {% load .. %}, points the way.
I believe I can provide a way for you to get the results you want, but there might be a better way for you to achieve the desired functionality if you can provide some context.
Anyway, you might do something like this in your view.py:
tags_list = [
Template('{% load my_tags %}{% ' + t.tag + ' %}').render(Context())
for t in tags_list
]

update value of a variable inside a template - django

I have seen enough number of examples that allow me to declare a new variable inside a template and set its value. But what I want to do is to update the value of a particular variable inside the template.
For example I have a datetime field for an object and I want to add timezone according to the request.user from the template. So I will create a template filter like {% add_timezone object.created %} and what it will do is that it will add timezone to object.created and after that whenever I access {{object.created}} it will give me the updated value.
Can anybody tell me how this can be done. I know I need to update the context variable from the template filter. But don't know how.
You cannot modify a value in a template, but you can define 'scope' variables using the {% with %} tag:
{% with created=object.created|add_timezone %}
Date created with fixed timezone: {{ created }}
{% endwith %}
where add_timezone is a simple filter:
def add_timezone(value):
adjusted_tz = ...
return adjusted_tz
register.filter('add_timezone', add_timezone)

Django Custom Template Tag with Context Variable Argument

I have a custom template tag which shows a calendar. I want to populate certain items on the calendar based on a dynamic value.
Here's the tag:
#register.inclusion_tag("website/_calendar.html")
def calendar_table(post):
post=int(post)
imp=IMP.objects.filter(post__pk=post)
if imp:
...do stuff
In my template, it works fine when I pass a hard coded value, such as
{% load inclusion_tags %}
{% calendar_table "6" %}
However when I try something like {% calendar_table "{{post.id}}" %} , it raises a error a ValueError for the int() attempt. How can I get around this?
You want {% calendar_table post.id %}; the extra {{ and }} are what are causing you the heartburn.
Note that, in your custom tag, you need to take the string ("post.id") that gets passed and resolve it against the context using Variable.resolve. There's more information on that in the Django docs; in particular, look here: http://docs.djangoproject.com/en/1.3/howto/custom-template-tags/#passing-template-variables-to-the-tag