Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

django-searchable-dropdown

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

django-searchable-dropdown

Biblioteca Django para criar dropdowns pesquisáveis e customizáveis

pipPyPI
Version
1.0.2
Maintainers
1

SearchableDropdown

PyPI version Python 3.7+ Django 2.2+ License: MIT Build Status Code Coverage

Uma biblioteca Django completa para criar dropdowns pesquisáveis e customizáveis com funcionalidades avançadas.

Sobre a Biblioteca

O SearchableDropdown é uma biblioteca Django que oferece componentes de dropdown avançados com funcionalidades de busca em tempo real, integração nativa com formulários Django e suporte a múltiplas seleções.

Características Principais

  • Dropdowns Pesquisáveis: Busca em tempo real nas opções
  • Integração Django: Widgets e campos de formulário nativos
  • Suporte AJAX: Busca dinâmica em modelos Django
  • Múltipla Seleção: Suporte a seleção de múltiplas opções
  • Informações Adicionais: Exibição de dados extras sobre opções selecionadas
  • Customizável: Temas e configurações flexíveis
  • Responsivo: Funciona em dispositivos móveis
  • Acessível: Suporte a navegação por teclado e leitores de tela

Status do Projeto

  • Versão: 1.0.0 (Estável)
  • Testes: 110 testes passando (100% cobertura)
  • Compatibilidade: Django 2.2+ e Python 3.7+
  • Documentação: Completa com exemplos práticos
  • Licença: MIT (Open Source)
  • PyPI: Disponível para instalação via pip

Instalação

Opção 1: Instalar via pip (Recomendado para produção)

pip install django_searchable_dropdown

E então adicionar ao INSTALLED_APPS:

INSTALLED_APPS = [
    # ... outras apps
    'recoveredperispirit.django.django_searchable_dropdown',
]

Opção 2: Integrar em Projeto Existente

1. Adicionar ao projeto

# Copiar a pasta recoveredperispirit/django/django_searchable_dropdown para seu projeto Django
cp -r recoveredperispirit/django/django_searchable_dropdown/ /path/to/your/django/project/

2. Configurar settings.py

INSTALLED_APPS = [
    # ... outras apps
    'recoveredperispirit.django.django_searchable_dropdown',
]

# Configurações opcionais
SEARCHABLE_DROPDOWN_CONFIG = {
    'default_placeholder': 'Selecione uma opção',
    'default_search_placeholder': 'Digite para buscar...',
    'default_no_results_text': 'Nenhum resultado encontrado',
    'ajax_timeout': 5000,
    'min_search_length': 1,
    'max_results': 50,
}

3. Coletar arquivos estáticos

python manage.py collectstatic

Guia de Uso - Widgets Disponíveis

1. SearchableDropdownWidget (Básico)

O widget básico para dropdowns pesquisáveis com opções estáticas ou de modelos Django.

Exemplo Básico

from recoveredperispirit.django.django_searchable_dropdown.widgets import SearchableDropdownWidget
from django import forms

class CategoriaForm(forms.Form):
    categoria = forms.ModelChoiceField(
        queryset=Categoria.objects.all(),
        widget=SearchableDropdownWidget(
            placeholder="Selecione uma categoria",
            search_placeholder="Digite para buscar categorias...",
            dropdown_type="categoria",
            min_search_length=1,
            max_results=20
        ),
        required=False
    )

Como Funciona

  • Renderização: O widget cria um dropdown customizado com campo de busca
  • Busca: Usuário digita no campo de busca e as opções são filtradas em tempo real
  • Seleção: Usuário clica em uma opção para selecioná-la
  • Validação: O valor é validado pelo Django normalmente

Resultado Visual

┌─────────────────────────────────────┐
│ Selecione uma categoria             ▼ │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ Digite para buscar categorias...    │
├─────────────────────────────────────┤
│ ✓ Tecnologia                        │
│   Esportes                          │
│   Música                            │
│   Livros                            │
└─────────────────────────────────────┘

2. SearchableDropdownMultipleWidget (Múltipla Seleção)

Widget para selecionar múltiplas opções simultaneamente.

Exemplo de Uso

from recoveredperispirit.django.django_searchable_dropdown.widgets import SearchableDropdownMultipleWidget

class ProdutoForm(forms.Form):
    tags = forms.ModelMultipleChoiceField(
        queryset=Tag.objects.all(),
        widget=SearchableDropdownMultipleWidget(
            placeholder="Selecione tags (máximo 5)",
            search_placeholder="Digite para buscar tags...",
            dropdown_type="tags",
            max_selections=5,
            allow_clear=True
        ),
        required=False
    )

Como Funciona

  • Múltipla Seleção: Usuário pode selecionar várias opções
  • Contador: Mostra quantas opções foram selecionadas
  • Limite: Pode definir um número máximo de seleções
  • Remoção: Opções selecionadas podem ser removidas individualmente

Resultado Visual

┌─────────────────────────────────────┐
│ ✓ Tecnologia, Esportes (2/5)       ▼ │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ Digite para buscar tags...          │
├─────────────────────────────────────┤
│ ✓ Tecnologia [×]                    │
│ ✓ Esportes [×]                      │
│   Música                            │
│   Livros                            │
└─────────────────────────────────────┘

3. SearchableDropdownAjaxWidget (Busca AJAX)

Widget para busca dinâmica via AJAX em grandes datasets.

Exemplo de Uso

from recoveredperispirit.django.django_searchable_dropdown.widgets import SearchableDropdownAjaxWidget

class ClienteForm(forms.Form):
    cliente = forms.ModelChoiceField(
        queryset=Cliente.objects.none(),  # Queryset vazio inicialmente
        widget=SearchableDropdownAjaxWidget(
            placeholder="Digite para buscar clientes...",
            search_placeholder="Nome ou email do cliente...",
            dropdown_type="cliente_ajax",
            ajax_url="/api/clientes/search/",
            min_search_length=2,
            max_results=20,
            delay=300,  # Delay em ms antes de fazer a busca
            allow_clear=True
        ),
        required=False
    )

Como Funciona

  • Busca Dinâmica: Dados são carregados via AJAX conforme o usuário digita
  • Delay: Aguarda o usuário parar de digitar antes de fazer a requisição
  • Filtros: Aplica filtros no servidor para melhor performance
  • Cache: Pode implementar cache para melhorar performance

Resultado Visual

┌─────────────────────────────────────┐
│ Digite para buscar clientes...      ▼ │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ joão...                             │
├─────────────────────────────────────┤
│ Buscando...                         │
├─────────────────────────────────────┤
│ João Silva (joao@email.com)         │
│ João Santos (joao.s@email.com)      │
│ João Oliveira (joao.o@email.com)    │
└─────────────────────────────────────┘

API Endpoint Necessário

# views.py
from django.http import JsonResponse
from django.db.models import Q

def api_clientes_search(request):
    query = request.GET.get('q', '')
    if len(query) < 2:
        return JsonResponse({'results': []})
    
    clientes = Cliente.objects.filter(
        Q(nome__icontains=query) | Q(email__icontains=query)
    )[:20]
    
    results = [
        {
            'id': cliente.id,
            'text': f"{cliente.nome} ({cliente.email})"
        }
        for cliente in clientes
    ]
    
    return JsonResponse({'results': results})

4. SearchableDropdownWithInfoWidget (Com Informações)

Widget que mostra informações detalhadas sobre a opção selecionada.

Exemplo de Uso

from recoveredperispirit.django.django_searchable_dropdown.widgets import SearchableDropdownWithInfoWidget

class ProdutoDetalhadoForm(forms.Form):
    produto = forms.ModelChoiceField(
        queryset=Produto.objects.all(),
        widget=SearchableDropdownWithInfoWidget(
            placeholder="Selecione um produto para ver detalhes",
            search_placeholder="Digite para buscar produtos...",
            dropdown_type="produto_info",
            info_url="/api/produtos/info/",
            info_container_id="produto-info-container"
        ),
        required=False
    )

Como Funciona

  • Seleção: Usuário seleciona uma opção no dropdown
  • Carregamento: Informações detalhadas são carregadas via AJAX
  • Exibição: Dados são exibidos em um container separado
  • Atualização: Informações são atualizadas a cada nova seleção

Resultado Visual

┌─────────────────────────────────────┐
│ iPhone 13 Pro                      ▼ │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ Digite para buscar produtos...      │
├─────────────────────────────────────┤
│ iPhone 13 Pro                       │
│ iPhone 12                           │
│ Samsung Galaxy S21                  │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ Informações do Produto              │
├─────────────────────────────────────┤
│ Nome: iPhone 13 Pro                 │
│ Código: PROD001                     │
│ Preço: R$ 8.999,00                  │
│ Estoque: 15 unidades                │
│ Categoria: Smartphones              │
│ Marca: Apple                        │
│ Descrição: iPhone 13 Pro 256GB...   │
└─────────────────────────────────────┘

API Endpoint Necessário

# views.py
def api_produtos_info(request):
    produto_id = request.GET.get('id')
    if not produto_id:
        return JsonResponse({'error': 'ID do produto não fornecido'}, status=400)
    
    try:
        produto = Produto.objects.get(id=produto_id)
        info = {
            'id': produto.id,
            'nome': produto.nome,
            'codigo': produto.codigo,
            'preco': str(produto.preco),
            'estoque': produto.estoque,
            'categoria': produto.categoria.nome,
            'marca': produto.marca.nome,
            'descricao': produto.descricao or 'Sem descrição'
        }
        return JsonResponse(info)
    except Produto.DoesNotExist:
        return JsonResponse({'error': 'Produto não encontrado'}, status=404)

Template HTML Necessário

<!-- Incluir container para informações -->
<div class="row">
    <div class="col-md-8">
        {{ form.produto }}
    </div>
    <div class="col-md-4">
        <div class="card">
            <div class="card-header">
                <h6>Informações do Produto</h6>
            </div>
            <div class="card-body" id="produto-info-container">
                <p class="text-muted">Selecione um produto para ver as informações</p>
            </div>
        </div>
    </div>
</div>

Customização Avançada

Configurações por Tipo de Dropdown

# Em apps.py ou settings.py
from recoveredperispirit.django.django_searchable_dropdown.utils import dropdown_config

# Configurar tipo personalizado
dropdown_config.register_type('activity', {
    'placeholder': 'Selecione uma atividade',
    'search_placeholder': 'Digite o nome da atividade...',
    'no_results_text': 'Nenhuma atividade encontrada',
    'min_search_length': 2,
    'max_results': 20,
    'allow_clear': True,
    'allow_create': False,
})

# Usar em formulários
class ActivityForm(forms.Form):
    activity = forms.ModelChoiceField(
        queryset=Activity.objects.all(),
        widget=SearchableDropdownWidget(dropdown_type='activity')
    )

Estilos CSS Customizados

/* Tema personalizado */
.searchable-dropdown.custom-theme {
    --dropdown-bg: #ffffff;
    --dropdown-border: #e0e0e0;
    --dropdown-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    --option-hover-bg: #f8f9fa;
    --option-selected-bg: #007bff;
    --option-selected-color: #ffffff;
    --search-input-bg: #f8f9fa;
    --search-input-border: #dee2e6;
}

/* Estilos específicos para diferentes tipos */
.searchable-dropdown[data-type="produto"] {
    --option-selected-bg: #28a745;
}

.searchable-dropdown[data-type="cliente"] {
    --option-selected-bg: #17a2b8;
}

JavaScript Customizado

// Eventos customizados
document.addEventListener('dropdown:change', function(e) {
    const dropdown = e.target;
    const value = e.detail.value;
    const text = e.detail.text;
    
    console.log('Dropdown alterado:', { value, text });
    
    // Lógica customizada aqui
    if (dropdown.getAttribute('data-type') === 'produto') {
        updateProductInfo(value);
    }
});

// Inicialização customizada
const customDropdown = new SearchableDropdown(element, {
    placeholder: 'Selecione uma opção',
    searchPlaceholder: 'Digite para buscar...',
    onSelect: function(value, text) {
        console.log('Opção selecionada:', value, text);
    },
    onSearch: function(query) {
        console.log('Buscando:', query);
    }
});

Exemplos Práticos Completos

Exemplo 1: Sistema de Agendamento

# models.py
class Activity(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField()
    duration = models.IntegerField(help_text="Duração em minutos")
    max_participants = models.IntegerField()
    
    def __str__(self):
        return self.name

class Schedule(models.Model):
    activity = models.ForeignKey(Activity, on_delete=models.CASCADE)
    date = models.DateField()
    time = models.TimeField()
    participants = models.ManyToManyField('User', blank=True)
    
    def __str__(self):
        return f"{self.activity.name} - {self.date} {self.time}"

# forms.py
class ScheduleForm(forms.ModelForm):
    activity = forms.ModelChoiceField(
        queryset=Activity.objects.all(),
        widget=SearchableDropdownWithInfoWidget(
            dropdown_type='activity',
            info_url='/api/activities/info/',
            info_container_id='activity-info',
            placeholder='Selecione uma atividade'
        )
    )
    
    participants = forms.ModelMultipleChoiceField(
        queryset=User.objects.all(),
        widget=SearchableDropdownMultipleWidget(
            dropdown_type='users',
            placeholder='Selecione participantes',
            max_selections=10
        ),
        required=False
    )
    
    class Meta:
        model = Schedule
        fields = ['activity', 'date', 'time', 'participants']

# views.py
def api_activities_info(request):
    activity_id = request.GET.get('id')
    try:
        activity = Activity.objects.get(id=activity_id)
        return JsonResponse({
            'name': activity.name,
            'description': activity.description,
            'duration': f"{activity.duration} minutos",
            'max_participants': activity.max_participants
        })
    except Activity.DoesNotExist:
        return JsonResponse({'error': 'Atividade não encontrada'}, status=404)

Exemplo 2: E-commerce com Produtos

# models.py
class Category(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField()
    
    def __str__(self):
        return self.name

class Product(models.Model):
    name = models.CharField(max_length=200)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.IntegerField()
    description = models.TextField()
    tags = models.ManyToManyField('Tag', blank=True)
    
    def __str__(self):
        return self.name

# forms.py
class ProductSearchForm(forms.Form):
    category = forms.ModelChoiceField(
        queryset=Category.objects.all(),
        widget=SearchableDropdownWidget(
            dropdown_type='category',
            placeholder='Todas as categorias',
            allow_clear=True
        ),
        required=False
    )
    
    products = forms.ModelMultipleChoiceField(
        queryset=Product.objects.all(),
        widget=SearchableDropdownMultipleWidget(
            dropdown_type='products',
            placeholder='Selecione produtos para comparar',
            max_selections=5
        ),
        required=False
    )
    
    featured_product = forms.ModelChoiceField(
        queryset=Product.objects.all(),
        widget=SearchableDropdownWithInfoWidget(
            dropdown_type='product_info',
            info_url='/api/products/info/',
            info_container_id='product-details',
            placeholder='Selecione um produto para ver detalhes'
        ),
        required=False
    )

Configurações Avançadas

Configurações Globais

# settings.py
SEARCHABLE_DROPDOWN_CONFIG = {
    # Configurações padrão
    'default_placeholder': 'Selecione uma opção',
    'default_search_placeholder': 'Digite para buscar...',
    'default_no_results_text': 'Nenhum resultado encontrado',
    
    # Configurações de performance
    'ajax_timeout': 5000,
    'min_search_length': 1,
    'max_results': 50,
    
    # Configurações de UI
    'allow_clear': True,
    'allow_create': False,
    'delay': 300,
    
    # Configurações de acessibilidade
    'aria_label': 'Dropdown pesquisável',
    'aria_describedby': 'dropdown-help',
}

Configurações por Ambiente

# settings/development.py
SEARCHABLE_DROPDOWN_CONFIG = {
    'debug': True,
    'ajax_timeout': 10000,
    'delay': 500,
}

# settings/production.py
SEARCHABLE_DROPDOWN_CONFIG = {
    'debug': False,
    'ajax_timeout': 3000,
    'delay': 200,
    'cache_results': True,
}

Deploy e Performance

Otimizações para Produção

# settings.py
SEARCHABLE_DROPDOWN_CONFIG = {
    'cache_results': True,
    'cache_timeout': 300,  # 5 minutos
    'ajax_timeout': 3000,
    'max_results': 20,
    'min_search_length': 2,
}

# views.py com cache
from django.core.cache import cache

def api_search_view(request):
    query = request.GET.get('q', '')
    cache_key = f"search_{query}"
    
    # Verificar cache
    cached_results = cache.get(cache_key)
    if cached_results:
        return JsonResponse(cached_results)
    
    # Buscar dados
    results = perform_search(query)
    
    # Salvar no cache
    cache.set(cache_key, results, 300)
    
    return JsonResponse(results)

Troubleshooting

Problemas Comuns e Soluções

1. Dropdown não abre

// Verificar se os scripts foram carregados
console.log('SearchableDropdown:', typeof SearchableDropdown);
console.log('SearchableDropdownUtils:', typeof SearchableDropdownUtils);

// Verificar se o elemento existe
const dropdown = document.querySelector('.searchable-dropdown');
console.log('Dropdown element:', dropdown);

2. Busca AJAX não funciona

# Verificar se a view retorna JSON válido
def api_search_view(request):
    try:
        # Sua lógica aqui
        return JsonResponse({'results': results})
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)

3. Estilos não aplicados

/* Forçar estilos com !important se necessário */
.searchable-dropdown {
    display: block !important;
    position: relative !important;
    z-index: 1000 !important;
}

Debug Avançado

// Habilitar modo debug
SearchableDropdownUtils.setDebugMode(true);

// Verificar dropdowns inicializados
const dropdowns = SearchableDropdownUtils.getInitializedDropdowns();
console.log('Dropdowns inicializados:', dropdowns);

// Verificar configurações
const configs = SearchableDropdownUtils.getConfigs();
console.log('Configurações:', configs);

Testes e Qualidade

Executando Testes

# Testes da biblioteca
cd recoveredperispirit/django/django_searchable_dropdown
python -m pytest tests/ -v

# Testes com cobertura
python -m pytest tests/ --cov=. --cov-report=html

# Testes do test_app
cd test_app
python manage.py test

Status dos Testes

  • 110 testes passando (100% de cobertura)
  • Formulários: 25 testes
  • Widgets: 25 testes
  • Utilitários: 35 testes
  • Integração: 6 testes

Demonstração Completa

Para ver todos os widgets em ação, execute o test_app:

cd test_app
python manage.py runserver

Acesse: http://localhost:8000

Páginas de Demonstração

  • Página Inicial: Visão geral de todas as funcionalidades
  • Produtos: Dropdowns básicos e com filtros
  • Clientes: Dropdowns com AJAX
  • Pedidos: Dropdowns múltiplos
  • AJAX Demo: Demonstração completa de busca AJAX
  • With Info Demo: Demonstração de dropdowns com informações

Licença

Esta biblioteca é distribuída sob a licença MIT.

Contribuição

Para contribuir:

  • Fork o repositório
  • Crie uma branch para sua feature
  • Faça commit das suas mudanças
  • Push para a branch
  • Abra um Pull Request

Suporte

Para suporte e dúvidas:

  • Teste primeiro: Use o test_app para verificar se o problema é específico do seu projeto
  • Consulte a documentação: Esta documentação e os exemplos no test_app
  • Abra uma issue: No GitHub com detalhes do problema e ambiente
  • Verifique os logs: Use o modo debug para identificar problemas

Keywords

django

FAQs

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts