Criando uma aplicação web com Django e JQuery
Recentemente migrei um projeto de um gerenciador de provedores de internet escrito em PHP utilizando Code Igniter para Django 1.0 e vou utilizar como exemplo para demonstrar como criar uma aplicação web com Django e JQuery usando Ajax. O sistema permite o cadastro de clientes, controle de banda, regras de firewall, fluxo de caixa e relatórios gerenciais, interagindo com ferramentas do terminal Linux.
Iniciando
A aplicação foi escrita em um ambiente contendo as seguintes configurações:
- Ubuntu 8.04
- Python 2.5
- MySQL 5.0
- VIM editor
Não vou abordar como instalar o Django no Ubuntu. Recomendo uma leitura na documentação oficial pois, dependendo da data que esse texto estiver sendo lido, o processo de instalação pode mudar.
Projeto vs Aplicação
Em Django, projeto é o sistema no todo e aplicação é um módulo desse sistema. No projeto podemos ter várias aplicações como: cliente, rede, relatório etc. Você pode criar uma única aplicação contendo toda funcionalidade do sistema, porém isso deixaria o código mais extenso e de difícil manutenção.
Criando um projeto
Vou chamar o projeto de Sigep (Sistema de Gerenciamento de Provedores - dããã, eu sei). Abra um terminal Linux e execute os comandos abaixo:
django-admin.py startproject sigep
cd sigep
/bin/ls -1 sigep
A saída do comandos ls
deve ser:
__init__.py
manage.py
settings.py
urls.py
O django-admin.py
criou um diretório com o nome do projeto contendo os seguintes arquivos:
- init.py: arquivo sem conteúdo utilizado para identificar que é um pacote Python
- manage.py: script para gerenciar o projeto Django, criar aplicações e rodar o projeto
- settings.py: contém as configurações do projeto como log e banco de dados
- urls.py: arquivo de rotas/mapeamento das URLs
Configurando o projeto
Edite o arquivo settings.py
adicionando:
import os
LOCAL = lambda *args:os.path.join(os.path.dirname(__file__), *args)
As linhas acimas criam uma função para retornar o caminho absoluto do diretório do projeto.
Em seguida, altere as variáveis dentro do settings.py
conforme abaixo:
DATABASE_ENGINE = 'mysql'
DATABASE_NAME = 'sigepdjango'
DATABASE_USER = 'usuario_do_mysql_aki'
DATABASE_PASSWORD = 'senha_do_usuario_do_mysql_aki'
TIME_ZONE = 'America/Sao_Paulo'
LANGUAGE_CODE = 'pt-br'
MEDIA_ROOT = LOCAL('media')
TEMPLATE_DIRS = (
LOCAL('templates')
)
A configuração acima define a conexão com o banco de dados MySQL, ajusta o timezone e idioma de acordo com o Brasil, informa o caminho do MEDIA_ROOT
, que é o diretório onde os arquivos externos (imagens, CSS e Javascript) serão armazenados e por último o diretório de templates HTML.
O próximo passo é criar a aplicação principal.
Criando uma aplicação
Utilize o manage.py startpp
para criar uma aplicação:
python manage.py startapp cliente
O startapp
cria um diretório contendo:
- init.py: tal como no diretório do projeto, indica que a aplicação também é um pacote
- models.py: classes que correspondem as tabelas MySQL
- views.py: contém as funções responsáveis por receber os requests de acordo com a rota definida no urls.py
Criando os modelos
Um model contém a estrutura de dados do sistema. A grosso modo, pode-se dizer que é o arquivo que contém classes que correspondem às tabelas do banco de dados, e os atributos dessas classes correspondem aos campos das tabelas. Os models do Django utilizam um design pattern conhecido como ORM, no qual um objeto possui métodos para lidar com a camada de acesso ao banco de dados.
Cada aplicação no Django contém um arquivo chamado models.py
. As classes devem ser adicionadas nesse arquivo:
from django.db import models
class Bairro(models.Model):
bairro = models.CharField(max_length=150)
def __unicode__(self):
return self.bairro
class Meta:
db_table = 'bairro'
class Logradouro(models.Model):
bairro = models.ForeignKey(Bairro)
logradouro = models.CharField(max_length=250)
def __unicode__(self):
return self.logradouro
class Meta:
db_table = 'logradouro'
class GrupoVencimento(models.Model):
grupo_vencimento = models.CharField("Grupo",max_length=20)
dia_vencimento = models.IntegerField("Dia",max_length=2)
def __unicode__(self):
return self.grupo_vencimento
class Meta:
db_table = 'grupovencimento'
verbose_name = 'Grupo de Vencimento'
verbose_name_plural = 'Grupos de Vencimento'
unique_together = ['grupo_vencimento']
class Cliente(models.Model):
TIPO_PESSOA = (
('F','Pessoa Fisica'),
('J','Pessoa Juridica'),
('R','Representante de Pessoa Juridica')
)
grupovencimento = models.ForeignKey(GrupoVencimento,verbose_name="Grupo")
nome = models.CharField("Nome/Razao Social",max_length=200)
apelido = models.CharField("Apelido/Nome Fantasia",max_length=200,blank=True,null=True)
cpf = models.CharField("CPF/CNPJ",max_length=14,unique=True)
rg = models.CharField("RG/IE",max_length=11,blank=True,null=True)
orgao_rg = models.CharField("Orgao RG",max_length=6,blank=True,null=True)
tipo_pessoa = models.CharField(max_length=1,choices=TIPO_PESSOA)
data_nascimento = models.DateField(blank=True,null=True)
data_cadastro = models.DateField(auto_now_add=True)
logradouro = models.ForeignKey(Logradouro,verbose_name='Endereco',blank=True,null=True)
numero = models.CharField(max_length=50,blank=True,null=True)
referencia = models.CharField(max_length=250,blank=True,null=True)
cep = models.PositiveIntegerField(max_length=8,blank=True,null=True)
def __unicode__(self):
return self.nome
class Meta:
db_table = 'cliente'
unique_together = ('nome','cpf','rg')
Crie o database no MySQL e em seguida rode o comando manage.py syncdb
para criar as tabelas:
mysqladmin -u root -p create sigepdjang
python manage.py syncdb
Uma mensagem perguntando se deseja você deseja criar um super-usuário para acesso ao nosso sistema será exibida. Esse tipo de autenticação do Django é usado na interface de administração automática (conhecido como Django Admin). Digite yes para poder criar uma conta. Digite o nome do super-usuário, um e-mail válido e uma senha. Se tudo deu certo, no final será exibido:
Superuser created successfully.
Installing index for auth.Permission model
Installing index for auth.Message model
Adicione a aplicação na variável INSTALLED_APPS
dentro do arquivo settings.py
. Essa variável contém as aplicações utilizadas no projeto, portanto, cada aplicação criada precisa ser inserida dentro desse array:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'sigep.cliente',
)
Habilitando o Django Admin
O Django Admin é uma interface simples e prática para utilizar no dia-a-dia. Ela fornece um CRUD completo sobre os modelos criados em cada aplicação. Para habilitá-la, adicione no INSTALLED_APPS
e descomente algumas linhas no arquivo urls.py
:
# settings.py
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'sigep.cliente',
'django.contrib.admin', # Adicione após as aplicações criadas
)
# urls.py
from django.conf.urls.defaults import *
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
)
Cada classe modelo precisa ser configurada para funcionar no Django Admin. Isso é feito criando o arquivo admin.py
dentro do diretório da aplicação contendo uma chamada à função admin.site.register
. O Django Admin possui várias opções de configuração que são definidas através de herança (conceito de orientação à objetos). Para customizar o Admin, é necessário criar uma classe que extende de admin.ModelAdmin
:
from django.contrib import admin
from sigep.cliente.models import *
class ClienteAdmin(admin.ModelAdmin):
date_hierarchy = 'data_cadastro'
ordering = ['nome']
list_filter = ('id','nome')
search_fields = ('nome','cpf')
admin.site.register(Cliente,ClienteAdmin)
class GrupoVencimentoAdmin(admin.ModelAdmin): pass
admin.site.register(GrupoVencimento,GrupoVencimentoAdmin)
class BairroAdmin(admin.ModelAdmin): pass
admin.site.register(Bairro,BairroAdmin)
class LogradouroAdmin(admin.ModelAdmin): pass
admin.site.register(Logradouro,LogradouroAdmin)
Aplique as configurações do Django Admin executando o comando syncdb
. Rode a aplicação com o comando runserver
e acesse pelo browser o endereço http://localhost:8000/admin
.
python manage.py syncdb
python runserver
Se quiser alterar o visual do Admin, basta copiar o diretório /usr/lib/python2.5/site-packages/django/contrib/admin/templates/admin
para dentro do diretório de templates definido no arquivo settings.py
, e alterar o código HTML do mesmo.
Criando forms
O Django permite que sejam gerados forms a partir das classes modelos. Desse modo é possível tratar os erros de preenchimento utilizando métodos nativos para validação de formulários.
Para criar esse tipo de form, basta criar uma classe que estende de ModelForm
e adicionar o model desejado.
Crie o arquivo forms.py
dentro do diretório da aplicação contendo:
from sigep.cliente.models import *
from django.forms import *
class ClienteForm(ModelForm):
data_nascimento = DateField(widget=DateTimeInput(format='%d/%m/%Y'))
class Meta:
model = Cliente
Assim, automaticamente é criado um formulário contendo todos os campos da tabela cliente. Também é possível inserir algumas propriedades dentro de cada elemento do formulário, como por exemplo style e onblur, utilizando um recurso chamado widgets. Exemplo usando widgets:
widget_nome = TextInput(attrs={'onBlur': mark_safe("alert('Exemplo widget')")})
widget_cpf = TextInput(attrs={'style':"width:135px;",})
class ClienteForm(ModelForm):
nome = CharField(max_length=200,widget=widget_nome)
cpf = CharField(widget=widget_cpf)
class Meta:
model = Cliente
Arquivos estáticos
São imagens, documentos, código Javascript e CSS. Eles devem ficar armazenados no diretório definido na variável MEDIA_ROOT
no settings.py
.
Crie o diretório media
na raiz do projeto. Faça o download da biblioteca JQuery e coloque dentro desse diretório:
mkdir media
cd media
wget http://www.jquery.com/download/jquery-1.2.6.min.js
JQuery é uma poderosa biblioteca Javascript que facilita a manipulação de elementos HTML e o uso de Ajax para obter dados sem precisar atualizar a página.
Crie o arquivo sigep.js
dentro do diretório media
contendo:
function editar(url, form, select) {
var id = $('#'+select+' option:selected').val();
var $form = $('#'+form);
if (id && id > 0) {
$form.attr('action', url);
$form.submit();
} else {
alert('Selecione um item primeiro.');
}
}
function excluir(url, form, select) {
if (confirm('Tem certeza?')) {
editar(url, form, select);
}
}
O arquivo sigep.js
possui 2 funções: editar, que verifica se foi selecionada alguma opção com id diferente de zero no <select>
e faz um submit do <form>
; e excluir, que pede confirmação do usuário antes de submeter o formulário.
Para configurar o Django para servir os arquivos estáticos, altere o urls.py
adicionando dentro da variável urlpatterns
:
(r'^files/(.*)','django.views.static.serve',{'document_root':'./files'}),
Templates
Templates são arquivos HTML que são renderizados pelo Django, substituindo variáveis pelos seus respectivos valores. O sistema de templates do Django permite o uso de tags como if
e for
em conjunto com o HTML. É possível também usar herança, ou seja, um template filho pode conter os atributos do template pai. Os templates devem ficar dentro do diretório definido na variável TEMPLATES_DIR
no settings.py
.
A princípio serão 3 templates – a página inicial (base.html
), a página onde serão exibidos os nomes dos clientes cadastrados (clientes.html
) e o formulário para cadastrar clientes (form.html
).
Criando o diretório templates
mkdir templates
Criando o template base
Esse é o template que serve como base para os outros templates. Nesse arquivo que você deve carregar os estáticos. Crie o base.html
:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<title>SIGEP - Sistema de Gestão de Provedor</title>
<script language="javascript" type="text/javascript" src="/media/jquery-1.2.6.min.js"></script>
<script language="javascript" type="text/javascript" src="/media/sigep.js"></script>
<style type="text/css">
ul, li { list-style: none; padding: 0px; margin: 0px; }
</style>
</head>
<body>
<div id="divMenuSuper">
<a href="/cliente/list/">Pagina Principal</a>
</div>
<div id="divEsqueleto">
{% block principal %}
{% endblock %}
</div>
</body>
</html>
Criando o template para listagem de clientes
Esse arquivo será utilizado para mostrar a lista de clientes cadastrados. Reparem que não foi definido um action
para o form pois será utilizado Javascript para submeter o formulário via POST, através da função editar
do arquivo sigep.js
.
Crie o clientes.html
:
{% extends 'base.html' %}
{% block principal %}
{% ifequal operacao "Cadastrar" %}
<form name="buscacliente" id="frmBuscaCliente" method="post" action="/cliente/search/">
<div id="divBuscaCliente">
<br />Buscar por nome ou CPF: <input type="text" name="busca" id="id_busca">
<input type="submit" name="btBuscar" value="Buscar">
</div>
</form>
<form name="listacliente" id="frmListaCliente" method="post" action="">
<div id="divListaCliente">
<h2>{{titulo|default:"CLIENTES CADASTRADOS"}} - TOTAL: {{clientes|length}}</h2>
{% if not clientes %}
Nenhum cliente cadastrado.
{% else %}
Nome:
<select name="listaclientes" id="id_cliente">
{% for c in clientes %}
<option value="{{c.id}}">{{c.nome|upper}}</option>
{% endfor %}
</select>
<br /><br />
<input type="button" name="btEditar" value="Editar" onclick="editar('/cliente/edit/','frmListaCliente','id_cliente')">
<input type="button" name="btExcluir" value="Excluir" onclick="excluir('/cliente/delete/','frmListaCliente','id_cliente')">
{% endif %}
</div>
</form>
{% endifequal %}
{% block form %}{% endblock %}
{% endblock %}
Criando o formulário de cadastro
Nesse template vou utilizar algumas das facilidades fornecidas pelo ModelForm
do Django. A variável form
é uma instância da classe ClienteForm
criada no arquivo forms.py
. É possível simplicar ainda mais a criação do formulário se utilizar os métodos {{form.as_p}}
ou {{form.as_table}}
.
Crie o arquivo form.html
:
{% extends 'clientes.html' %}
{% block clienteform %}
<form name="clienteform" id="frmCliente" method="post" action="{{form_url|default:"/cliente/add/"}}">
<div id="divFormCliente">
<h2>{{operacao|upper|default:"CADASTRAR"}} CLIENTE</h2>
<input type="hidden" name="id" id="id_cliente" value="{{id_cliente}}">
Nome ou Razão Social: {{form.nome}}<br />
Apelido ou Nome Fantasia: {{form.apelido}}<br />
CPF ou CNPJ: {{form.cpf}}<br />
RG ou Insc. Estadual: {{form.rg}}<br />
Orgão Exp. RG: {{form.orgao_rg}}<br />
Tipo Pessoa: {{form.tipo_pessoa}}<br />
Data de Nasc.: {{form.data_nascimento}}<br />
Grupo: {{form.grupovencimento}}<br />
Bairro:
<select name="bairro">
{% for b in bairros %}
<option value="{{b.id}}">{{b.bairro}}</option>
{% endfor %}
</select><br />
Endereco: <select name="logradouro" id="id_logradouro"></select>
Numero: {{form.numero}} CEP: {{form.cep}}<br />
Referencia: {{form.referencia}}<br /><br />
<input type="submit" value="{{operacao|default:"Cadastrar"}}">
</div>
</form>
<div id="divErro" style="color:red;">
<br />
{% ifequal mensagem "ok" %}
Cadastro realizado com sucesso.
{% else %}
{% for campo in form %}
{% if campo.errors %}
<br />{{campo.label}} {{campo.errors}}
{% endif %}
{% endfor %}
{% endifequal %}
</div>
{% endblock %}
Reparem que criamos manualmente um elemento para representar o endereço (logradouro). Fizemos isso porque esse campo será preenchido utilizando ajax quando for selecionado um bairro.
O formulário será submetido e os campos validados. Então a variável mensagem receberá o valor ok
se o formulário passar na validação ou erro
se não passar. Depois varre o array form obtendo o objeto chamado campo e verifica se há alguma mensagem de erro referente ao campo. Uma melhor abordagem sobre ModelForm pode ser obtida na documentação do Django.
O próximo passo é a criação das views.
Infelizmente não consegui recuperar o restante desse texto escrito em 2008.