Como adiciono vários argumentos ao meu filtro de modelo personalizado em um modelo django?

91

Este é meu filtro personalizado:

from django import template

register = template.Library()

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

e aqui estão as maneiras que tentei usá-lo em meu arquivo de modelo que resultou em um erro:

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

Eu olhei os documentos e o livro de django, mas só encontrei exemplos usando um único argumento ... é mesmo possível?

bchhun
fonte

Respostas:

108

É possível e bastante simples.

Django permite apenas um argumento para o seu filtro, mas não há razão para você não colocar todos os seus argumentos em uma única string usando uma vírgula para separá-los.

Então, por exemplo, se você quiser um filtro que verifique se a variável X está na lista [1,2,3,4], você vai querer um filtro de modelo semelhante a este:

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

Agora podemos criar sua templatetag assim:

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)

A linha que cria arg_list é uma expressão geradora que divide a string args em todas as vírgulas e chama .strip () para remover todos os espaços à esquerda e à direita.

Se, por exemplo, o terceiro argumento for um int, basta fazer:

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

Ou se todos eles forem ints, faça:

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

EDITAR: agora, para responder especificamente à sua pergunta usando pares de chave e valor como parâmetros, você pode usar a mesma classe que o Django usa para analisar strings de consulta de URL, que também tem o benefício de lidar com a codificação de caracteres adequadamente de acordo com suas configurações.py .

Portanto, como acontece com as strings de consulta, cada parâmetro é separado por '&':

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

Em seguida, sua função de substituição ficará assim:

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

Você pode acelerar um pouco com o risco de fazer algumas substituições incorretas:

qs = QueryDict(args)
return value.replace(qs.get('cherche',''), qs.get('remplacement',''))
Van Gale
fonte
1
Se os valores para estes estiverem em variáveis, como implementar isso ...?
Anto
2
Isso pareceu útil, mas não consegui fazer funcionar com as variáveis ​​sendo passadas. Para fazer isso, usei tagou simple_tag- que permite a passagem de várias variáveis, até mesmo variáveis ​​nomeadas.
Furbeenator
18

Não é possível de acordo com esta seção dos documentos:

Filtros personalizados são apenas funções Python que aceitam um ou dois argumentos:

  • O valor da variável (entrada) - não necessariamente uma string.
  • O valor do argumento - pode ter um valor padrão ou ser totalmente omitido.
Jeff Bauer
fonte
A abordagem de Van Gale funcionará se você estiver usando strings embutidas em código. Django ticket [ code.djangoproject.com/ticket/1199] suporta múltiplos argumentos em um filtro customizado e um patch foi aceito.
Jeff Bauer,
17

É fácil assim.

@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
fonte
Muito obrigado por esta solução. Eu o atualizei um pouco para que você possa encadear diferentes comprimentos de parâmetros. gist.github.com/BrnoPCmaniak/e9552294b3059461f940a47143f58811
Filip Dobrovolný
1
Esta deve ser a resposta correta! É uma bela solução python (talvez não seja a melhor solução django, veja a resposta @dragonroot)
Antoine Draune
15

Em vez de um filtro, registre sua tag como uma tag simples. Esses podem ter vários argumentos. A sintaxe para invocá-lo será um pouco diferente, mas isso é apenas uma mudança sintática.

dragonroot
fonte
2
Esta foi a resposta correta para o meu problema. Para passar uma variável de modelo para esta função, tive que usar um simple_tag.
Furbeenator
1
Esta é uma boa solução. Definitivamente, vale a pena verificar a documentação do django para a tag simples: docs.djangoproject.com/en/1.8/howto/custom-template-tags/…
Gunther
6

É mais simples do que você pensa.

Você pode usar simple_tag para isso.

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

No modelo :

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

NOTA: Não se esqueça de executar novamente o servidor.

Sawan Chauhan
fonte
4

Este recurso foi marcado como WONTFIX em 2013 Django's Trac: http://code.djangoproject.com/ticket/1199

bchhun
fonte
Esse tíquete foi fechado como WONTFIX no ano passado (2013), seu desenvolvedor sugere o uso de uma tag personalizada se precisar de vários argumentos.
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)

No modelo:

{{ "saniel"|search:"s"|replace:"d" }}
theosp
fonte
Seria bom se você explicasse um #f4x@SgXXmSpouco?
Dan Abramov,
1
apenas uma string aleatória usada como espaço reservado. Eu escolhi esta string porque acreditei que ela não faria parte da string de entrada. Se, por exemplo, eu usei "{}" em vez de '# f4x @ SgXXmS' {{"use {} em vez de off []" | search: "off" | replace: "of"}} retornaria: "uso de em vez de [] "e não o resultado esperado:" use {} em vez de [] "
theosp
6
Oh, isso faz sentido. Pode ser bom declará-lo como SUBSTRING_THAT_NEVER_OCCURSpensamento.
Dan Abramov,
-1

Você pode simplesmente fazer isso:

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

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

E chegará ao destino como a abstração da função sjare.

Saad Hasnain Khan
fonte
-2

É uma má ideia, mas funciona:

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

e

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

fonte