2010-04-02 9 views
10

Voglio essere in grado di impostare le variabili in un modello per i valori stringa. Ho scritto un tag, ma non sembra cambiare il contesto. La destinazione d'uso è:C'è un tag modello Django che mi consente di impostare una variabile di contesto?

{% define "a string" as my_var %} 

Update (risolto):

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

Sembra buono con me. Ho un tag simile, l'unica differenza è che ho usato register.tag ('define', define), ma l'annotazione non dovrebbe fare la differenza che ti aspetteresti. –

+0

Forse stai incontrando un conflitto di nomi? 'definire' sembra pericoloso ... –

risposta

5

Non è necessario scrivere il proprio tag. Il tag integrato {% with %} esegue questa operazione.

+0

Sì hai ragione. Una cosa che questo tag fa in modo diverso è che non usa e termina il tag.Il nome viene inserito nel contesto per il resto del modello. – hekevintran

+5

Sono sorpreso che questo ha un segno di spunta poiché non affronta il problema: non vedo "con" la creazione di una nuova variabile di contesto; si rinomina solo uno esistente per un momento. –

+7

Questa non è una soluzione alla domanda. "with" rinomina temporaneamente una variabile solo all'interno di una sezione limitata di un modello. Non aggiunge la nuova variabile al contesto più ampio, come chiede la domanda. – Cerin

3

Prima di tutto, in genere si desidera impostare variabili di contesto all'interno della visualizzazione. Mettendo la logica nel modello è davvero una formula per il caos aggiunto. Detto questo, arriva un momento in cui vuoi usare questo e il tag {% with%} fa un casino di cose dato che DEVI terminarlo con un {% endwith%}, perdendo la variabile. Il problema che ho riscontrato è che non posso includere un modello durante il suo passaggio. Mi piacerebbe fare:

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

{% include "some_template_partial.html" %} 

Questo è impossibile da fare utilizzando {% con%} tag senza dover codice ripetuto:

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

bene così com'è ora, ma questo sarà degradarsi in un pasticcio orribile mentre i casi proliferano. Così questo codice è stato scritto:

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

In realtà, il tuo esempio può essere fatto in una sola riga: {% include "some_template_partial.html" con some_option = criteri | yesno: "foo, bar"%} – Alan

9

La risposta è sepolto all'interno the more complex current_time example in the documentation.

Problema

si desidera aggiungere una variabile al contesto. Ma non vuoi tornare indietro e aggiungere quella variabile a tutte le viste che chiamano tutti i modelli che invocano il tag. Vuoi solo un tag che può aggiungere alcuni dati al contesto ovunque lo desideri. Sto cercando questo tipo di cose quando faccio quelle distrazioni casuali che vengono rilasciate nelle barre laterali e non sono specificamente correlate al lavoro della vista principale, per esempio.

Metodo

Per iniettare una variabile al contesto è necessario accedere al contesto. Per fare ciò il tuo tag personalizzato inietterà un nodo che ha aggiunto i dati al contesto del modello.

Esempio

Questo esempio aggiunge un queryset "coming_events" al contesto poi passanti sopra ogni risultato. Lo fa dichiarando un tag personalizzato che esegue il rendering di un nodo che aggiunge un queryset al contesto.

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

usereste in questo modo:

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

Credit Extra

Se siete veramente entusiasta di essere in grado di nominare la variabile arbitrariamente ad esempio {% coming_events come eventi%}, quindi guarda attentamente l'esempio nella documentazione e osserva come suddividono il token in quello che precede il 'come' e ciò che viene dopo e usa la seconda parte per denominare la variabile di contesto. Dovresti implementarlo.

Nota che se finissi di inserire l'HTML per ogni evento nel suo modello dedicato, sarei molto meglio seguire lo standard inclusion tag example in the documentation. Questa soluzione è consigliata per quando vuoi i dati senza bagaglio.

+0

Grazie per questa risposta approfondita, mi ha davvero aiutato. Sono sorpreso che questo non sia un problema più comune con una soluzione più semplice. – Joseph

8

Se si desidera che la variabile sarà disponibile in altri blocchi di template, si dovrebbe guardare a http://od-eon.com/blogs/liviu/scope-variables-template-blocks/. In particolare, nel codice tag personalizzato, è necessario sostituire:

context[some_var_name] = some_val 

con:

context.dicts[0][some_var_name] = some_val 

che farà il trucco (anche se è forse un brutto scherzo, e si dovrebbe prendere in considerazione alternative).

+0

questo mi ha davvero salvato! grazie mille! – dmitko

15

Django ha già preso in considerazione questo caso particolare e fornisce lo assigment tags, un modo speciale di registrare tag che impostano una variabile nel contesto.

In questo caso, non è necessario preoccuparsi di recuperare, aggiornare e salvare il contesto. È sufficiente fare questo:

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

E si può utilizzare lo stesso modo, ma è molto più pulito :

{% define "a string" as my_var %} 

questo tutto il codice necessario.

MODIFICA: Come sottolineato da Dirk Bergstrom, poiché la versione django 1.9 assignment_tag è obsoleta. simple_tag è un sostituto perfetto.

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

Tutte le altre risposte non sono aggiornate. Se stai cercando una soluzione, usa questo! –

+1

Funziona alla grande con Django 1.10. Tieni presente che non utilizzare il valore "as" mostrerà semplicemente il valore, quindi puoi ancora modificare @ register.simple_tag in @ register.assignment_tag e non dovrai modificare i luoghi in cui lo utilizzi come {% your_tag 'something'% } come questo mostrerà il valore. – mrmuggles

+2

assignment_tag è obsoleto dal 1.9. simple_tag è la nuova hotness: https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/#django.template.Library.simple_tag –

2

È possibile utilizzare la risposta di kiril. È abbastanza semplice Puoi anche utilizzare il tag set_context di django-libs.

Esempio:

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

Mi è piaciuto molto questo. ma sfortunatamente django-libs non gestiva correttamente verbatim quindi dovevo rimuoverlo e usare la risposta di @ kiril sopra – Nimo

Problemi correlati