I want to be able to set variables in a template to string values. I wrote a tag, but it doesn't seem to change the context. The intended use is:
{% define "a string" as my_var %}
Update (solved):
class DefineNode(Node):
def __init__(self, var, name):
self.var = var
self.name = name
def __repr__(self):
return "<DefineNode>"
def render(self, context):
context[self.name] = self.var
return ''
@register.tag
def define(parser, token):
"""
Adds a name to the context for referencing an arbitrarily defined string.
For example:
{% define "my_string" as my_string %}
Now anywhere in the template:
{{ my_string }}
"""
bits = list(token.split_contents())
if (len(bits) != 4 or bits[2] != "as") or \
not (bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]):
raise TemplateSyntaxError("%r expected format is '\"string\" as name'" % bits[0])
else:
value = bits[1][1:-1]
name = bits[3]
return DefineNode(value, name)
We can set the value of a variable in the Django template using with tag. This will output the below content. One downside of this approach is that we have to write the lines where we are accessing the variable inside with and endwith block. Using with is useful when using a costly variable multiple times.
A context is a variable name -> variable value mapping that is passed to a template. Context processors let you specify a number of variables that get set in each context automatically – without you having to specify the variables in each render() call.
{% %} and {{ }} are part of Django templating language. They are used to pass the variables from views to template. {% %} is basically used when you have an expression and are called tags while {{ }} is used to simply access the variable.
Django already considered this particular case and provides the assigment tags, an special way of registering tags that set a variable in the context.
In this case, you don't need to care about retrieving, updating and saving the context. You simply do this:
@register.assignment_tag
def define(the_string):
return the_string
And you can use it the very same way, but it is much cleaner:
{% define "a string" as my_var %}
This all the code you need.
EDIT:
As Dirk Bergstrom pointed out, since version django 1.9 assignment_tag
is deprecated. simple_tag
is a perfect replacement.
@register.simple_tag
def define(the_string):
return the_string
The answer is buried inside the more complex current_time example in the documentation.
Problem
You want to add a variable to the context. But you don't want to go back and add that variable to all the views which call all the templates which invoke the tag. You just want a tag which can add some data to the context wherever its wanted. I'm looking for this kind of thing when rendering those random distractions which get dropped into sidebars and aren't specifically related to the work of the main view, for example.
Method
To inject a variable to the context you need access to the context. To do that your custom tag will inject a node which added the data to the template context.
Example
This example adds a "coming_events" queryset to the context then loops over each result. It does that by declaring a custom tag which renders a node which adds a queryset to the context.
from django import template
from apps.events.models import Event
register = template.Library()
@register.tag
def coming_events(parser, token):
return EventNode()
class EventNode(template.Node):
def render(self, context):
context['coming_events'] = Event.objects.all()
return ''
You'd use it like this:
{% load events %}
{% coming_events %}
{% for event in coming_events %}
<div class="eventItem">
<p>{{event.title}} {{event.data}}</p>
</div>
{% endfor %}
Extra Credit
If you're really keen to be able to name the variable arbitrarily eg {% coming_events as events %} then look closely at the example in the documentation and note how they split the token into what's before the ' as ' and what's after and use the latter part to name the context variable. You'd have to implement that.
Note that if I wound up putting the HTML for each event into its own dedicated template then I'd be better off just following the standard inclusion tag example in the documentation. This solution is suggested for when you want the data without any baggage.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With