2010-04-02 4 views
10

Ich möchte Variablen in einer Vorlage auf String-Werte setzen können. Ich habe ein Tag geschrieben, aber es scheint den Kontext nicht zu ändern. Die bestimmungsgemäße Verwendung ist:Gibt es ein Django Template-Tag, mit dem ich eine Kontextvariable setzen kann?

{% define "a string" as my_var %} 

Update (gelöst):

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

mir sieht gut aus. Haben Sie ein ähnliches Tag, nur Unterschied, dass ich register.tag ('definieren', definieren) verwendet habe, aber Annotation sollte keinen Unterschied machen, den Sie erwarten würden. –

+0

Vielleicht geraten Sie in einen Namenskonflikt? 'definieren' scheint gefährlich ... –

Antwort

5

Sie brauchen nicht Ihre eigenen Tag zu schreiben. Das eingebaute {% with %} Tag tut dies.

+0

Ja, Sie haben Recht. Eine Sache, die dieses Tag anders macht, ist, dass es das Tag nicht verwendet und beendet.Der Name wird in den Kontext für den Rest der Vorlage eingefügt. – hekevintran

+5

Ich bin überrascht, dass dies ein Häkchen hat, da es das Problem nicht anspricht: Ich sehe 'mit' keine neue Kontextvariable erstellen; es benennt nur einen vorhandenen für einen Moment um. –

+7

Dies ist keine Lösung für die Frage. "mit" benennt eine Variable nur innerhalb eines begrenzten Abschnitts einer Vorlage temporär um. Es fügt die neue Variable nicht dem größeren Kontext hinzu, wie die Frage fragt. – Cerin

3

Zunächst möchten Sie in der Regel Kontextvariablen in Ihrer Sicht festlegen. Putting Logik in der Vorlage ist wirklich eine Formel für zusätzliche Chaos. Das heißt, es kommt eine Zeit, wenn Sie diese verwenden möchten, und das {% mit%} -Tag macht ein Chaos der Dinge, da Sie es mit einem {% endwith%} beenden müssen, die Variable zu verlieren. Das Problem, auf das ich gestoßen bin, ist, dass ich keine Vorlage einfügen kann, während ich einen Wert übergebe. Ich würde gerne tun:

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

{% include "some_template_partial.html" %} 

Dies ist unmöglich {% mit%} zu tun, mit Hilfe von Tags ohne wiederholten 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 %} 

Fein wie es jetzt ist, aber das wird degradieren in eine schreckliche Unordnung als Fälle vermehren sich. So wurde dieser Code geschrieben:

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

Eigentlich kann Ihr Beispiel in nur einer Zeile erfolgen: {% "some_template_partial.html" mit some_option = Kriterien | yesno: "foo, bar"%} – Alan

9

Die Antwort ist innerhalb the more complex current_time example in the documentation begraben.

Problem

Sie möchten eine Variable auf den Kontext hinzuzufügen. Aber Sie möchten nicht zurückgehen und diese Variable allen Ansichten hinzufügen, die alle Vorlagen aufrufen, die das Tag aufrufen. Sie möchten nur ein Tag, das dem Kontext einige Daten hinzufügen kann, wo immer es gewünscht wird. Ich suche nach solchen Dingen, wenn ich diese zufälligen Ablenkungen rendere, die in Seitenleisten fallen gelassen werden und nicht speziell mit der Arbeit der Hauptansicht verwandt sind.

Methode

Um eine Variable auf den Kontext injizieren Sie den Zugriff auf den Kontext benötigen. Dazu fügt Ihr benutzerdefiniertes Tag einen Knoten ein, der die Daten dem Vorlagenkontext hinzufügt.

Beispiel

diesem Beispiel wird eine „coming_events“ queryset auf den Kontext Schleifen dann über jedes Ergebnis. Dies geschieht, indem ein benutzerdefiniertes Tag deklariert wird, das einen Knoten rendert, der dem Kontext ein Abfrageset hinzufügt.

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

würden Sie es wie folgt verwenden:

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

Extra Credit

Wenn Sie wirklich daran interessiert sind in der Lage sein, die Variable zu nennen beliebig zB {% coming_events als events%} dann schauen Sie sich das Beispiel in der Dokumentation genau an und beachten Sie, wie sie das Token in das, was vor dem 'as' und was danach ist, aufteilen und den letzten Teil verwenden, um die Kontextvariable zu benennen. Du müsstest das umsetzen.

Beachten Sie, dass wenn ich den HTML für jedes Ereignis in eine eigene dedizierte Vorlage setzen würde, wäre ich besser dran, nur die standard inclusion tag example in the documentation folgen. Diese Lösung wird empfohlen, wenn Sie die Daten ohne Gepäck haben möchten.

+0

Dank für diese gründliche Antwort, hat mir wirklich geholfen. Ich bin überrascht, dass dies kein häufigeres Problem mit einer einfacheren Lösung ist. – Joseph

8

Wenn Sie die Variable wollen in anderen Template-Blöcke zur Verfügung stehen, können Sie bei http://od-eon.com/blogs/liviu/scope-variables-template-blocks/ aussehen sollte. Insbesondere in dem benutzerdefinierten Tag-Code, sollten Sie ersetzen:

context[some_var_name] = some_val 

mit:

context.dicts[0][some_var_name] = some_val 

, die den Trick tun wird (obwohl es vielleicht ein hässlicher Trick ist, und Sie sollten Alternativen in Betracht ziehen).

+0

das hat mich wirklich gerettet! ich danke dir sehr! – dmitko

15

Django hat diesen speziellen Fall bereits berücksichtigt und bietet die assigment tags, eine spezielle Möglichkeit zum Registrieren von Tags, die eine Variable in den Kontext setzen.

In diesem Fall müssen Sie sich nicht um das Abrufen, Aktualisieren und Speichern des Kontexts kümmern. Sie tun einfach:

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

Und Sie können es genau die gleiche Art und Weise verwenden, aber es ist viel sauberer :

{% define "a string" as my_var %} 

Das gesamte Code Sie benötigen.

EDIT: Wie Dirk Bergstrom wies darauf hin, seit Version django 1.9 assignment_tag ist veraltet. simple_tag ist ein perfekter Ersatz.

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

Alle anderen Antworten sind veraltet. Wenn Sie nach einer Lösung suchen, verwenden Sie diese! –

+1

Funktioniert gut mit Django 1.10. Beachten Sie, dass die Verwendung des 'as' den Wert einfach anzeigt. Sie können also @ register.simple_tag in @ register.assignment_tag ändern und müssen die Orte, an denen Sie sie verwenden, nicht ändern, wie {% your_tag 'irgendwas'% }, da dies den Wert anzeigen wird. – mrmuggles

+2

assignment_tag ist seit 1.9 veraltet. simple_tag ist die neue Schärfe: https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/#django.template.Library.simple_tag –

2

Sie können kirils Antwort verwenden. Es ist ganz einfach. Sie können auch das set_context-Tag von django-libs verwenden.

Beispiel:

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

hat es geliebt. aber django-libs hat leider nicht richtig mit wörtlich umgehen können, also musste ich es entfernen und die obige Antwort von @ kiril verwenden – Nimo

Verwandte Themen