Ajax ManyToManyField Widget
Os campos ManyToManyField são uma beleza: permitem que se selecione diversos registros extrangeiros para um único campo, possibilitando campos clássicos onde normalmente seria necessária uma nova classe com tratamento e manutenção particulares.
No entanto, esse tipo de campo traz um problema consigo: é que o SelectMultiple [1] carrega todas as opções disponíveis para o usuário, o que no caso de grande quantidade de registros, cria uma imensa estrutura em HTML capaz de travar até o mais rápido dos navegadores.
O widget FilteredSelectMultiple [2] oferece alguma organização para isso, mas ainda assim, a carga é a mesma ou ainda maior.
O novo admin, do branch newforms-admin [4] oferece o ManyToManyRawIdWidget [3], muito leve, mas nada amigável, considerando que este exibe somente o código dos itens selecionados, o que não ajuda de forma alguma o usuário final.
Com um problema para resolver num trabalho onde nos aproximamos de uma base de 20 mil clientes, surgiu a idéia para o Ajax ManyToManyField Widget, um nome meio D. Pedro I, mas que traduz exatamente o que é: uma alternativa leve sem se tornar estranha ao usuário: exibe-se um campo de auto-complete em Ajax, e à medida que o usuário escolhe o item desejado, clica-se no íconde com sinal de "+" e este é adicionado a uma lista logo acima.
Para isso, fiz uso de do plugin de auto-complete [5] disponível para jQuery [6], criado por Dylan Verheul. Com algumas modificações. Após uma conversa com o mesmo, eu soube que as minhas modificações se tornaram desnecessárias ante à nova versão disponível - farei a adaptação em breve.
Verifique os exemples disponíveis, e os arquivos estáticos utilizados. É necessário o jQuery, de preferência em sua versão mais recente.
Por fim, é importante ressaltar os dois elementos principais do widget:
auto_complete_view
Esta é uma generic view necessária para a disponibilização dos dados desejados em formato separado por pipe (|) - este é o formato utilizado pelo plugin de auto-complete - com possibilidade de pesquisa por quantos campos forem necessários:
from django.conf.urls.defaults import *
from apps.utils.ajax_m2m_widget import auto_complete_view
from models import Spare, Category
def spare_auto_query_func(q):
return Spare.objects.filter(name__startswith=q)
def category_auto_query_func(q):
return Category.objects.filter(name__startswith=q)
urlpatterns = patterns('apps.spares.views',
(r'^spare/auto/$', auto_complete_view, {'query_func': spare_auto_query_func, 'desc_field': 'name', 'id_field': 'id'}),
(r'^category/auto/$', auto_complete_view, {'query_func': category_auto_query_func, 'desc_field': 'name', 'id_field': 'id'}),
)AjaxMultiSelect
Este é o widget a ser utilizado no admin (que seja on branch newforms-admin) ou em algum form do padrão NewForms. Veja o exemplo abaixo no Admin do newforms-admin:
from django.contrib import admin
from django.contrib.admin.options import ModelAdmin
from apps.spares.models import Category, Spare
from apps.utils.ajax_m2m_widget import AjaxMultiSelect
class SpareAdmin(ModelAdmin):
list_display = ('name','manufacturer','description')
list_display_links = ('name',)
list_filter = ('manufacturer',)
raw_id_fields = ('manufacturer','specification_sheet',)
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'categories':
kwargs['widget'] = AjaxMultiSelect(db_field.verbose_name, Category, '/spares/category/auto/', attrs={'style': 'margin-left: 105px;'})
elif db_field.name == 'equivalents':
kwargs['widget'] = AjaxMultiSelect(db_field.verbose_name, Spare, '/spares/spare/auto/', attrs={'style': 'margin-left: 105px;'})
if 'widget' in kwargs:
return db_field.formfield(**kwargs)
else:
return super(SpareAdmin, self).formfield_for_dbfield(db_field, **kwargs)
admin.site.register(Category)
admin.site.register(Spare, SpareAdmin)Agora um exemplo em form:
from django import newforms as forms
from apps.spares.models import Category, Spare
from ajax_m2m_widget import AjaxMultiSelect
class MyForm(forms.Form):
spares = forms.Field(widget=AjaxMultiSelect(db_field.verbose_name, Spare, '/spares/spare/auto/'))
categories = forms.Field(widget=AjaxMultiSelect(db_field.verbose_name, Category, '/spares/category/auto/'))O download está disponível em [7] (o Google fez o favor de bloquear minha conta para criação de novos projetos pelas próximas 24 horas por motivos que só deus sabe).
Links relacionados
| [1] | http://code.djangoproject.com/browser/django/trunk/django/newforms/widgets.py#L245 |
| [2] | http://code.djangoproject.com/browser/django/branches/newforms-admin/django/contrib/admin/widgets.py#L12 |
| [3] | http://code.djangoproject.com/browser/django/branches/newforms-admin/django/contrib/admin/widgets.py#L112 |
| [4] | http://code.djangoproject.com/wiki/NewformsAdminBranch |
| [5] | http://code.google.com/p/jquery-autocomplete/ |
| [6] | http:/jquery.com/ |
| [7] | http://django-plus.googlecode.com/files/ajax_m2m_widget.rar |
Marinho Brandão