21

I'm trying to use a simple_tag and set a context variable. I'm using the trunk version of django:

from django import template

@register.simple_tag(takes_context=True)
def somefunction(context, obj):   
    return set_context_vars(obj)

class set_context_vars(template.Node):
    def __init__(self, obj):
        self.object = obj
    
    def render(self, context):
        context['var'] = 'somevar'
        return ''

This doesn't set the variable, but if I do something very similar with @register.tag it works but the object parameter doesn't pass through...

Thanks!

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
neolaser
  • 6,722
  • 18
  • 57
  • 90

3 Answers3

24

You are mixing two approaches here. A simple_tag is merely a helper function, which cuts down on some boilerplate code and is supposed to return a string. To set context variables, you need (at least with plain django) to write your own tag with a render method.

from django import template

register = template.Library()


class FooNode(template.Node):

    def __init__(self, obj):
        # saves the passed obj parameter for later use
        # this is a template.Variable, because that way it can be resolved
        # against the current context in the render method
        self.object = template.Variable(obj)

    def render(self, context):
        # resolve allows the obj to be a variable name, otherwise everything
        # is a string
        obj = self.object.resolve(context)
        # obj now is the object you passed the tag

        context['var'] = 'somevar'
        return ''


@register.tag
def do_foo(parser, token):
    # token is the string extracted from the template, e.g. "do_foo my_object"
    # it will be splitted, and the second argument will be passed to a new
    # constructed FooNode
    try:
        tag_name, obj = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
    return FooNode(obj)

This may be called like this:

{% do_foo my_object %}
{% do_foo 25 %}
Reiner Gerecke
  • 11,936
  • 1
  • 49
  • 41
  • 6
    Note that the development version of Django includes `assignment_tag` which is similar to `simple_tag` but with `as variablename` implemented: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#assignment-tags – Jordan Reiter Mar 15 '12 at 16:39
  • Huh, I'd never run across `assignment_tag` before. Nifty. An update for future readers: `assignment_tag` is available for use in Django versions >= 1.4 (which I assume was in dev when comment above was made). – chucksmash Apr 07 '13 at 18:07
  • 3
    saving `simple_tag` result using `as variable` is now possible and `assignment_tag` is being deprecated. – mehmet Aug 22 '16 at 17:29
  • 1
    From the docs: assignment_tag is deprecated since version 1.9. simple_tag can now store results in a template variable and should be used instead. https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/#assignment-tags – highpost Nov 16 '17 at 23:21
10

Since Django 1.9, it is possible to store simple_tag results in a template variable by using the as argument followed by the variable name:

@register.simple_tag
def current_time(format_string):
    return datetime.datetime.now().strftime(format_string)
{% current_time "%Y-%m-%d %I:%M %p" as the_time %}
<p>The time is {{ the_time }}.</p>
mrts
  • 16,697
  • 8
  • 89
  • 72
0

You can store a context variable with @register.simple_tag which returns simple data instead of returning a complicated Node class based object as shown below:

# "custom_tags.py"

from django.template import Library

register = Library()

@register.simple_tag(takes_context=True)
def person(context):
    context["name"] = "John"
    context["age"] = 36
    return ""
# "index.html"

{% load custom_tags %}

{% person %}
{{ name }} {{ age }}

Output:

John 36

In addition, you can store the return value from @register.simple_tag's person() to person_info with as argument as shown below:

# "custom_tags.py"

@register.simple_tag(takes_context=True)
def person(context):
    return "John 36"
# "index.html"

{% load custom_tags %}

{% person as person_info %}
{{ person_info }}

Output:

John 36

And, you can store a context variable with @register.tag which returns a complicated Node (class) based object as shown below. *@register.tag cannot accept takes_context argument otherwise there is an error and doesn't work with as argument:

# "custom_tags.py"

from django.template import Library, Node

register = Library()

@register.tag
def person(parser, token):
    return PersonNode()

class PersonNode(Node):
    def __init__(self):
        pass

    def render(self, context):
        context["name"] = "John"
        context["age"] = 36
        return ""
# "index.html"

{% load custom_tags %}

{% person %}
{{ name }} {{ age }}

Output:

John 36
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129