Design de Tabelas para Sistema CRM
Para o projeto de um sistema CRM, é essencial definir a estrutura do banco de dados. As tabelas principais incluem uma tabela de clientes, tabela de usuários, tabela de campus e tabela de departamentos. Adicionalmente, para o módulo de vendas, são necessárias tabelas de registro de acompanhamento, tabela de inscrição e tabela de pagamentos. Para o módulo de supervisão de turmas, inclui-se uma tabela de turmas, tabela de registros de aula e tabela de registros de aprendizado.
Autenticação: Registro e Login
Implementação de formulários de registro usando Django's ModelForm. Abaixo, um exemplo de formulário personalizado:
from django import forms
from . import models
class UserForm(forms.ModelForm):
class Meta:
model = models.User
fields = "__all__"
exclude = ['is_active']
def clean_password(self):
value = self.cleaned_data.get('password')
# Adicionar validação personalizada aqui
if len(value) < 8:
raise forms.ValidationError('Senha muito curta')
return value
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
confirm_password = cleaned_data.get('confirm_password')
if password and confirm_password and password != confirm_password:
self.add_error('confirm_password', 'Senhas não coincidem!')
return cleaned_data
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name, field in self.fields.items():
if not isinstance(field, forms.BooleanField):
field.widget.attrs.update({'class': 'form-control'})
# View para registro
def register_view(request):
form_instance = UserForm()
if request.method == 'POST':
form_instance = UserForm(request.POST)
if form_instance.is_valid():
form_instance.save()
return redirect('home')
return render(request, 'registration.html', {'form': form_instance})
# View para edição
def edit_user_view(request, user_id):
user_obj = models.User.objects.get(pk=user_id)
form_instance = UserForm(instance=user_obj)
if request.method == 'POST':
form_instance = UserForm(request.POST, instance=user_obj)
if form_instance.is_valid():
form_instance.save()
return redirect('user_list')
return render(request, 'edit_user.html', {'form': form_instance})
No template, o formulário pode ser renderizado com:
{% for field in form %}
<label>{{ field.label }}</label>
{{ field }}
{% if field.errors %}
<span class="error">{{ field.errors.0 }}</span>
{% endif %}
{% endfor %}
Exibição de Dados
Ao exibir dados, considere diferentes tipos de campos:
- Para campos comuns: acessar o atributo diretamente, como
obj.field_name. - Para campos com choices: usar
obj.get_field_name_display()para exibir o valor legível. - Para chaves estrangeiras: acessar o objeto relacionado via
obj.foreign_keye seus campos. - Métodos personalizados podem ser definidos no modelo, por exemplo, para retornar strings HTML seguras com
mark_safe.
Exclusão de Dados
Implementar exclusão simples usando object.delete() após confirmação.
Paginação
Calcular intervalos para paginação: para página page_num, o início é (page_num - 1) * per_page e o fim é page_num * per_page. Aplicar isso em queries como data[start:end].
Busca por Texto
Utilizar filtros com Q objects para buscas flexíveis. Exemplo:
from django.db.models import Q
def search_records(query, field_names):
q_condition = Q()
q_condition.connector = 'OR'
for field in field_names:
q_condition.children.append((f'{field}__icontains', query))
return q_condition
# Uso: Model.objects.filter(search_records('termo', ['nome', 'email']))
Preservar Condições de Busca na Paginação
Manipular request.GET para manter parâmetros de busca. Exemplo:
query_params = request.GET.copy()
query_params['page'] = new_page_number
redirect_url = f"{request.path}?{query_params.urlencode()}"
Redirecionamento Após Edição
Incluir a URL atual como parâmetro next nos links de edição. Após salvar, redirecionar para esse parâmetro.
Tag de Template Personalizada
Criar uma tag simple_tag para gerar URLs com parâmetros:
from django import template
from django.urls import reverse
register = template.Library()
@register.simple_tag
def url_with_next(request, view_name, *args, **kwargs):
next_path = request.get_full_path()
url = reverse(view_name, args=args, kwargs=kwargs)
return f"{url}?next={next_path}"
No template:
{% load custom_tags %}
{% url_with_next request 'edit_view' object_id %}
Transações e Bloqueio de Linha
Para operações concorrentes, usar transações com bloqueio. Exemplo:
from django.db import transaction
with transaction.atomic():
# Adquirir bloqueio em linhas específicas
records = Model.objects.filter(pk__in=ids, assigned_to__isnull=True).select_for_update()
if records.count() == len(ids):
records.update(assigned_to=request.user)
else:
raise ValueError("Conflito de concorrência")
Inicialização em Lote de Registros
Criar múltiplos objetos de uma vez com bulk_create:
record_list = [Model(field=value) for item in data]
Model.objects.bulk_create(record_list)
Controel de Acesso por Requisição
Implementar um middleware para verificar permissões em cada requisição:
import re
from django.http import HttpResponseForbidden
class PermissionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
current_url = request.path_info
# Verificar whitelist e login
# ...
permissions = request.session.get('user_permissions', [])
for perm in permissions:
if re.match(f'^{perm["url"]}$', current_url):
return self.get_response(request)
return HttpResponseForbidden('Acesso não autorizado')
Design de Menu Dinâmico
Para menus dinâmicos, armazenar no modelo Menu e Permission com campos como is_menu, icon, e weight. Na autenticação, carregar menus na sessão.
Exemplo de template inclusion tag para menus de primeiro nível:
@register.inclusion_tag('menu.html')
def render_menu(request):
menu_items = request.session.get('menus', [])
current_path = request.path_info
for item in menu_items:
item['active'] = re.match(f'^{item["url"]}$', current_path) is not None
return {'menu_items': menu_items}
No template:
{% load menu_tags %}
{% render_menu request %}
Gerenciamento de Permissões Não-Menu
Para permissões não listadas como menus, vinculá-las a menus pai usando campos como parent_permission. Isso permite organizar hierarquias de acesso.