Django UtilidadesMarinho Brandão

Abr/08 11

Ajax ManyToManyField Widget

django
Publicado há 4 meses, 2 semanas por Marinho Brandao

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.

http://marinho.webdoisonline.com/blog/p/152/?img=1

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

Links sociais


PyConBrasil 2008

Gabeira para prefeito do Rio

Comentários


Escreva o seu


.net adoradores ajax android apple banco de dados blogosfera brasil django emprego família gadgets google inovação java linux lua microsoft musica opensocial opinião publicidade python rails religiao screencast seguranca software-livre tdd web windows yadsel

Artigos recentes