116 votes

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

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 modèle qui ont abouti à une erreur :

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

J'ai regardé dans La documentation de django y livre mais je n'ai trouvé qu'un exemple utilisant un seul argument... est-ce même possible ?

122voto

Van Gale Points 21982

C'est possible et assez simple.

Django n'autorise qu'un seul argument à votre filtre, mais il n'y a aucune raison pour 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 aurez besoin d'un filtre modèle 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 génératrice qui divise la chaîne args sur toutes les virgules et appelle .strip() pour supprimer les espaces de début et de fin.

Si, par exemple, le troisième argument est un int, il suffit de faire :

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

Ou si tous sont des ints, faites-le :

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

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

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

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

Votre fonction de remplacement ressemblera alors à 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 un peu le processus au risque de faire des remplacements incorrects :

qs = QueryDict(args)
return value.replace(qs.get('cherche',''), qs.get('remplacement',''))

1 votes

Si les valeurs de ces éléments sont dans des variables, comment mettre cela en œuvre ?

2 votes

Cela semblait utile, mais je n'ai pas réussi à le faire fonctionner avec des variables passées. Pour ce faire, j'ai dû utiliser un tag o simple_tag - qui permet de passer plusieurs variables, même des variables nommées.

2 votes

Je suis loin de l'avenir, mais il me semble qu'une balise serait une meilleure solution que d'écrire essentiellement un analyseur syntaxique dans le filtre.

31voto

gshmu Points 1

C'est simple comme bonjour.

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

0 votes

Vraiment génial merci pour cette solution. Je l'ai un peu améliorée pour que vous puissiez enchaîner différentes longueurs de paramètres. gist.github.com/BrnoPCmaniak/e9552294b3059461f940a47143f5881‌​1

4 votes

Cela devrait être la bonne réponse ! C'est une belle solution python (peut-être pas la meilleure solution django, voir la réponse de @dragonroot).

21voto

dragonroot Points 1850

Au lieu d'un filtre, enregistrez votre balise comme une balise simple. Celles-ci peuvent prendre plusieurs arguments. La syntaxe pour l'invoquer sera un peu différente, mais c'est juste un changement de sucre syntaxique.

2 votes

C'était la bonne réponse à mon problème. Afin de passer une variable de modèle dans cette fonction, j'ai dû utiliser une fonction simple_tag .

1 votes

C'est une bonne solution. Cela vaut vraiment la peine de consulter la documentation de django pour la balise simple : docs.djangoproject.com/fr/1.8/howto/custom-template-tags/

0 votes

C'est ce qui me semble le plus logique. Les balises sont plus puissantes et complexes que les filtres, donc si votre problème est plus complexe, cela semble être une meilleure approche que d'essayer de faire rentrer une cheville carrée dans un trou rond.

17voto

Jeff Bauer Points 6598

Pas possible selon cet article des docs :

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

  • La valeur de la variable (entrée) -- pas nécessairement une chaîne de caractères.
  • La valeur de l'argument -- elle peut avoir une valeur par valeur par défaut, ou être complètement ignorée.

0 votes

L'approche de Van Gale fonctionnera si vous utilisez des chaînes codées en dur. Ticket Django [ [code.djangoproject.com/ticket/1199]](https://code.djangoproject.com/ticket/1199]) prend en charge les arguments multiples dans un filtre personnalisé et un correctif a été accepté.

3voto

theosp Points 2763

<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" }}

0 votes

Ce serait bien si vous expliquiez #f4x@SgXXmS un peu ?

1 votes

Juste une chaîne aléatoire utilisée comme substitut. J'ai choisi cette chaîne parce que je pense qu'elle ne fera pas partie de la chaîne d'entrée. Si par exemple j'avais utilisé "{}" au lieu de '#f4x@SgXXmS' {{"use {} instead off []"|search : "off"|replace : "of" }} retournerait : "use of instead of []" et non le résultat attendu : "utiliser {} au lieu de []"

6 votes

Oh, c'est logique. Ce serait bien de le déclarer comme SUBSTRING_THAT_NEVER_OCCURS pensée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X