Opened 18 years ago

Closed 18 years ago

Last modified 16 years ago

#1322 closed enhancement (wontfix)

Feature request: new tag SET in template syste

Reported by: mordaha Owned by: Adrian Holovaty
Component: Template system Version:
Severity: normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I'd like to have a new tag SET in template system, which is creates variables on the fly.

{% set var1 var2 %}

which equivalent in python is:

var1 = var2

So each element in form will be normalized like in this example:

{% set f form.field1 %}
<div class="form-row {% if f.errors %}error{% endif %}">
{% if f.errors %}{{ f.html_error_list }}{% endif %}
<label class="required">
  field1: {{ f }} 
</label> 
</div>

{% set f form.field2 %}
<div class="form-row {% if f.errors %}error{% endif %}">
{% if f.errors %}{{ f.html_error_list }}{% endif %}
<label class="required">
  field2: {{ f }} 
</label> 
</div>

e.t.c.

Or like this:
{% set f form.field1 %}
{% set label "field1" %}
{% include "from_row_snippet" %}

{% set f form.field2 %}
{% set label "field2" %}
{% include "from_row_snippet" %}
...

Where the form_row_snippet is:

<div class="form-row {% if f.errors %}error{% endif %}">
{% if f.errors %}{{ f.html_error_list }}{% endif %}
<label class="required">
  {{ label }}: {{ f }} 
</label> 
</div>

Another use of this tag is to make list snippets like this:

{% set list peoples_list_males %}
{% include "peoples_list" %}

{% set list peoples_list_females %}
{% include "peoples_list" %}

...

where the "peoples_list" snippet is like:

<table>
...headers...
{% for obj in list %}
  <tr>
  <td>{{ obj.name }}</td>
  <td>{{ obj.age }}</td>
  <td>{{ obj.email }}</td>
  e.t.c.
  </tr>
{% endfor %}
</table>

Change History (8)

comment:1 by Karsu, 18 years ago

We have used this kind of templatetag. It does not handle all features that you requested, but it has been usefull for us.

from django.core import template
register = template.Library()

class SetVariable(template.Node):
    def __init__(self, varname, value, is_string=False):
        self.varname = varname
        self.value = value
        self.is_string = is_string

    def render(self,context):
        if self.is_string or not context[self.value]:
            context[self.varname] = self.value
        else:
            context[self.varname] = context[self.value]
        return ''

def set_var(parser, token):
    """
    Set value to variable. If value is object or variable 
    copy pointer of that object/variable to new variable.
    
        {% set_var variable value %}
    
    Set string to variable.
        {% set_var variable "new string" %}        
    """
    
    from re import split
    bits = split(r'\s+', token.contents, 2)
    if bits[2][:1] in '"\'' and bits[2][-1:] in '"\'':
        return SetVariable(bits[1], bits[2][1:-1], True)
    
    return SetVariable(bits[1], bits[2])
register.tag('set_var', set_var)

comment:2 by Adrian Holovaty, 18 years ago

Resolution: wontfix
Status: newclosed

A goal of the template system is not to turn it into a programming language, so this feature won't be added. Of course, there's nothing stopping you from using a custom template tag to do this.

comment:3 by mordaha, 18 years ago

A goal of template system - is to use templates :)
And without this tag, your template system has a big mess.
So, you answer is an stereotype only! :)

a=b is not programming language, but a try to set arguments to template procedures (snippets)

How you "template" my examples without this tag? :)

I don't like to reopen this ticket, but not sure will my question be readed ...

i hope it will :)

comment:4 by Luke Plant, 18 years ago

If you add a 'set' construct, combined with other existing tags, you do effectively have a programming language.

The examples you give can be done with a 'set' custom template tag, as you propose, but could also be done with other custom template tags, to which you can pass the variable to use e.g.

{% form_row_snippet field1 "field1" %}

This obviously isn't as general purpose, but has the advantage of being briefer.

Your second example could be done like this:

{% for list in people_lists %}
  {% include "people_lists" %}
{% endfor %}

You would have to set 'peoples_list' in your python code:
people_lists = [male_people_list, female_people_list]

Also check out the 'regroup' tag, which I think could be useful.

comment:5 by mordaha, 18 years ago

2Karsu: render() method like this doing things as i requested :)

template.resolve_variable() - is in django.template :)

    def render(self,context):
        var = template.resolve_variable(self.value, context)
        if var:
            context[self.varname] = var
        else:
            context[self.varname] = self.value
        return ''

comment:6 by Xin, 17 years ago

Summary: Feature request: new tag SET in template systemFeature request: new tag SET in template syste

mordaha,

Thanks for this solution. I needed it.

comment:7 by prz, 16 years ago

I actually ended up with this:

class SetVariable(Node):

def init(self, varname, nodelist):

self.varname = varname
self.nodelist = nodelist

def render(self,context):

context[self.varname] = self.nodelist.render(context)
return

@register.tag(name='setvar')
def setvar(parser, token):

"""
Set value to content of a rendered block.
{% setvar var_name %}

....

{% endsetvar
"""
try:

# split_contents() knows not to split quoted strings.
tag_name, varname = token.split_contents()

except ValueError:

raise template.TemplateSyntaxError, "%r tag requires a single argument for variable name" % token.contents.split()[0]

nodelist = parser.parse(('endsetvar',))
parser.delete_first_token()
return SetVariable(varname, nodelist)

This allows to set variables to funky translated things I needed, e.g.

{% setvar NotWellVar %}{{ _("Weltschmerz") }}{% endsetvar %}

comment:8 by prz, 16 years ago

ouch, forgot codeblock, sorry for that

class SetVariable(Node):
    def __init__(self, varname, nodelist):
        self.varname = varname
        self.nodelist = nodelist

    def render(self,context):
        context[self.varname] = self.nodelist.render(context) 
        return ''

@register.tag(name='setvar')
def setvar(parser, token):
    """
    Set value to content of a rendered block. 
    {% setvar var_name %}
     ....
    {% endsetvar
    """
    try:
        # split_contents() knows not to split quoted strings.
        tag_name, varname = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError, "%r tag requires a single argument for variable name" % token.contents.split()[0]

    nodelist = parser.parse(('endsetvar',))
    parser.delete_first_token()
    return SetVariable(varname, nodelist)

Note: See TracTickets for help on using tickets.
Back to Top