Opened 9 years ago

Closed 9 years ago

Last modified 8 years ago

#1322 closed enhancement (wontfix)

Feature request: new tag SET in template syste

Reported by: mordaha Owned by: adrian
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: UI/UX:

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 Changed 9 years ago by Karsu

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 Changed 9 years ago by adrian

  • Resolution set to wontfix
  • Status changed from new to closed

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 Changed 9 years ago by mordaha

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 Changed 9 years ago by lukeplant

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 Changed 9 years ago by mordaha

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 Changed 8 years ago by Xin

  • Summary changed from Feature request: new tag SET in template system to Feature request: new tag SET in template syste

mordaha,

Thanks for this solution. I needed it.

comment:7 Changed 8 years ago by prz

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 Changed 8 years ago by prz

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