O “OBJETO” ADMIN DE UMA APP (PARTE II)

Dando continuidade ao nosso blog, e a post da séria ADMIN, iremos agora demonstrar como existem outras inúmeras configurações que podemos realizar no admin. Como faz tempo desde o último post, quem quiser relembrar algo a 1ª parte está aqui (http://goo.gl/DuipR).

Uma das dicas que ficaram para esse segundo post é configurar o admin para que a listagem dos itens seja associada ao usuário logado no sistema. Isso é muito último para sistemas com vários perfis de usuário onde cada perfil pode visualizar os itens direcionados ao perfil dele.

Para não ficar um post muito grande deixarei para falar sobre outras configurações mais avançadas, como por exemplo mostrar no change_list apenas os elementos pertencentes ao usuário logado. No nosso caso como estamos trabalhando com itens de contato iremos limitar os contatos enviados para o usuário logado.

Então vamos ao que interessa, vamos começar configurando o admin para primeiro identificarmos qual o usuário que está logado no sistema, para realizarmos essa tarefas teremos que subscrever o método queryset, não confundir com QuerySets que é outra coisa, esse método é do Admin e é o responsável por realizar a consulta que resulta nos itens mostrado no change_list. No caso se não declamarmos no Admin o método queryset o comportamento será o padrão do Admin que é mostrar todos os itens.

    def queryset(self, request):
        u""" Subescrevendo o queryset para retornar apenas os chamados destinados ao setor do
        usuário logado e os chamados que ainda não foram atendidos. Para o SuperUser são mostrados todos os chamados.
        """
        try:
            qs = super(ContatoAdmin, self).queryset(request)
            if request.user.is_superuser is False:
                if 'perseguidora' in str(request.user.groups.values_list('name', flat=True)).lower():
                    return qs
                else:
                    usuario = Equipe(fkUserAdmin=request.user.pk).retornar_colaborador()
                    tipos_contato = usuario.atendimento_tipo_contato.values_list('pk', flat=True)
                    return qs.filter(fkEmpresa=usuario.fkEmpresa).filter(tipo_contato__in=tipos_contato).filter(processado=False)
            else:
                return qs
        except Exception, e:            
            pass

Linha 1 – Declaração dos métodos
Linha 2 à 4 – Comentário do código
Linha 6 – Instanciando o queryset padrão do nosso Model, no caso é o model Contato, assim poderemos realizar operações no queryset, como por exemplo aplicar um filtro, nosso objetivo.
Linha 7 – Aqui começa a brincadeira, o comando request.user, retorna o usuário logado, e como tudo no Django/Python é objeto ele possui vários métodos/funções e um deles é o is_superuser que retorna um boolean que determina se o usuário logado é super usuário ou não. Caso nosso usuário seja super usuário iremos mostrar todos os itens sem nenhuma configuração adicional linha 15; caso não seja super user cairemos na Linha 8.
Linha 8 – Fazemos aqui um outro teste para saber se o usuário logado pertence ao grupo ‘perseguidora’, aqui utilizamos o request.user.groups que retorna os grupos aos quais o usuário logado está associado, o comando values_list(‘name’, Flat=True) fica como tarefa para vocês descobrirem o que ele faz.
Linha 9 – Caso o teste da linha 8  seja verdadeiro fazemos o retorno do qs sem nenhuma configuração adicional, o que resulta na listagem de todos os itens.
Linha 11 à 12 – Executo aqui outras consultas para resgatar dados necessário para o filter do qs, isso está totalmente relacionado à estrutura do seu models, por isso não é objeto desse post.
Linha 13 – Aqui sim damos o pulo do gato, percebam que estou fazendo qs.filter(), que é aplicar um filtro ao qs (queryset) padrão assim podemos aplicar qualquer tipo de filtro, como já sabemos fazer no models. O que fiz aqui foi justamente filtrar os chamados para a empresa X, pois o sistema é multi-empresas, depois um outro filtro que é pelo tipo de contato, resultando nos contatos direcionados apenas para o setor do usuário logado e por fim filtro novamente trazendo apenas os contato ainda não atendidos.

Outro método bem interessante de falarmos é o changelist_view(). Nesse método iremos dentre várias possibilidades, determinar os campos que serão mostrados na “tabela” novamente iremos utilizar o usuário logado como fator determinante para nossa customização.

    def changelist_view(self, request, extra_content=None):
        u""" Alterando o changelist_view para gerenciar os campos que serão mostrados conforme
             perfil do usuário logado. Sendo SuperUser será mostrado a data de envio da resposta.
        """
        if request.user.is_superuser or 'perseguidora' in str(request.user.groups.values_list('name', flat=True)).lower():
            self.list_display = ('tipo_contato', 'nome', 'email', 'telefone',
                                 'data_recebimento', 'data_resposta', 'mostrar_status_contato', 'fkEmpresa')
        else:
            self.list_display = ('tipo_contato', 'nome', 'email', 'telefone',
                                 'data_recebimento', 'data_resposta', 'fkEmpresa')

        return super(ContatoAdmin, self).changelist_view(request, extra_content)

Linha 5 – Novamente realizado alguns testes para identificar o perfil do usuário logado, já explicado no método anterior
Linha 6 – O list_display determina quais os campos serão “listados” no change_list, nesse caso estamos determinado via self.list_display quais campos serão mostrados, percebam que a linha 6 é diferente da linha 9 apenas na quantidade de itens (que são os atributos do nosso model), é só isso o pronto, configuramos o Admin para mostrar campos conforme o perfil do usuário logado.

Com a mesma lógica podemos subescrever outros métodos como o get_form, que é responsável por configurar o formulário de inserção/edição dos dados no Admin. Como estamos trabalhando com o Models Contato iremos configurar conforme o perfil do usuário quais campos serão mostrados ou não.

    def get_form(self, request, obj=None, **kwargs):
        form = super(AtendimentoAdmin, self).get_form(request, obj, **kwargs)
        if request.user.is_superuser:
            return form
        else:
            self.fieldset = [('tipo_contato', 'nome', 
                              'email', 'telefone', 
                              'data_recebimento', 
                              'data_resposta',)]
        return form

Tentei aqui mostrar alguns métodos do admin, novamente para não me estender muito e assim tornar cansativo a leitura do post, deixei para a III parte do Admin falarmos sobre:

  1. TabularInline
  2. Herança de Admins
  3. raw_id_fields
  4. formfield_overrides
  5. delete_model

Projeto Django – Governo Federal.

Foi prorrogado até domingo, 23/06/2013, às 23h59min o Edital de contratação de Consultor PNUD para um projeto Django. O valor total é de R$60.000,00 (sessenta mil reais). O trabalho pode ser feito remotamente, com alguns encontros de controle em Brasília. O projeto é uma expansão do serviço Colab de suporte às Comunidades Interlegis (http://colab.interlegis.leg.br)

Os requisitos profissionais obrigatórios são:

  1. Curso Superior concluído na área de Tecnologia da Informação ou áreas afins
  2. Experiência mínima comprovada de dois anos em desenvolvimento de produtos na plataforma GNU/Linux
  3. Experiência mínima comprovada de um ano em desenvolvimento de produtos na plataforma Python e Django
  4. Experiência mínima comprovada de dois anos em desenvolvimento de aplicativos para a web

O Interlegis é um programa de modernização do legislativo nacional e seus principais clientes são as Câmaras Municipais. Este projeto é uma oportunidade de engajamento com o processo democrático, no ponto mais próximo ao cidadão.

Vejam o edital completo em
http://www.interlegis.leg.br/produtos_servicos/publicacoes/editais/2013/edital-no-03-2013-colab4-republicacao

O “Objeto” admin de uma app (Parte I)

Bom dia, boa tarde, boa noite.

Após um longo tempo sem escrever retorno com a continuação da série sobre os objetos de uma aplicação no Django, e nesse post erei tratar sobre o admin.

O admin é considerado por muitos como a grande vantagem do Django em relação aos demais framework’s tanto em Python como em outros framework’s de outras linguagens, mas eu acho esse tipo de pensamento muito restrito pois o Django é muito mais que o admin. Concordo plenamente que a utilização do admin reduz absurdamente o tempo de desenvolvimento de qualquer tipo de aplicação, mesmo quando torna-se necessário realizar algum tipo de customização da interface. Um simples exemplo que posso citar é o campo do tipo Date, onde precisamos trabalhar com arquivos JS para criarmos uma boa experiência para o usuário, no Django esse elemento por padrão já é configurado sem necessitar de intervenção nossa em nenhum outro arquivo.

O admin utiliza-se das configurações realizadas no objeto models.py para realizar a “mágica” de com apenas uma linha, registrar o admin, criar a interface para o usuário final. Vamos então mostrar um simples exemplo aqui de como podemos com apenas uma linha “gerar” o front-end do cliente, tendo uma app já criada, vamos fazer dois passos.

  1. Criar um arquivo admin.py no diretório da nossa app
  2. Editar o arquivo admin.py criado e registrar o admin para nosso model

Para entendermos melhor vamos utilizar como modelo o model a seguir.

class Contato(models.Model):
    nome = models.CharField(max_length=150, verbose_name=u'Nome')
    empresa = models.CharField(max_length=150, verbose_name=u'Empresa',
                               null=True, blank=True)
    email = models.EmailField(verbose_name=u'E-mail')
    telefone = models.CharField(max_length=255, verbose_name=u'Telefone',
                                default='0')
    mensagem = models.TextField(verbose_name=u'Mensagem')
    cidade = models.CharField(max_length=250)
    estado = models.CharField(max_length=250)
    assunto = models.CharField(verbose_name=u'Assunto do contato', max_length=250)
    tipo_contato = models.ForeignKey(TipoContatoCliente,
                                     verbose_name=u'Tipo do contato')
    processado = models.BooleanField(u'Contato respondido?', default=False)
    acao_tomada = models.TextField(verbose_name=u'Ação de resposta ao contato',
                                   null=True, blank=True)
    atendente = models.ForeignKey(Equipe, verbose_name='Atendente', null=True,
                                  blank=True)
    data_recebimento = models.DateField(u'Data de recebimento', null=True,
                                        blank=True, auto_now_add=True)
    data_resposta = models.DateField(u'Data de resposta', auto_now=True,
                                     null=True, blank=True)
    fkSetor = models.ForeignKey(SetorFuncionario, verbose_name='Empresa',
                                null=True, blank=True)
    fkEmpresa = models.ForeignKey(Empresa, verbose_name='Site/Empresa', null=True, blank=True)

    def __unicode__(self):
        return unicode(self.nome)

Não irei me ater aos detalhes da nossa Classe (model), pois o foco aqui é o objeto admin. Para que a “mágica” aconteça vamos editar nosso arquivo admin.py deixando ele assim:

from django.contrib import admin
from contatos.models import Contato

admin.site.register(Contato)

Tudo bem eu falei que seria apenas 1 linha e escrevemos 3 linhas, mas vamos pensar; na linha 1 fizemos a importação do admin do Django, na 2ª importamos a Classe do nosso model que iremos registrar e na 4ª é que realmente registramos nosso admin, mas independente de ser 1 ou 3 linhas o resultado é esse

Change List do Admin
Change List do Admin
Change Form
Add Form
Change form do models
Change form

Vejam que com um simples registro temos as interfaces (telas) necessárias para que o usuário do sistema possa gerenciar as informações. Agora tenho que destacar que o front-end gerado no exemplo não é o padrão Admin do Django, essa é uma customização que fiz nos templates do Admin, essa interface está disponível no meu GitHub [https://github.com/guilhermecarvalhocarneiro/BootstrapAdmin] é opensource e podem utilizar o quanto desejarem, contribuam também com melhorias.

Achou fantástico? Calma, ainda temos um longo caminho, pois o admin tem muita coisa, como falei antes podemos modificar o admin da maneira que precisarmos/desejarmos. Vamos agora para algumas coisas mais legais ainda.

Para que possamos modificar a configuração padrão do admin deveremos criar uma classe dentro do admin.py ficando assim nossa classe.

class ContatoAdmin(admin.ModelAdmin):

    class Media:
        js = ("tinymce/jscripts/tiny_mce/tiny_mce.js",
              "tinymce/jscripts/tiny_mce/textarea.js",
              )

    search_fields = ('tipo_contato', 'nome', 'empresa', 'email', 'telefone')
    list_per_page = 10
    save_on_top = True
    fieldsets = [
        (None, {'fields': ['tipo_contato',
                           'nome', 'empresa',
                           ('email', 'telefone'),
                           'mensagem',
                           ('processado', 'acao_tomada'), ]}),
    ]

admin.site.register(Contato, ContatoAdmin)

Vamos para as explicações necessárias.

Linha 1: Criando a Classe ContatoAdmin, por padrão eu sempre trabalho com NomeModel e o sulfixo Admin, assim fica fácil de entender.  A nossa classe é do tipo admin.ModelAdmin, pois vamos de certa forma subscrever o objeto admin.ModelAdmin da classe em questão.

Linha 3: Aqui configuramos os arquivos js para que os elementos do tipo TextArea recebam o editor TinyMCE

Linha 8: Outra “mágica”, com essa configuração eu determino quais campos do Model poderão ser utilizados para realizar pesquisas, quando o usuário estiver na tela de listagem (change_list).

Linha 9: Aqui estou simplesmente determinando quantos elementos o admin deve mostrar por página, percebam aqui que é muito simples fazer a customização que desejamos.

Linha 10: Novamente de forma bem sugestiva configuro para que a barra de save seja mostrada também no topo do formulário (change_form).

Linha 12: Já o fieldsets serve para quando desejamos reordenar os atributos, que serão transformados em campos de formulário.

Agora vejamos o que temos depois dessas configurações, comparando claro com os outros print’s já mostrados acima.

Nova change list
Nova change list

Aqui podemos verificar que muita coisa mudou, temos um campo de pesquisa, resultado da configuração do search_fields, também podemos verificar a numeração no canto direito inferior, sendo esse resultado da configuração list_per_page.

Novo formulário de adição
Novo formulário

Já o formulário ficou dessa forma, comparando com o mostrado acima podemos ver que os campos estão numa disposição diferente e ainda temos um editor WYSIWYG, o que possibilita a inserção de texto formatado, no template temos que utilizar uma TemplateTag, mas depois falaremos disso.

Para não ficar um post muito grande deixarei para falar sobre outras configurações mais avançadas, como por exemplo mostrar no change_list apenas os elementos pertencentes ao usuário logado. No nosso caso como estamos trabalhando com itens de contato iremos limitar os contatos enviados para o usuário logado.

Também iremos subscrever os itens mostrados num ComboBox trazendo apenas elementos aos quais o usuário logado pode visualizar.

>>> print u’%s’ % (“Abraços”)

Django e GoogleMaps

Após um longo tempo sem escrever, volto a falar sobre a melhor plataforma de desenvolvimento que já trabalhei, e nesse post falarei sobre a integração entre o Django e o GoogleMaps, mesmo existindo app’s para quase tudo no Django decidi mostrar como fazer tal integração sem utilizar algo pronto, pois poderemos ver que mesmo quando necessitamos criar algo que ainda não exista na comunidade Django o trabalho também é simples, prático e rápido.

Os passos que iremos seguir são:

  1. Criar um model com campos para latitude e longitude
  2. Criar a chave para uso da API do GoogleMaps
  3. Criar os arquivos JavaScript necessários para identificarmos a localização do nosso visitante
  4. Subscrever o save_model do nosso model para tratar os valores do item 3
  5. Subscrever o templade do admin do nosso model para registrar a posição do usuário e também para mostrar o mapa

Estaremos também utilizando o HTML5 e a funcionalidade de Georeferenciamento, nativa da linguagem que é compatível com os principais navegadores do mercado. Se você ainda pensa que o HTML5 é o futuro mude rápido o seu conceito, pois o uso dela já é obrigatório caso queira desenvolver um software/site de qualidade.

Vamos agora ao que interessa que é fazer o projeto, lembrando que irei utilizar o admin do Django mesmo, assim fica até melhor de entender.

1 – Criar um model com campos para latitude e longitude

Aqui não temos muito o que falar pois é bem simples, criar um model e mais ainda criar dois atributos para receber os dados de latitude e longitude, no caso esses atributos são do tipo CharField

2 – Criar a chave para uso da API do GoogleMaps

  1. Logar na sua conta do Google
  2. Acessar o site http://goo.gl/uc8nL
  3. Seguir os passos para configuração da conta

3 - Criar os arquivos JavaScript necessários para funcionamento

Aqui teremos um pouco de trabalho, o primeiro arquivo JavaScript a ser desenvolvido configurado, na verdade é apenas para identificar se o navegador do cliente tem suporte à tecnologia de Geolocalização do HTML5, e para novamente não termos muito trabalho, vamos utilizar o Modernizr [http://goo.gl/qMf19], que é um arquivo javascript que nos permite dentre outras coisas, testarmos se determinada funcionalidade do HTML5 é compatível com o navegador do usuário do nosso sistema. Basta para tanto baixar o arquivo e configurar no seu projeto.
Depois de configurado no projeto, vamos fazer o teste para identificarmos a funcionalidade de geo-referenciamento no navegador do cliente, o código é esse aqui:

	if (Modernizr.geolocation) {
        navigator.geolocation.getCurrentPosition(function(posicao){
                $("#demanda_form").append("<input type='hidden' name='codigo_longitude' id='codigo_longitude' value="+ posicao.coords.longitude +">");
                $("#demanda_form").append("<input type='hidden' name='codigo_latitude' id='codigo_latitude' value="+ posicao.coords.latitude +">");
                $("#id_longitude").val(posicao.coords.longitude);
                $("#id_latitude").val(posicao.coords.latitude);
            });
    }

Na linha 3  e 4 adiciono via JavaScript dois campos do tipo hidden no formulário do nosso model, no caso chamado de demanda.

4 - Subscrever o save_model do nosso model para tratar os valores do item 3

Os dois campos da linha 3 e 4 serão resgatados no save_model para podermos vinculá-los aos campos lagitude e longitude do nosso model, vamos fazer isso agora, para tanto devemos ter o admin.py configurado para nosso model, no meu caso minha configuração do save_model ficou assim:

    def save_model(self, request, obj, form, change):
        try:
            requisitante = Requisitante.objects.get(id_user=request.user.pk)
            obj.solicitante = requisitante
            obj.chave_anonimo = requisitante.chave_identificacao
            obj.longitude = request.POST.get('codigo_longitude') or None
            obj.lagitude = request.POST.get('codigo_latitude') or None
            obj.save()
        except:
            pass

Nesse caso as linhas que interessam para o exemplo são as de número 6 e 7, e os comando request.POST.get(‘codigo_longitude’) e request.POST.get(‘codigo_lagitude’), esses dois comandos fazem justamente o “resgate” dos valores que inserimos no JavaScript e os vincula aos campos do nosso model longitude e lagitude, o resto do bloco já deve ser conhecido por vocês, estou apenas subscrevendo o save. Outra configuração que precisamos fazer no nosso admin é criar um método retornar um botão na grid (tabela) permitindo assim que o usuário exiba o mapa, a configuração ficou assim:

    def popup_georeferenciamento(self, obj):
        try:
            if obj.latitude and obj.longitude:
                return '<a href="#" class="btn btn-large" id="btn_georeferenciamento_%s_%s"> <i class="icon-screenshot"></i> Mapa</a>' % (obj.latitude, obj.longitude)
            else:
                return ''
        except:
            pass

O botão criado no código acima e inserido como um item no list_display do nosso admin pode ser visto na imagem abaixo, com o label de Mapa

5 - Subscrever o templade do admin do nosso model para registrar a posição do usuário e também para mostrar o mapa

Essa pode ser a parte mais “complicada” pois temos que ter um certo entendimento do funcionamento dos templates do admin, mas vou tentar explicar da melhor forma possível. A mágica do Django em criar a interface Front-end das app’s e dos models é tarefa da app admin, que configuramos no nosso settings com o ‘django.contrib.admin’, feito isso devemos também configurar o TEMPLATE_DIR = (), aqui podemos determinar ao Django que os arquivos HTML (Templates) do admin estão no diretório X, no meu caso eu sempre crio um diretório chamado ‘templates’ dentro da estrutura do meu projeto e copio os arquivos padrões do admin para esse diretório, para mim é necessária essa configuração pois eu não utilizo a estrutura padrão de HTML e CSS do Django, eu tenho templates customizados para utilizar o BootStrap do Twitter, isso também possibilita customizar o template do nosso model ‘demanda’, não que essa seja a única possibilidade, para poder inserir nele um elemento com o objetivo de mostrar o mapa quando o usuário clicar num determinado botão, basta para isso que criemos uma estrutura como essa meuprojeto-> templates -> admin -> minhaaplicacao -> nomedotemplatequequerosubscrever no meu caso estou subscrevendo o change_list.html, a imagem final fica assim, onde o mapa aparecerá antes do grid contendo os itens:

Captura de Tela (46)

Esse é o resultado final da nossa configuração, para atingir tal estrutura, foram adicionadas as estrutura abaixo no change_list.html da nossa app.

      <div id="maps_google">
        <div id="mapa"></div>
        <a href="#" id="btn_fechar_mapa_demanda" class="btn">Fechar</a>
      </div>

Veja que como falei é tudo bem simples e direto, veremos agora o que considero o único arquivo da nossa estrutura que pode causar alguma dúvida, é um JavaScript (jQuery), utilizado para mostrar a DIV onde o mapa será inserido e posicionar a caixa da legenda.

$("a[id^='btn_georeferenciamento_']").live("click", function(){
        var infowindow = new google.maps.InfoWindow();
        var botao = $(this).attr('id').split('_');
        var latitude = botao[2];
        var longitude = botao[3];
        var opcoes = {
            center: new google.maps.LatLng(longitude, latitude),
            // position: new google.maps.LatLng(longitude, latitude),
            zoom: 15,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            content: 'Origem da demanda.'
        }
        $("#maps_google").css("display", "block");
        $("#maps_google").slideDown('slow');
        $("#mapa").css("display", "block");
        $("#mapa").slideDown('slow');
        var local = new google.maps.LatLng(longitude, latitude);
        var mapa = new google.maps.Map(document.getElementById("mapa"), opcoes);
        mapa.setCenter(local);
        infowindow.setPosition(local);
        infowindow.open(mapa);
        infowindow.setContent('Origem da demanda.');
    });

Na linha 1 é disparada uma função quando houver o click no botão criado no item 4, é uma função jQuery simples, na linha 2 criamos a variável infowindow já utilizando a API do GoogleMaps, na linha 3 é resgatado o valor do atributo ID do botão, onde temos armazenado os valores da latitude e longitude que são transferidas para as variáveis latitude e longitude, na linha 6 é feito o uso de uma variável que chamei de opções, onde são configurados parâmetros conforme a documentação da API do GoogleMaps, os parâmetros podem ser estudados na documentação, da linha 13 à 16 faço a exibição das divs onde irei inserir o mapa, já na linha 17 crio uma outra variável que contem a localização conforme o resultado da funcao LatLng do GoogleMaps, depois na linha 18 faço a inserção do mapa na div “mapa”, com o comando google.maps.Map(document.getElementById(“mapa”), opcoes); perceba aqui o uso da variável criada na linha 6 opcoes, que nada mais é que o dicionário com os valores para configuração do mapa.

Bom espero ter sido bem claro, e que esse artigo sirva como base para que vocês possam ampliar o conhecimento e uso do GoogleMaps, afinal o que vemos agora são cada vez mais aplicações que fazem uso do geo-referenciamento ou geo-localização.

Django 1.5

Finalmente a nova, e podemos dizer mais aguardada versão do Django foi disponibilizada, e quando digo mais aguardada é por trazer a solução de algumas demandas antigas de muitos que trabalham com esse maravilhoso framework, entre elas destacamos:

  • Suporte ao Python 3.2 ou superior, http://goo.gl/llfcx
  • Maior flexibilidade na costumização do User model ou substituição por completo por outro model e mesmo assim mantendo as funcionalidades de autenticação do Django, http://goo.gl/mxlB2

Divirtam-se, https://www.djangoproject.com/download/

Abraços.

O “objeto” Models de uma app

Num post anterior eu falei de forma geral sobre os “objetos” do Django e um deles é o models, e como prometido irei postar sobre cada “objeto” individualmente e trarei algumas dicas sobre cada um deles.

Nesse post irei tratar do “objeto” models.py, que na minha maneira de entender o Django é o elemento mais importante de todos, pois os outros dependem diretamente dele, e sem esse não poderemos criar nossa estrutura de persistência. No models de cada app definimos as classes e os tipos de dados que iremos tratar na app. Para exemplificar de forma bem didática iremos imaginar que nossa app tratará informações sobre clientes, seus dados de endereço e seus perfis nas redes sociais.

from django.db import models

class Cliente(models.Model):
    """docstring for Cliente"""
    CHOICES_SEXO = (('M', 'Masculino'), ('F', 'Feminino'))
    nome = models.CharField(max_length=30)
    sobrenome = models.CharField(max_length=30)
    sexo = models.CharField(u'Sexo', max_length=1, choices=CHOICES_SEXO)
    nascimento = models.DateField(u'Data de nascimento')

class Social(models.Model):
    """docstring for Social"""
    cliente = models.ForeignKey(Cliente, verbose_name=u'Cliente')
    rede_social = models.ForeignKey('RedeSocial', vernose_name='Rede Social')
    email = models.CharField(u'Login', max_length=50)

class DadosContato(models.Model):
    """docstring for DadosContato"""
    cliente = models.ForeignKey(Cliente)
    logradouro = models.CharField(u'Logradouro', max_length=100)
    cidade = models.CharField(u'Cidade', max_length=50)
    email = models.EmailField(u'Email de contato', null=True, blank=True)                

class RedeSocial(models.Model):
    """docstring for RedeSocial"""
    titulo = models.CharField(u'Nome da rede social', max_length=100)
    url = models.URLField(u'Endereço da rede social')

Explicação:
Antes de qualquer coisa informo que codifiquei dessa forma para poder dar o máximo de exemplos possíveis sobre os tipos de dados que temos disponível.

A estrutura é bem simples e temos as classes do model herdando por padrão do models.Model, logo em seguida temos o comentário, docstring, e logo em seguida temos as declarações do atributos das nossas classes, e aqui tem um detalhe bem importante, pois dependendo da escolha do tipo do campo que fizermos poderemos ter benefícios ou grandes problemas. Percebam que não declarei um campo para chave primária, assim o Django cria “automaticamente” um campo identificador para cada uma das classes.

As atributos do tipo models.CharField são campos para tratar informação do tipo texto simples, e possuem um parâmetro obrigatório na definição que é o max_length, esse parâmetro determina a quantidade de caracteres que teremos armazenada nesse atributo, esse valor é utilizado tanto na criação do campo na tabela do banco de dados, como também usado para validar os dados no template.

Os atributos do tipo models.URLField e models.EmailField, são tipos textos especiais, pois quando o usuário está trabalhando a informação no template o Django faz uma validação para esses campos, mas no banco de dados são tipos varchar normais, o campo URLField deve ser utilizado quando precisamos tratar uma URL, e o tipo EmailField deve ser utilizado quando precisarmos tratar um dado do tipo Email

Já os atributos do tipo models.ForeignKey são utilizados para realizar a “ligação” entre as classe, e aqui entra o conceito de relacionamento entre classe, esse campo para o Django é do tipo Integer e faz internamente uma referência à classe que especificarmos como primeiro parâmetro do atributo. Veja que nas classes: Social e DadosContato eu tenho atributos do tipo ForeignKey que fazem referência à Cliente e RedeSocial.

Esse atributos quando exibidos nos templates do Admin são transformados em campos do tipo select do HTML e trazem nos options valores das classes as quais fazem referência, no nosso exemplo o campo cliente da classe Social, será mostrado como um select e trará por padrão os valores Cliente objects, aí temos um pequeno “problema” que é como identificar qual cliente pois todos os valores serão no padrão NomeClasse objects, isso ocorre até que configuremos o método def __unicode__(self): esse método é o responsável por retornar um valor, determinado por nós, para todos os momentos em que o Django fizer referência à nossa classe, seja num relacionamento ou mesmo nas mensagens enviadas aos usuários do sistema.

Aplicando o método Unicode na classe Cliente.

class Cliente(models.Model):
    """docstring for Cliente"""
    CHOICES_SEXO = (('M', 'Masculino'), ('F', 'Feminino'))
    nome = models.CharField(max_length=30)
    sobrenome = models.CharField(max_length=30)
    sexo = models.CharField(u'Sexo', max_length=1, choices=CHOICES_SEXO)
    nascimento = models.DateField(u'Data de nascimento')

    def __unicode__(self):
        return u'%s' % (self.nome)

Percebam que o método deve ser bem simples, pois os valores que colocarmos nele serão mostrados nos campos ForeignKey das classes que o referenciam. A partir do momento que configuramos nosso método o atributo cliente das classes Social e DadosContato não mostraram mais o valor Cliente objects e sim o nome de cada cliente, que foi o campo que configuramos para retornar.

Uma coisa que muitas pessoas que estão começando no Django não fazem e é de muita importância, principalmente pelo conceito de orientação à objeto, é trabalhar com os método no models. Imaginem que precisamos num determinado momento da nossa aplicação exibir o nome do cliente, trazendo primeiro o último nome e depois o primeiro nome, para resolver esse problema criaremos um método dentro da classe Cliente no models mesmo, e assim depois poderemos utilizar em qualquer instância da classe Cliente. A criação de um método segue a mesma estrutura do método __unicode__, ficando assim nossa classe Cliente.

class Cliente(models.Model):
    """docstring for Cliente"""
    CHOICES_SEXO = (('M', 'Masculino'), ('F', 'Feminino'))
    nome = models.CharField(max_length=30)
    sobrenome = models.CharField(max_length=30)
    sexo = models.CharField(u'Sexo', max_length=1, choices=CHOICES_SEXO)
    nascimento = models.DateField(u'Data de nascimento')

    def __unicode__(self):
        return u'%s' % (self.nome)

    def retornar_sobrenome_nome(self):
        return u'%s %s' % (self.sobrenome, self.nome)

    retornar_sobrenome_nome.short_description = u'Nome curto do nosso método'

Esse novo método da classe cliente pode ser utilizado nos demais elementos da nossa aplicação, tipo num list_display do nosso admin, como também numa views. É muito importante que nós fiquemos atentos nesse ponto.

Por fim falarei da classe Meta, que também deve ser configurada, apesar de ser opcional, pois assim poderemos entre outras coisas determinados como irão aparecer os nomes de nossas classe no template do admin, e mais uma vez a configuração é bem simples, ficando assim nossa classe Cliente.

class Cliente(models.Model):
    """docstring for Cliente"""
    CHOICES_SEXO = (('M', 'Masculino'), ('F', 'Feminino'))
    nome = models.CharField(max_length=30)
    sobrenome = models.CharField(max_length=30)
    sexo = models.CharField(u'Sexo', max_length=1, choices=CHOICES_SEXO)
    nascimento = models.DateField(u'Data de nascimento')

    def __unicode__(self):
        return u'%s' % (self.nome)

    def retornar_sobrenome_nome(self):
        return u'%s %s' % (self.sobrenome, self.nome)

    retornar_sobrenome_nome.short_description = u'Nome curto do nosso método'

    class Meta:
        verbose_name = u'Nome da nossa classe no singular'
        verbose_name_plural = u'Noome da nossa classe no plural'

Para não deixar o post ainda maior preferi apenas disponibilizar o link para os tipos de campos possíveis de serem utilizados em nossos models, http://goo.gl/dHRCe

Entendendo o conceito de app’s plugáveis

Para os desenvolvedores que já trabalharam com outras tecnologias como PHP, Java, Asp.NET ou até outros frameworks, o Django traz uma nova visão sobre um “sistema”, no Django na verdade um sistema é formado por várias “app’s plugáveis”, e mudar a forma de pensar/planejar/desenvolver para essa nova abordagem é um dos grandes desafios que encontramos no Django.

Na maioria dos sistemas/sites que desenvolvemos teremos uma ou várias funcionalidades que já foram desenvolvidas, e aqui entra o já conhecido conceito DRY do Django, porque se eu já fiz determinada funcionalidade porque desenvolver novamente?

Vamos para um exemplo bem simples e muito usual, que é uma app para comunicação entre o visitante/usuário do sistema/site e o proprietário do site/sistema. Hoje em dia com as redes sociais temos inúmeros meios de comunicação: Twitter, Facebook, Google+, Instagram, LinkedIn, Foursquare, Pinterest, sem conta o velhinho e-mail, imagina se você toda vez que for desenvolver um produto novo, seja sistema ou site, tenha que codificar novamente todas as interações com esses meios de contato? Complicado né?

Exemplo da abordagem de app’s plugáveis.

Requisitos do sistema XPTO:

  1. Gerenciamento dos clientes
  2. Comunicação do cliente via e-mail
  3. Comunicação do cliente via redes sociais
  4. Compartilhamento das informações do site nas redes sociais

Com esses requisitos a primeira idéia que temos é “Já que é um sistema pequeno vou fazer tudo numa app só e resolvo tudo ali dentro mesmo“. Essa idéia tem dois grandes erros, primeiro não existe sistema pequeno, vá por mim, e depois estamos usando um Framework e não estaríamos usando as boas práticas determinadas pelo Django.

Levando em conta os requisitos do sistema, iremos dividir nosso sistema em duas app’s, 1ª Cliente e 2ª Comunicação, então vamos para o projeto, para tanto devemos abrir o terminal Linux/Mac ou cmd Windows (meu caso).

Criando o sistema demo_01:

python c:\Python27\Scripts\django-admin.py startproject demo_01

O comando acima é bem simples, a parte de maior destaque é o demo_01 que é o nome do projeto/sistema que eu quero desenvolver o resto é fixo do próprio Django, agora vamos criar as duas app’s  Cliente e Comunicacao.

python manage.py startapp Cliente
python manage.py startapp Comunicacao

Precisamos agora criar em cada nova app um subdiretório static onde deveremos colocar os arquivos estáticos (img, css, js) da referida app. Devemos criar a mesma estrutura para a app Comunicacao.

|Cliente
|—__init__.py
|—models.py
|—views.py
|—tests.py
|—static
|——js
|——css
|——img

Agora o trabalho é feito de forma normal dentro dos models.py, views.py, etc, fazendo claro as divisões de quais classes devem pertencer à app Cliente e quais à Comunicacao.

Como forma didática irei criar as classes da app Comunicacao, portanto não irei tratar dos acessos às API’s das redes sociais, nem irei levar em consideração questões de herança.

#-*- coding:utf8 -*-

from django.db import models

class Facebook(models.Model):
    login = models.CharField(max_length=100, unique=True, blank=False, null=False)
    email = models.EmailField(unique=True)
    senha = models.CharField(max_length=10)
    post = models.TextField(u'Conteúdo do novo post', blank=True, null=True)

    def publicar_post(self):
        u"""Método para publicar um novo post no Facebook"""
        pass

class Pintarest(models.Model):
    login = models.CharField(max_length=200, unique=True)
    senha = models.CharField(max_length=10)
    ...
    def pinar(self):
        u"""Metodo para criar um novo pin"""
        pass

class Twitter(models.Model):
    login = models.CharField(max_length=100)
    senha = models.CharField(max_length=30)
    twitter = models.CharField(max_length=140, blank=True, null=True)

    def twittar(self):
        u"""Método para tuitar"""
        pass

class Email(models.Model):
    emailRemetente = models.EmailField(u'Email remetente')
    nomeRemetente = models.CharField(u'Nome remetente', max_length=500)
    emailDestinatario = models.EmailField(u'Email destinatário')
    nomeDestinatario = models.CharField(u'Nome destinatário', max_length=500)
    emailBCC = models.EmailField(u'Email destinatário BCC', null=True, blank=True)
    nomeBCC = models.CharField(u'Nome destinatário BCC', null=True, blank=True, max_length=500)
    emailCC = models.EmailField(u'Email destinatário CC', null=True, blank=True)
    nomeCC = models.CharField(u'Nome destinatário CC', null=True, blank=True, max_length=500)
    tituloMensagem = models.CharField(u'Título da mensagem', max_length=300)
    tipoMensagem = models.BooleanField(u'Tipo da mensagem', default=False)
    prioridadeMensagem = models.IntegerField(u'Prioridade no envio da mensagem', default=0)
    confirmacaoEntrega = models.IntegerField(u'Confirmar envio da mensagem?', default=False)
    corpoMensagem = models.TextField(u'Corpo da mensagem')

    def enviar(self):
        destinatario = []
        try:
            destinatario.append(self.emailDestinatario)
            if self.emailBCC:
                destinatario.append(self.emailBCC)
            if self.emailCC:
                destinatario.append(self.emailCC)
            send_mail(self.tituloMensagem,
                      self.corpoMensagem,
                      self.emailRemetente,
                      destinatario,
                      fail_silently=True)
        except BadHeaderError:
            return 'Cabecalho invalido.'
        except Exception, e:
            pass

Agora nossa app de Comunicacao já possui as classes necessárias para gerenciar os meios de contatos que desejamos fornecer no nosso sistema, agora precisamos habilitar o admin dessa app, vamos editar o arquivo admin.py, caso não exista basta criá-lo, deixando conforme abaixo.

# -*- coding:utf-8 -*-

from Comunicacao.models import Facebook, Pintarest, Twitter, Email
from django.contrib import admin

admin.site.register(Facebook)
admin.site.register(Pintarest)
admin.site.register(Twitter)
admin.site.register(Email)

Agora temos a estrutura para que o admin do Django possa gerenciar as informações da app Comunicacao.

Para não tornar o post muito longo eu ocultei aqui a parte de criação das views e das urls, mas o que é necessário entender é que desde ponto em diante em qualquer outro sistema/site que eu venha a precisar de funcionalidades de comunicação basta copiar o diretório Comunicacao, colar no novo sistema/site registrar no settings.py e teremos todas as funcionalidades da app Comunicacao sem refazer nenhuma linha de código, e se levarmos em consideração que temos no diretório static da app Comunicacao todos os arquivos js, css que desenvolvemos, nem a parte de AJAX para Comunicacao será necessária refazer.

Agora imaginem se a nossa abordagem fosse a de fazer tudo numa app apenas? #FAIL Portanto tente separar o máximo possível as “funcionalidades” dos sistemas em app’s, assim o DRY será muito utilizado, e vocês chegaram num ponto onde irão apenas “plugar” as app’s já desenvolvidas anteriormente quando forem desenvolver um novo sistema/site.

>>> print u’%s’ % (“Abraços”)

“Objetos” do Django.

O Django como sabemos é um framework desenvolvido em Python. A grande função de um framework é automatizar determinadas tarefas no desenvolvimento de sistemas/site, como também facilitar tal desenvolvimento, evitando que o desenvolver tenha que ficar repetindo várias vezes a mesma tarefa, por isso temos no Django o conceito de DRY¹ (Don’t Repeat Yourself) ou “não se repita”.

Um bom exemplo do conceito do DRY são os comando de banco de dados também conhecido como CRUD (Create, Read, Update, Delete), o Django traz nativamente o ORM para os banco de dados MySQL, PostgreSQL, Oracle, SqlLite, tendo também inúmeras bibliotecas para outros bancos, como por exemplo MongoDB.

O Django segue a orientação à objeto, possuindo características como classes, atributos,  métodos, herança dentre outras. Quando falamos de classes temos inúmeras classes como por exemplo Model, Form, Admin. Essas classes herdam de objetos como o models.Model, forms.Form, admin.ModelAdmin respectivamente, não sendo essas as únicas classes que tais elementos podem herdam, já identificamos aqui outra característica da orientação a objetos.

O Model
Esse objeto/arquivo  é onde o desenvolvedor cria toda a estrutura do sistema, podendo ser considerado o principal objeto dentro de uma aplicação e possui uma estrutura bem básica, sem muitos mistérios, como tudo no Django.

Como no Django um sistema é composto por várias app’s cada uma sendo responsável por gerenciar determinada informação, em cada app teremos, na maioria dos casos, uma estrutura de arquivos .py.

 

 

Como estamos tratando da classe Model, iremos encontrá-la no arquivo models.py e veremos uma estrutura básica conforme abaixo.

Exemplo de Model

Na linha 236 temos a declaração da classe onde temos o nome da classe que nesse caso é Foto, e entre parênteses o models.Model, que será o objeto do qual a classe Foto herdará as características, atributos, método e funções. Na linha 237, temos o o docstring, que segundo a PEP deve ser utilizado para documentar a minha classe, nesse caso reduzi a documentação por economia de espaço. Nas linhas 238 e 239 temos a declaração dos atributos da classe, que serão “traduzidos” pelo Django para tipos de dados do SGBD que escolhermos, tendo nesse caso dois tipos de dados um ForeignKey e outro ImageField. Na linha 241 temos a declaração de um método da classe e entre parênteses os atributos desse método, isso será discutido num outro post sobre Python.

O Admin

Uma das grandes facilidades que o Django traz para os desenvolvedores é a geração de forma “automática” das interfaces de gerenciamento dos dados, não podendo confundir com os templates do site ou sistema. Quando desenvolvemos um site/sistema precisamos de uma interface para possibilitar ao(s) administrador(es) do sistema gerenciarem as informações, realizando inserts, updates e deletes, e no Django o objeto responsável por essas tarefas é o admin.py. O admin herda diretamente do admin.ModelAdmin e nele podemos configurar como os dados serão “listados” para o usuário, podemos conformar a disposição dos campos na tela, podemos também ocultar determinados campos e ainda torná-los campos apenas de leitura.

Dentre as inúmeras configurações que podemos fazer, vou destacar aqui as mais utilizadas:

  1. search_fields, responsável por determinar quais campos serão utilizados na busca.
  2. list_per_page, responsável por controlar a quantidade de itens exibidos na listagem.
  3. list_display, tupla com os campos que serão mostrador na tela de listagem(change_list)
  4. inlines, tupla onde podemos configurar admin’s de outros models que possuem relacionamento com o admin que estamos configurando, explicarei melhor mais à frente este item.
  5. fieldsets, tupla onde configuramos como os campos do nosso admin serão mostrados no formulário de inserção/atualização.

O Form

Mesmo tendo no admin um grande aliado para geração do front-end de gestão dos dados do nosso sistema, teremos ainda duas necessidades, customizar determinados campos e comportamento do form dentro do admin, e também export para fora do admin um front-end para que o visitante do nosso site possa, quando necessário, efetuar o CRUD.

Para ficar mais claro vamos imaginar  que precisamos criar um formulário de contato para um site de um cliente x, onde ele deseja que o formulário tenha os campos: Nome, Email, Telefone, Cidade, Estado, Assunto e Mensagem, a imagem acima mostra exatamente a criação de um formulário de contato, seguindo os padrões de orientação à objeto do Django, perceba que ele herda de forms.Form. Para que possamos criar o formulário HTML originado dessa estrutura precisaremos da ajuda do objeto views.py que veremos adiante, entretanto como forma de esclarecimento, no nosso template de contato basta fazermos {{ form_contato.as_p }} ou {{ form_contato.as_table }}.

A Views

Podemos tratar a views como o meio de campo entre o usuário/visitante e as operações do Django em si, como por exemplo a persistência de uma informação no banco de dados, ou o envio de um e-mail por meio de um formulário de contato. Continuando com o exemplo acima de um formulário de contato, quando o usuário preencher os dados e clicar no botão enviar “alguém” deverá tratar esse comando, e é justamente a views quem faz isso, recebendo as informações preenchidas no formulário, fazendo o tratamento necessário e depois executando a operação desejada que nesse caso é enviar uma mensagem, acima temos justamente uma views responsável pelo envio de um e-mail com os dados fornecidos pelo visitante.

As urls

Apesar de pequeno esse objeto é um dos mais importantes no framework, opnião pessoal, pois é ele quem “traduz” a interação do usuário para uma views, pode parecer confuso, mas podemos criar uma analogia com a brincadeira do telefone sem fio, só que nesse caso a mensagem sempre chega correta, onde a interface do site em HTML recebe alguma interação do usuário, por exemplo o clique do botão de enviar mensagem de um formulário, essa ação é “interpretada” por alguma linha no arquivo urls.py, que a mesma também identifica qual views deve ser acionada para executar a ação que o usuário solicitou.

Não se preocupe esse post foi apenas uma passada rápida em cada elemento de uma app no django, e iremos tratar com mais detalhe cada uma delas.

1 – http://c2.com/cgi/wiki?DontRepeatYourself

>>> print u’%s’ % (“Abraços”)