¿Cómo agrego varios argumentos a mi filtro de plantilla personalizada en una plantilla de django?


89

Aquí está mi filtro personalizado:

from django import template

register = template.Library()

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

y estas son las formas en que intenté usarlo en mi archivo de plantilla que resultó en un error:

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

Busqué en los documentos y el libro de django, pero solo encontré un ejemplo con un solo argumento ... ¿es posible?

Respuestas:


108

Es posible y bastante simple.

Django solo permite un argumento para su filtro, pero no hay razón para que no pueda poner todos sus argumentos en una sola cadena usando una coma para separarlos.

Entonces, por ejemplo, si desea un filtro que verifique si la variable X está en la lista [1,2,3,4], querrá un filtro de plantilla que se vea así:

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

Ahora podemos crear su etiqueta de plantilla así:

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 línea que crea arg_list es una expresión generadora que divide la cadena de argumentos en todas las comas y llama a .strip () para eliminar los espacios iniciales y finales.

Si, por ejemplo, el tercer argumento es un int, simplemente haz lo siguiente:

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

O si todos ellos lo hacen:

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

EDITAR: ahora, para responder específicamente a su pregunta utilizando pares clave, valor como parámetros, puede usar la misma clase que usa Django para analizar cadenas de consulta de URL, que luego también tiene la ventaja de manejar la codificación de caracteres correctamente de acuerdo con su configuración.py .

Entonces, al igual que con las cadenas de consulta, cada parámetro está separado por '&':

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

Entonces su función de reemplazo ahora se verá así:

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

Puede acelerar esto con el riesgo de hacer algunos reemplazos incorrectos:

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

1
Si los valores de estos están en variables, ¿cómo implementar esto ...?
Anto

2
Esto pareció útil, pero no pude hacer que funcionara con las variables que se pasaban. Para hacerlo, utilicé un tago simple_tag, que permite pasar múltiples variables, incluso variables con nombre.
Furbeenator

18

No es posible según esta sección de los documentos:

Los filtros personalizados son solo funciones de Python que toman uno o dos argumentos:

  • El valor de la variable (entrada), no necesariamente una cadena.
  • El valor del argumento: puede tener un valor predeterminado o omitirse por completo.

El enfoque de Van Gale funcionará si está utilizando cadenas codificadas. El ticket de Django [ code.djangoproject.com/ticket/1199] admite varios argumentos en un filtro personalizado y se ha aceptado un parche.
Jeff Bauer

17

Es así de fácil.

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

Realmente muchas gracias por esta solución. Lo he actualizado un poco para que pueda encadenar diferentes longitudes de parámetros. gist.github.com/BrnoPCmaniak/e9552294b3059461f940a47143f58811
Filip Dobrovolný

1
¡Esta debería ser la respuesta correcta! Es una hermosa solución de Python (tal vez no sea la mejor solución de Django, vea la respuesta de @dragonroot)
Antoine Draune

15

En lugar de un filtro, registre su etiqueta como una simple etiqueta. Esos pueden tomar múltiples argumentos. La sintaxis para invocarlo será un poco diferente, pero eso es solo un cambio sintáctico de azúcar.


2
Esta fue la respuesta correcta para mi problema. Para pasar una variable de plantilla a esta función, tuve que usar un archivo simple_tag.
Furbeenator

1
Esta es una buena solución. Definitivamente vale la pena revisar los documentos de django para la etiqueta simple: docs.djangoproject.com/en/1.8/howto/custom-template-tags/…
Gunther

6

Es más simple de lo que crees.

Puedes usar simple_tag para esto.

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

En plantilla :

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

NOTA: No olvide volver a ejecutar el servidor.



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)

En la plantilla:

{{ "saniel"|search:"s"|replace:"d" }}

¿Sería bueno si explicaras un #f4x@SgXXmSpoco?
Dan Abramov

1
solo una cadena aleatoria utilizada como marcador de posición. Elegí esta cadena porque creí que no será parte de la cadena de entrada. Si, por ejemplo, he usado "{}" en lugar de '# f4x @ SgXXmS' {{"use {} en lugar de apagado []" | búsqueda: "apagado" | reemplazar: "de"}} devolvería: "uso de en lugar de [] "y no el resultado esperado:" use {} en lugar de [] "
theosp

5
Oh eso tiene sentido. Sería bueno declararlo como SUBSTRING_THAT_NEVER_OCCURSpensado.
Dan Abramov

-1

Simplemente puede hacer esto:

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

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

Y llegará al destino cuando la abstracción de la función sea sjare .


-2

Ésta es una mala idea pero funciona:

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

y

@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)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.