Есть ли тег шаблона Django, который позволяет мне установить переменную контекста?

Я хочу иметь возможность устанавливать переменные в шаблоне в строковые значения. Я написал тег, но он, похоже, не меняет контекст. Предполагаемое использование:

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

Ответ 1

Вам не нужно писать свой собственный тег. Это делает {% with %}.

Ответ 2

Django уже рассмотрел этот частный случай и предоставляет теги привязки, специальный способ регистрации тегов, которые устанавливают переменную в контекст.

В этом случае вам не нужно заботиться о поиске, обновлении и сохранении контекста. Вы просто делаете это:

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

И вы можете использовать его так же, но он намного чище:

{% define "a string" as my_var %}

Это весь необходимый код.

EDIT: Как отметил Дирк Бергстром, поскольку версия django 1.9 assignment_tag устарела. simple_tag - идеальная замена.

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

Ответ 3

Ответ скрыт внутри более сложный пример current_time в документации.

Проблема

Вы хотите добавить переменную в контекст. Но вы не хотите возвращаться и добавлять эту переменную ко всем представлениям, которые вызывают все шаблоны, вызывающие тег. Вам просто нужен тег, который может добавить некоторые данные в контекст, где бы он ни захотел. Я искал такие вещи, когда делал эти случайные отвлекающие факторы, которые попадают в боковые панели, и конкретно не связаны с работой основного вида, например.

Метод

Чтобы ввести переменную в контекст, вам нужен доступ к контексту. Для этого пользовательский тег будет вводить node, который добавляет данные в контекст шаблона.

Пример

В этом примере добавляется запрос "coming_events" в контекст, затем выполняется цикл по каждому результату. Он делает это, объявляя пользовательский тег, который отображает node, который добавляет запрос в контекст.

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 как events%}, посмотрите внимательно на пример в документации и обратите внимание, как они разделяют токен на то, что перед "как", а затем и использовать последнюю часть, чтобы назвать переменную контекста. Вы должны были бы реализовать это.

Обратите внимание, что если я закончил помещать HTML для каждого события в свой собственный выделенный шаблон, мне было бы лучше просто следовать стандарту стандартного тега включения документация. Это решение предлагается, когда вам нужны данные без какого-либо багажа.

Ответ 4

Если вы хотите, чтобы переменная была доступна в других блоках шаблонов, вы должны посмотреть http://od-eon.com/blogs/liviu/scope-variables-template-blocks/. В частности, в коде пользовательского тэга вы должны заменить:

context[some_var_name] = some_val

с:

context.dicts[0][some_var_name] = some_val

Это сделает трюк (хотя это, возможно, уродливый трюк, и вы должны рассмотреть альтернативы).

Ответ 5

Прежде всего, вы обычно хотите установить переменные контекста в своем представлении. Ввод логики в шаблон - действительно формула для добавленного беспорядка. Тем не менее, настало время, когда вы хотите использовать это, а тэг {% 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 first argument indeciperable" % tag_name
  return define_node(ret, key, parse)

Ответ 7

Вы можете использовать ответ kiril. Это довольно просто. Вы также можете использовать тег set_context django-libs.

Пример:

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