ChatGPT解决这个技术问题 Extra ChatGPT

How to set a value of a variable inside a template code?

Say I have a template

<html>
<div>Hello {{name}}!</div>
</html>

While testing it, it would be useful to define the value of the variable without touching the python code that invokes this template. So I'm looking for something like this

{% set name="World" %}     
<html>
<div>Hello {{name}}!</div>
</html>

Does something like this exists in Django?


J
John

You can use the with template tag.

{% with name="World" %}     
<html>
<div>Hello {{name}}!</div>
</html>
{% endwith %}

but can you change the variable's value in the with?
It seems you cannot declare a container (I've tried list and tuple) in a with clause
If you need to declare a list, use make_list. docs.djangoproject.com/en/1.9/ref/templates/builtins/#make-list
Jinja says it's {% set myvar=value %} why it doesn't work in django?
@holms Because Django doesn't use Jinja :-) docs.djangoproject.com/en/1.7/topics/templates
J
John R Perry

Create a template tag:

The app should contain a templatetags directory, at the same level as models.py, views.py, etc. If this doesn’t already exist, create it - don’t forget the __init__.py file to ensure the directory is treated as a Python package.

Create a file named define_action.py inside of the templatetags directory with the following code:

from django import template
register = template.Library()

@register.simple_tag
def define(val=None):
  return val

Note: Development server won’t automatically restart. After adding the templatetags module, you will need to restart your server before you can use the tags or filters in templates.

Then in your template you can assign values to the context like this:

{% load define_action %}
{% if item %}

   {% define "Edit" as action %}

{% else %}

   {% define "Create" as action %}

{% endif %}


Would you like to {{action}} this item?

in my case after loop this returns old value :(
In the latest version it appears that you can use simple_tag instead of assignment_tag (and it worked for me).
Issue I got with this solution is that it appears, that you cannot override values.
if you want to use this technique to set a list instead of just a value, check this: stackoverflow.com/a/34407158/2193235
if you are setting the variable as an integer and you want to increment it (for example), you need to use add: {% define counter|add:1 as counter %}. Similarly for other operations.
L
Lundis

An alternative way that doesn't require that you put everything in the "with" block is to create a custom tag that adds a new variable to the context. As in:

class SetVarNode(template.Node):
    def __init__(self, new_val, var_name):
        self.new_val = new_val
        self.var_name = var_name
    def render(self, context):
        context[self.var_name] = self.new_val
        return ''

import re
@register.tag
def setvar(parser,token):
    # This version uses a regular expression to parse tag contents.
    try:
        # Splitting by None == splitting by spaces.
        tag_name, arg = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError, "%r tag requires arguments" % token.contents.split()[0]
    m = re.search(r'(.*?) as (\w+)', arg)
    if not m:
        raise template.TemplateSyntaxError, "%r tag had invalid arguments" % tag_name
    new_val, var_name = m.groups()
    if not (new_val[0] == new_val[-1] and new_val[0] in ('"', "'")):
        raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name
    return SetVarNode(new_val[1:-1], var_name)

This will allow you to write something like this in your template:

{% setvar "a string" as new_template_var %}

Note that most of this was taken from here


How about assigning variables to other variables present in the context? And on a different note: allowing templates to arbitrarily assign context variables without checking if they exist already may have security implications. A more sensible approach in my opinion would be to check the context for the variable before attempting to assign it:
if context.get(self.var_name): raise SuspiciousOperation("Attempt to assign variable from template already present in context")
d
djvg

There are tricks like the one described by John; however, Django's template language by design does not support setting a variable (see the "Philosophy" box in Django documentation for templates).
Because of this, the recommended way to change any variable is via touching the Python code.


Thanks for the pointer. From a perspective of a designer is it sometimes easier to quickly set a variable to test various states of a page while designing it. Not suggesting this practice to be used in a running code.
the "with" tag is accepted in django1.0. So looks like they are finally amending their philosophy :).
As a matter of facts, the "with" tag is just for aliases. This may have a huge impact on performance (and on readability as well!) but it is not really setting a variable in traditional programming terms.
M
Mr. Lance E Sloan

The best solution for this is to write a custom assignment_tag. This solution is more clean than using a with tag because it achieves a very clear separation between logic and styling.

Start by creating a template tag file (eg. appname/templatetags/hello_world.py):

from django import template

register = template.Library()

@register.simple_tag
def get_addressee():
    return "World"

Now you may use the get_addressee template tag in your templates:

{% load hello_world %}

{% get_addressee as addressee %}

<html>
    <body>
        <h1>hello {{addressee}}</h1>
    </body>
</html>

For folks using the newer Django versions, its called simple_tag now! Save the time to figure out why "register.." is not recognized in your code...
J
John Mee

Perhaps the default template filter wasn't an option back in 2009...

<html>
<div>Hello {{name|default:"World"}}!</div>
</html>

I must say that this is what I was looking! It can be also be used with with: {% with state=form.state.value|default:other_context_variable %} instead of other_context_variable we can also use any 'string_value' as well
But it will print it, and I need to save it for later use
S
Sarang

This is not a good idea in general. Do all the logic in python and pass the data to template for displaying. Template should be as simple as possible to ensure those working on the design can focus on design rather than worry about the logic.

To give an example, if you need some derived information within a template, it is better to get it into a variable in the python code and then pass it along to the template.


A
Ashish Gupta

In your template you can do like this:

{% jump_link as name %}
{% for obj in name %}
    <div>{{obj.helo}} - {{obj.how}}</div>
{% endfor %}

In your template-tags you can add a tag like this:

@register.assignment_tag
def jump_link():
    listArr = []
    for i in range(5):
        listArr.append({"helo" : i,"how" : i})
    return listArr

r
ramwin

Use the with statement.

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

I can't imply the code in first paragraph in this answer. Maybe the template language had deprecated the old format.