Páginas

Friday, August 14, 2009

Filtros personalizados no admin do Django

Então ontem estava eu pensando na melhor forma de colocar meu próprio filtro naquela caixa lateral da interface padrão da aplicação 'admin' do Django.

Num ModelAdmin você normalmente pode especificar um nome de um campo que faça parte do seu Model, mas não pode fazer por exemplo um filtro por um valor computado.

Eu já tinha começado por uma abordagem bem rasteira de estender os templates padrão e colocar meu filtro logo abaixo dos outros filtros (guardado numa shelf do Bazaar :P). Mas eis que a melhor solução até agora foi a do Marinho Brandão e do Semente no DjangoSnippets.

Fiz algumas poucas alterações para pegar do banco apenas valores distintos, e apesar de ser um ponto que pode prejudicar o desempenho, gero a lista a cada requisição -- caso contrário, a lista só será recomputada quando o servidor for reiniciado. Talvez fosse mais interessante gerar um lista fixa de opções (implicando nenhum acesso ao banco), ou mesmo ao invés de consultar N linhas do banco e remover valores duplicados, poderia partir de uma lista fixa e chegar se o banco contém resultados para o valor da lista.

# meu_projeto/minha_app/admin/filterspecs.py
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext as _


class AlphabeticFilterSpec(ChoicesFilterSpec):
"""
Adds filtering by first char (alphabetic style) of values in the admin
filter sidebar. Set the alphabetic filter in the model field attribute
'alphabetic_filter'.

my_model_field.alphabetic_filter = True

Based on http://www.djangosnippets.org/snippets/1051/
"""

def __init__(self, f, request, params, model, model_admin):
super(AlphabeticFilterSpec, self).__init__(f, request, params, model,
model_admin)
self.lookup_kwarg = '%s__istartswith' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
self.model = model

def choices(self, cl):
yield {'selected': self.lookup_val is None,
'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
'display': _('All')}

f = self.field
values_list = self.model.objects.distinct().values_list(f.name, flat=True)
# getting the first char of values
lookup_choices = sorted(set(val[0].lower() for val in values_list if val))

for val in lookup_choices:
yield {'selected': smart_unicode(val) == self.lookup_val,
'query_string': cl.get_query_string({self.lookup_kwarg: val}),
'display': val.upper()}


# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'alphabetic_filter', False),
AlphabeticFilterSpec))


E para ativar seu filtro para algum campo:

# meu_projeto/minha_app/models.py
# [...]

class Empresa(models.Model):
nome = models.CharField(max_length=64)
nome.alphabetic_filter = True
# [...]


# meu_projeto/minha_app/admin/__init__.py
# [...]
class EmpresaAdmin(admin.ModelAdmin):
list_display = ('nome', 'cnpj', 'inicio_convenio',
'_termino_convenio', 'natureza')
list_display_links = ('nome', 'cnpj')
list_filter = ('natureza', 'termino_convenio', 'inicio_convenio', 'nome')


# meu_projeto/urls.py
# Chama o código que registra o filtro personalizado
import sgtce.admin.filterspecs
# [...]

1 comment:

sabino diego said...
This comment has been removed by the author.