是否有Django模板标签可以让我设置上下文变量?

时间:2010-04-02 10:58:57

标签: django django-templates

我希望能够将模板中的变量设置为字符串值。我写了一个标签,但它似乎没有改变上下文。预期用途是:

{% define "a string" as my_var %}

更新(已解决):

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)

7 个答案:

答案 0 :(得分:16)

Django已经考虑了这个特例,并提供了assigment tags一种特殊的方式来注册在上下文中设置变量的标记。

在这种情况下,您不需要关心检索,更新和保存上下文。你只需这样做:

@register.assignment_tag
def define(the_string):
  return the_string

你可以用同样的方式使用它,但它更干净

{% define "a string" as my_var %}

这是您需要的所有代码。

编辑: 正如Dirk Bergstrom指出的那样,因为版本django 1.9 assignment_tag已被弃用。 simple_tag是一个完美的替代品。

@register.simple_tag
def define(the_string):
  return the_string

答案 1 :(得分:11)

答案隐藏在the more complex current_time example in the documentation内。

<强>问题

您想要将变量添加到上下文中。但是您不想返回并将该变量添加到调用调用标记的所有模板的所有视图中。你只需要一个标签,它可以将一些数据添加到上下文的任何地方。我正在寻找这种东西,当渲染那些被放入侧边栏并且与主视图的工作没有特别关联的随机干扰时,例如。

方式

要将变量注入上下文,您需要访问上下文。为此,您的自定义标记将注入一个节点,该节点将数据添加到模板上下文中。

示例

此示例向上下文添加“coming_events”查询集,然后遍历每个结果。它通过声明一个自定义标记来实现这一点,该标记呈现一个节点,该节点将查询集添加到上下文中。

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 ''

你会这样使用它:

{% load events %}
{% coming_events %}
{% for event in coming_events %}
<div class="eventItem">
   <p>{{event.title}} {{event.data}}</p>
</div>
{% endfor %}

额外信用

如果您真的希望能够任意命名变量,例如{%coming_events 事件%},那么请仔细查看文档中的示例,并注意他们如何将令牌拆分为什么在'as'和之后的内容之前,使用后一部分来命名上下文变量。你必须实现它。

请注意,如果我将每个事件的HTML放入其自己的专用模板中,那么我最好只关注standard inclusion tag example in the documentation。建议您使用此解决方案,以便在没有任何包袱的情况下获取数据。

答案 2 :(得分:8)

如果您希望变量在其他模板块中可用,则应该查看http://od-eon.com/blogs/liviu/scope-variables-template-blocks/。特别是,在自定义标记代码中,您应该替换:

context[some_var_name] = some_val

使用:

context.dicts[0][some_var_name] = some_val

这样就可以了(虽然它可能是一个丑陋的伎俩,你应该考虑替代方案)。

答案 3 :(得分:4)

您无需编写自己的标记。内置的{% with %}代码执行此操作。

答案 4 :(得分:3)

首先,您通常希望在视图中设置上下文变量。将逻辑放在模板中实际上是一个增加混乱的公式。也就是说,有一段时间你想要使用它,并且{%with%}标记会弄乱一些东西,因为你必须以{%endwith%}结束它,丢失变量。我遇到的问题是我传递一个值时不能包含模板。我想这样做:

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

{% include "some_template_partial.html" %}

如果没有重复代码,则无法使用{%with%}标记:

{% 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 %}

现在很好,但随着病例的增加,这会变成一个可怕的混乱。因此编写了这段代码:

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)

答案 5 :(得分:2)

您可以使用自定义模板标记来设置变量,如下所示:

您在templatetags文件夹中创建一个名为set_var.py的文件,其中包含以下代码:

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)

然后在模板中使用它,您只需执行以下操作:

{% 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}}

答案 6 :(得分:2)

您可以使用kiril的答案。这很简单。您还可以使用django-libsset_context标记。

示例:

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