Wie füge ich meinem benutzerdefinierten Vorlagenfilter in einer Django-Vorlage mehrere Argumente hinzu?

87

Hier ist mein benutzerdefinierter Filter:

from django import template

register = template.Library()

@register.filter
def replace(value, cherche, remplacement):
    return value.replace(cherche, remplacement)

und hier sind die Möglichkeiten, wie ich versucht habe, es in meiner Vorlagendatei zu verwenden, was zu einem Fehler geführt hat:

{{ attr.name|replace:"_"," " }}
{{ attr.name|replace:"_" " " }}
{{ attr.name|replace:"_":" " }}
{{ attr.name|replace:"cherche='_', remplacement=' '" }}

Ich habe in die Dokumente und das Buch von Django geschaut, aber nur ein Beispiel mit einem einzigen Argument gefunden ... ist das überhaupt möglich?

bchhun
quelle

Antworten:

106

Es ist möglich und ziemlich einfach.

Django lässt nur ein Argument für Ihren Filter zu, aber es gibt keinen Grund, warum Sie nicht alle Argumente mit einem Komma in eine einzelne Zeichenfolge einfügen können, um sie zu trennen.

Wenn Sie beispielsweise einen Filter wünschen, der prüft, ob die Variable X in der Liste [1,2,3,4] enthalten ist, benötigen Sie einen Vorlagenfilter, der folgendermaßen aussieht:

{% if X|is_in:"1,2,3,4" %}

Jetzt können wir Ihr Templatetag folgendermaßen erstellen:

from django.template import Library

register = Library()

def is_in(var, args):
    if args is None:
        return False
    arg_list = [arg.strip() for arg in args.split(',')]
    return var in arg_list

register.filter(is_in)

Die Zeile, die arg_list erstellt, ist ein Generatorausdruck, der die args-Zeichenfolge in alle Kommas aufteilt und .strip () aufruft, um führende und nachfolgende Leerzeichen zu entfernen.

Wenn zum Beispiel das dritte Argument ein int ist, dann tun Sie einfach:

arg_list[2] = int(arg_list[2])

Oder wenn alle von ihnen Ints sind:

arg_list = [int(arg) for arg in args.split(',')]

BEARBEITEN: Um Ihre Frage jetzt gezielt zu beantworten, indem Sie Schlüssel-Wert-Paare als Parameter verwenden, können Sie dieselbe Klasse verwenden, die Django verwendet, um Abfragezeichenfolgen aus URLs zu analysieren. Dies hat dann auch den Vorteil, dass die Zeichencodierung entsprechend Ihrer settings.py richtig gehandhabt wird .

Wie bei Abfragezeichenfolgen wird jeder Parameter durch '&' getrennt:

{{ attr.name|replace:"cherche=_&remplacement= " }}

Dann sieht Ihre Ersetzungsfunktion nun folgendermaßen aus:

from django import template
from django.http import QueryDict

register = template.Library()

@register.filter
def replace(value, args):
    qs = QueryDict(args)
    if qs.has_key('cherche') and qs.has_key('remplacement'):
        return value.replace(qs['cherche'], qs['remplacement'])
    else:
        return value

Sie könnten dies beschleunigen, wenn das Risiko besteht, dass falsche Ersetzungen vorgenommen werden:

qs = QueryDict(args)
return value.replace(qs.get('cherche',''), qs.get('remplacement',''))
Van Gale
quelle
1
Wenn die Werte für diese in Variablen sind, wie dies implementiert werden soll ...?
Anto
2
Dies schien hilfreich zu sein, aber ich konnte es nicht zum Laufen bringen, wenn Variablen übergeben wurden. Dazu hatte ich ein tagoder simple_tag- verwendet, mit dem mehrere Variablen, sogar benannte Variablen, übergeben werden können.
Furbeenator
18

Gemäß diesem Abschnitt der Dokumentation nicht möglich :

Benutzerdefinierte Filter sind nur Python-Funktionen, die ein oder zwei Argumente annehmen:

  • Der Wert der Variablen (Eingabe) - nicht unbedingt eine Zeichenfolge.
  • Der Wert des Arguments - dies kann einen Standardwert haben oder ganz weggelassen werden.
Jeff Bauer
quelle
Der Ansatz von Van Gale funktioniert, wenn Sie fest codierte Zeichenfolgen verwenden. Das Django-Ticket [ code.djangoproject.com/ticket/1199] unterstützt mehrere Argumente in einem benutzerdefinierten Filter, und ein Patch wurde akzeptiert.
Jeff Bauer
16

So einfach ist das.

@register.filter(name='one_more')
def one_more(_1, _2):
    return _1, _2

def your_filter(_1_2, _3)
    _1, _2 = _1_2
    print "now you have three arguments, enjoy"

{{ _1|one_more:_2|your_filter:_3 }}
gshmu
quelle
Wirklich toll, danke für diese Lösung. Ich habe es ein wenig aktualisiert, damit Sie verschiedene Parameterlängen verketten können. gist.github.com/BrnoPCmaniak/e9552294b3059461f940a47143f58811
Filip Dobrovolný
1
Dies sollte die richtige Antwort sein! Es ist eine wunderschöne Python-Lösung (vielleicht nicht die beste Django-Lösung, siehe @ dragonroot-Antwort)
Antoine Draune
15

Registrieren Sie Ihr Tag anstelle eines Filters als einfaches Tag. Diese können mehrere Argumente annehmen. Die Syntax zum Aufrufen ist etwas anders, aber das ändert nur den syntaktischen Zucker.

Drachenwurzel
quelle
2
Dies war die richtige Antwort auf mein Problem. Um eine Vorlagenvariable an diese Funktion zu übergeben, musste ich a verwenden simple_tag.
Furbeenator
1
Dies ist eine gute Lösung. Es lohnt sich auf jeden Fall, die Django-Dokumente nach dem einfachen Tag zu durchsuchen
Gunther
4

Diese Funktion wurde 2013 in Djangos Trac als WONTFIX markiert: http://code.djangoproject.com/ticket/1199

bchhun
quelle
Dieses Ticket wurde letztes Jahr (2013) als WONTFIX geschlossen. Der Entwickler schlägt vor, ein benutzerdefiniertes Tag zu verwenden, wenn mehrere Argumente erforderlich sind.
Paul Lo
3

<my-site> /globaltags/replace.py

from django.template import Library

import re

register = Library()

def search(value, search):
    return re.sub(search, '#f4x@SgXXmS', value)

def replace(value, replace):
    return re.sub('#f4x@SgXXmS', replace, value)

register.filter(search)
register.filter(replace)

In der Vorlage:

{{ "saniel"|search:"s"|replace:"d" }}
theosp
quelle
Wäre schön wenn du #f4x@SgXXmSetwas erklären würdest ?
Dan Abramov
1
Nur eine zufällige Zeichenfolge, die als Platzhalter verwendet wird. Ich habe diese Zeichenfolge ausgewählt, weil ich geglaubt habe, dass sie nicht Teil der Eingabezeichenfolge ist. Wenn ich zum Beispiel "{}" anstelle von '# f4x @ SgXXmS' verwendet habe {{"benutze {} statt off []" | search: "off" | replace: "of"}} würde zurückgeben: "use of anstelle von [] "und nicht das erwartete Ergebnis:" benutze {} anstelle von [] "
theosp
5
Oh, das macht Sinn. Könnte schön sein, es als SUBSTRING_THAT_NEVER_OCCURSGedanken zu deklarieren .
Dan Abramov
2

Es ist einfacher als Sie denken.

Sie können dafür simple_tag verwenden .

@register.simple_tag
def multiple_args_tag(a, b, c, d):
   #do your stuff
   return 

In Vorlage :

{% multiple_args_tag 'arg1' 'arg2' 'arg3' 'arg4' %}

HINWEIS: Vergessen Sie nicht, den Server erneut auszuführen.

Sawan Chauhan
quelle
-1

Sie können dies einfach tun:

{% assign find_total_issued = dailysalesreport | find: "TotalIssued":"13" %}

public static List<object> Find(object collection, string column, string value)

Und es wird das Ziel erreichen, wie die Abstraktion der Funktion ist sjare.

Saad Hasnain Khan
quelle
-2

Hier ist eine schlechte Idee, aber funktioniert:

{{ xml|input_by_xpath:"{'type':'radio','xpath':'//result/value'}" }}

und

@register.filter
def input_by_xpath(device, args): 
    args = eval(args)
    ...
    result = "<input type=\"%s\" value=\"%s\" name=\"%s\"/>"%(args['type'],value,args['xpath'])
    return mark_safe(result)

quelle