2010-04-02 12 views
10

Quiero ser capaz de establecer variables en una plantilla para valores de cadena. Escribí una etiqueta, pero no parece cambiar el contexto. El uso previsto es:¿Hay una etiqueta de plantilla de Django que me permita establecer una variable de contexto?

{% define "a string" as my_var %} 

Update (resuelto):

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) 
+0

ve bien para mí. Tenga una etiqueta similar, la única diferencia es que utilicé register.tag ('define', define), pero la anotación no debería marcar la diferencia que usted esperaría. –

+0

¿Quizás te encuentres con un conflicto de nombres? 'definir' parece peligroso ... –

Respuesta

5

No es necesario para escribir su propia etiqueta. La etiqueta incorporada {% with %} hace esto.

+0

Sí, tienes razón. Una cosa que esta etiqueta hace de manera diferente es que no usa y termina la etiqueta.El nombre se pone en el contexto para el resto de la plantilla. – hekevintran

+5

Me sorprende que esto tenga un tic ya que no soluciona el problema: no veo 'con' la creación de una nueva variable de contexto; solo cambia el nombre de uno existente por un momento. –

+7

Esto no es una solución a la pregunta. "con" cambia temporalmente el nombre de una variable solo dentro de una sección limitada de una plantilla. No agrega la nueva variable al contexto más amplio, como pregunta la pregunta. – Cerin

3

En primer lugar, generalmente desea establecer variables de contexto dentro de su vista. Poner la lógica en la plantilla es realmente una fórmula para un desorden añadido. Dicho esto, llega un momento en que quieres usar esto, y la etiqueta {% with%} hace un lío de cosas ya que TIENES que terminar con un {% endwith%}, perdiendo la variable. El problema con el que me encontré es que no puedo incluir una plantilla mientras le paso un valor. Me gustaría hacer:

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

{% include "some_template_partial.html" %} 

Ésta es imposible hacer uso de {%}% con etiquetas sin necesidad de código repetidos:

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

bien como está ahora, pero esto degradará en una horrible desastre a medida que proliferan los casos. Así, este código fue escrito:

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) 
+0

En realidad, su ejemplo se puede hacer en una sola línea: {% include "some_template_partial.html" con some_option = criteria | yesno: "foo, bar"%} – Alan

9

La respuesta está enterrado en el interior the more complex current_time example in the documentation.

Problema

desea agregar una variable al contexto. Pero no desea volver y agregar esa variable a todas las vistas que llaman a todas las plantillas que invocan la etiqueta. Solo quiere una etiqueta que pueda agregar algunos datos al contexto donde quiera. Estoy buscando este tipo de cosas cuando represento esas distracciones aleatorias que se dejan caer en las barras laterales y no están específicamente relacionadas con el trabajo de la vista principal, por ejemplo.

Método

para inyectar una variable al contexto es necesario tener acceso al contexto. Para hacer eso, su etiqueta personalizada inyectará un nodo que agregó los datos al contexto de la plantilla.

Ejemplo

este ejemplo se agrega un queryset "coming_events" al contexto entonces bucles sobre cada resultado. Lo hace al declarar una etiqueta personalizada que representa un nodo que agrega un conjunto de consulta al contexto.

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

Se podría utilizar de esta manera:

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

Crédito extra

Si usted está realmente interesado en ser capaz de nombrar la variable arbitraria por ejemplo, {% coming_events como eventos%} luego observe de cerca el ejemplo en la documentación y observe cómo dividen el token en lo que está antes del 'como' y lo que está después y use la última parte para nombrar la variable de contexto. Tendrás que implementar eso.

Tenga en cuenta que si acabo poniendo el HTML para cada evento en su propia plantilla dedicada, entonces sería mejor solo seguir el standard inclusion tag example in the documentation. Esta solución se sugiere para cuando quiere los datos sin ningún tipo de equipaje.

+0

Gracias por esta minuciosa respuesta, realmente me ayudó. Me sorprende que este no sea un problema más común con una solución más simple. – Joseph

8

Si desea que la variable esté disponible en otros bloques de la plantilla, usted debe buscar en http://od-eon.com/blogs/liviu/scope-variables-template-blocks/. En particular, en el código de la etiqueta personalizada, debe reemplazar:

context[some_var_name] = some_val 

con:

context.dicts[0][some_var_name] = some_val 

que va a hacer el truco (aunque es posiblemente una mala pasada, y se debe considerar alternativas).

+0

¡esto realmente me salvó! ¡Muchas gracias! – dmitko

15

Django ya consideró este caso particular y proporciona el assigment tags, una forma especial de registrar etiquetas que establecen una variable en el contexto.

En este caso, no necesita preocuparse por recuperar, actualizar y guardar el contexto. Sólo tiene que hacer esto:

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

Y se puede usar de la misma manera, pero es mucho más limpio :

{% define "a string" as my_var %} 

Esta todo el código que necesita.

EDITAR: Como señaló Dirk Bergstrom, ya que la versión django 1.9 assignment_tag está obsoleta. simple_tag es un reemplazo perfecto.

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

Todas las otras respuestas están desactualizadas. Si está buscando una solución, ¡use esta! –

+1

Funciona muy bien con Django 1.10. Tenga en cuenta que no usar el 'como' simplemente mostrará el valor, por lo que aún puede cambiar @ register.simple_tag a @ register.assignment_tag y no tendrá que cambiar los lugares donde lo usa como {% your_tag 'something'% } ya que esto mostrará el valor. – mrmuggles

+2

assignment_tag está en desuso desde 1.9. simple_tag es el nuevo hotness: https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/#django.template.Library.simple_tag –

2

Puede usar la respuesta de kiril. Es bastante simple. También puede usar la etiqueta set_context de django-libs.

Ejemplo:

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

Me encantó esto. pero lamentablemente django-libs no manejaba textualmente correctamente, así que tuve que eliminarlo y usar la respuesta de @ kiril arriba – Nimo

Cuestiones relacionadas