Comment ajouter plusieurs arguments à mon filtre de modèle personnalisé dans un modèle django?

89

Voici mon filtre personnalisé:

from django import template

register = template.Library()

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

et voici les façons dont j'ai essayé de l'utiliser dans mon fichier de modèle qui a entraîné une erreur:

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

J'ai regardé dans les documents et le livre de django mais je n'ai trouvé qu'un exemple en utilisant un seul argument ... est-ce même possible?

bchhun
la source

Réponses:

108

C'est possible et assez simple.

Django n'autorise qu'un seul argument dans votre filtre, mais il n'y a aucune raison que vous ne puissiez pas mettre tous vos arguments dans une seule chaîne en utilisant une virgule pour les séparer.

Ainsi, par exemple, si vous voulez un filtre qui vérifie si la variable X est dans la liste [1,2,3,4], vous voudrez un modèle de filtre qui ressemble à ceci:

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

Maintenant, nous pouvons créer votre templatetag comme ceci:

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)

La ligne qui crée arg_list est une expression de générateur qui divise la chaîne args sur toutes les virgules et appelle .strip () pour supprimer tous les espaces de début et de fin.

Si, par exemple, le 3ème argument est un int alors faites simplement:

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

Ou si tous sont des entiers, faites-le:

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

EDIT: maintenant pour répondre spécifiquement à votre question en utilisant des paires clé / valeur comme paramètres, vous pouvez utiliser la même classe que Django utilise pour analyser les chaînes de requête à partir d'URL, ce qui a également l'avantage de gérer correctement le codage des caractères en fonction de vos paramètres.py .

Ainsi, comme pour les chaînes de requête, chaque paramètre est séparé par '&':

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

Ensuite, votre fonction de remplacement ressemblera maintenant à ceci:

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

Vous pourriez accélérer cela au risque de faire des remplacements incorrects:

qs = QueryDict(args)
return value.replace(qs.get('cherche',''), qs.get('remplacement',''))
Van Gale
la source
1
Si les valeurs pour ceux-ci sont dans des variables, comment implémenter cela ...?
Anto
2
Cela semblait utile, mais je ne pouvais pas le faire fonctionner avec des variables passées. Pour ce faire, j'avais utilisé un tagor simple_tag- qui permet de passer plusieurs variables, même des variables nommées.
Furbeenator
18

Pas possible selon cette section de la documentation:

Les filtres personnalisés ne sont que des fonctions Python qui prennent un ou deux arguments:

  • La valeur de la variable (entrée) - pas nécessairement une chaîne.
  • La valeur de l'argument - cela peut avoir une valeur par défaut, ou être complètement omis.
Jeff Bauer
la source
L'approche de Van Gale fonctionnera si vous utilisez des chaînes codées en dur. Django ticket [ code.djangoproject.com/ticket/1199] prend en charge plusieurs arguments dans un filtre personnalisé et un correctif a été accepté.
Jeff Bauer
17

C'est facile comme ça.

@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
la source
Vraiment super merci pour cette solution. Je l'ai un peu amélioré pour que vous puissiez enchaîner différentes longueurs de paramètres. gist.github.com/BrnoPCmaniak/e9552294b3059461f940a47143f58811
Filip Dobrovolný
1
Cela devrait être la bonne réponse! C'est une belle solution python (peut-être pas la meilleure solution django, voir la réponse @dragonroot)
Antoine Draune
15

Au lieu d'un filtre, enregistrez votre balise comme une simple balise. Ceux-ci peuvent prendre plusieurs arguments. La syntaxe pour l'invoquer sera un peu différente, mais ce n'est que du sucre syntaxique.

dragonroot
la source
2
C'était la bonne réponse à mon problème. Afin de passer une variable de modèle dans cette fonction, j'ai dû utiliser un fichier simple_tag.
Furbeenator
1
C'est une bonne solution. Il vaut vraiment la peine de consulter la documentation django pour la balise simple: docs.djangoproject.com/en/1.8/howto/custom-template-tags
Gunther
6

C'est plus simple que vous ne le pensez.

Vous pouvez utiliser simple_tag pour cela.

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

Dans le modèle :

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

REMARQUE: n'oubliez pas de réexécuter le serveur.

Sawan Chauhan
la source
4

Cette fonctionnalité a été marquée comme WONTFIX dans Trac de Django 2013: http://code.djangoproject.com/ticket/1199

bchhun
la source
Ce ticket a été fermé comme WONTFIX l'année dernière (2013), leur développeur suggère d'utiliser une balise personnalisée si besoin de plusieurs arguments.
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)

Dans le modèle:

{{ "saniel"|search:"s"|replace:"d" }}
theosp
la source
Ce serait bien si vous expliquiez un #f4x@SgXXmSpeu?
Dan Abramov
1
juste une chaîne aléatoire utilisée comme espace réservé. J'ai choisi cette chaîne parce que je pensais qu'elle ne ferait pas partie de la chaîne d'entrée. Si, par exemple, j'ai utilisé "{}" au lieu de '# f4x @ SgXXmS' {{"utilisez {} au lieu de []" | search: "off" | replace: "of"}} renverrait: "utilisation de au lieu de [] "et non le résultat attendu:" use {} au lieu de [] "
theosp
5
Oh, cela a du sens. Ça pourrait être bien de le déclarer comme SUBSTRING_THAT_NEVER_OCCURSpensé.
Dan Abramov
-1

Vous pouvez simplement faire ceci:

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

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

Et il atteindra la destination comme l'est l'abstraction de la fonction sjare.

Saad Hasnain Khan
la source
-2

Heres une mauvaise idée mais fonctionne:

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

et

@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)

la source