Is there a Django template tag that lets me set a context variable?

断了今生、忘了曾经 提交于 2021-02-04 12:07:52

问题


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)

回答1:


You don't need to write your own tag. The built-in {% with %} tag does this.




回答2:


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



回答3:


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.




回答4:


If you want the variable to be available in other template blocks, you should look at http://od-eon.com/blogs/liviu/scope-variables-template-blocks/ . In particular, in the custom tag code, you should replace:

context[some_var_name] = some_val

with:

context.dicts[0][some_var_name] = some_val

That will do the trick (though it is possibly an ugly trick, and you should consider alternatives).




回答5:


First of all, you generally want to set context variables within your view. Putting logic in the template is really a formula for added mess. That said, there does come a time when you want to use this, and the {% with %} tag makes a mess of things since you HAVE to end it with a {% endwith %}, losing the variable. The problem I ran into is that I can't include a template while passing it a value. I'd like to do:

{% if criteria %}
  {% define 'foo' as some_option %}
{% else %}
  {% define 'bar' as some_option %}
{% endif %}

{% include "some_template_partial.html" %}

This is impossible to do using {% with %} tags without having repeated code:

{% if criteria %}
  {% with 'foo' as some_option %}
    {% include "some_template_partial.html" %}
  {% endwith %}
{% else %}
  {% with 'bar' as some_option %}
    {% include "some_template_partial.html" %}
  {% endwith %}
{% endif %}

Fine as it is now, but this will degrade into a horrible mess as cases proliferate. Thus this code was written:

from django import template
from django.conf import settings
import logging
import re
register = template.Library()

NAMESPACE_PROTECTION = settings.DEBUG

class define_node(template.Node):
  def __init__(self, value, key, parse):
    self.value = value
    self.key = key
    self.parse = parse
  def render(self, context):
    if NAMESPACE_PROTECTION:
      if self.key in context:
        raise Exception("EPIC NAMESPACE FAIL, CONTEXT HAZ A %s" % self.key)
    if self.parse:
      context[self.key] = context[self.value]
    else:
      context[self.key] = self.value
    return ''

@register.tag
def define(parser, token):
  """Definition template tag. Use to define variables in your context within the template.
  Sorta like the {% with "blah" as blah %} tag, but without the {% endwith %} mess.

  Supports two modes:
  Literal mode: argument is encapsulated with quotes (e.g. "blah" or 'blah')
                variable, is set to the string literal, ex:
                {% define "fish" as foo %}
  Variable mode: argument is prefixed with a $ (e.g. $blah or $monkey)
                 variable is copied from another context variable, ex:
                 {% define $fish as foo %}

  Namespace protection is also provided if django.conf.settings.DEBUG is True.
  You will get an epic namespace fail if that occurs (please fix it before you deploy)

  TODO:
    * define override nomenclature if you REALLY want to overwrite a variable
      - should decide what nomeclature to use first
    * expand on variables so that {% define $array.blah as foo %} will work
      (this currently WILL NOT)
  """
  try:
    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
  value, key = m.groups()
  if (value[0] == value[-1] and value[0] in ('"', "'")):
    ret = value[1:-1]
    parse = False
  elif (value[0] == '$'):
    ret = value[1:]
    parse = True
  else:
    raise template.TemplateSyntaxError, "%r tag's first argument indeciperable" % tag_name
  return define_node(ret, key, parse)



回答6:


You can use kiril's answer. It's quite simple. You can also use the set_context tag of django-libs.

Example:

{% set_context foo.bar|filter_foo as foobar %}



回答7:


You can use custom template tags to set variables like so:

You create a file called set_var.py in your templatetags folder that contains the following code:

from django import template

register = template.Library()

class SetVarNode(template.Node):

    def __init__(self, var_name, var_value):
        self.var_name = var_name
        self.var_value = var_value

    def render(self, context):
        try:
            value = template.Variable(self.var_value).resolve(context)
        except template.VariableDoesNotExist:
            value = ""
        context[self.var_name] = value
        return u""

def set_var(parser, token):
    """
        {% set <var_name>  = <var_value> %}
    """
    parts = token.split_contents()
    if len(parts) < 4:
        raise template.TemplateSyntaxError("'set' tag must be of the form:  {% set <var_name>  = <var_value> %}")
    return SetVarNode(parts[1], parts[3])

register.tag('set', set_var)

Then to use this in your template, you just do the following:

{% load set_var %}

{% set a = 3 %}
{% set b = some_context_variable %}
{% set c = "some string" %}

Value of a is {{a}}
Value of b is {{b}}
Value of c is {{c}}


来源:https://stackoverflow.com/questions/2566265/is-there-a-django-template-tag-that-lets-me-set-a-context-variable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!