
Destrinchando o Django
Parte 4 — Criando Models e brincando com admin
E finalmente chegamos nas models! Mas não vamos ficar só nas models, como falei na parte 3, vamos trabalhar um pouco mais com testes e nos aprofundar um pouco mais com o admin.
Se você não leu as outras duas três primeiras partes, recomendo a leitura. No meu Github (link abaixo) você vai encontrar o que foi feito de código até agora e um índice para as publicações anteriores, que é atualizado a cada novo artigo.
https://github.com/fernandovalente/django-tutorial
Obs: Esse artigo será longo e com muita informação! Se você é marinheiro de primeira viagem, vá com calma, leia com atenção e mande suas dúvidas nos comentários, beleza?
Models
Uma model é a única, e definitiva, fonte de informações sobre seus dados. Ele contém os campos essenciais e os comportamentos dos dados que você precisa para armazenar. Cada model é responsável pelo mapeamento de uma tabela no bando de dados.
Sem mais delongas, vamos para o código. Abra o arquivo models.py dentro da app contatos, que criamos no outro tutorial:
from django.db import models
# Create your models here.
Nesse arquivo vamos criar nossa primeira model, chamada contato. Mas, seguindo a regra do TDD, vamos começar pelo teste ates de mais nada. Na parte 3 nós criamos um teste dentro dessa app. Nós podemos concentrar todos os testes dentro de um único arquivo, mas isso não é bom e nem fica elegante. Sendo assim, nossa primeira ação será criar uma pasta chamada tests, na raiz da app contato, em seguida, vamos mover o arquivo tests.py para dentro desse arquivo. Por último, vamos precisar criar um arquivo vazio chamado __init__.py dentro dessa pasta.
O resultado final ficará assim:

Rode o teste para conferir se tudo está ok:
$ python manage.py test contatos
Creating test database for alias ‘default’…
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Ran 2 tests in 0.035s
OK
Destroying test database for alias ‘default’…
Tudo continua funcionando 100%! Vamos fazer outra mudança. Vamos renomear esse arquivo tests.py para tests_views.py e deixar nele apenas os teste referentes as views. Depois, vamos criar um novo arquivo chamado tests_models.py.
Novamente, o resultado final ficará assim:

Feito isso, se quiser, rode novamente os testes e verá que tudo está funcionando perfeitamente.
Seguindo esse princípio, você terá seus testes separados e organizados por tipo (views, models, utils etc). Fica muito mais fácil dar manutenção nesses códigos, uma vez que eles não estão todos em um único arquivo por aplicação.
Voltando para os testes, vamos abrir o arquivo tests_models.py e começar a brincadeira. Como eu falei anteriormente, vou fugir um pouco do lance de blog e criar um gerenciador de contatos. Nossa primeira model vai ser Contato:
from django.test import TestCase
class ContatoTest(TestCase):
pass
Nade de mais foi feito, mas vamos fazer uma pausa para falar de Model Mommy antes de continuar o test.
Model Mommy
O Model Mommy foi criado pelo amigo da comunidade Python, Vanderson Mota, aka Argentino.
O Model Mommy oferece uma maneira inteligente de para criar testes no Django. Com uma API simples e poderosa que você pode criar muitos objetos com uma única linha de código.
Dito isso, vá ao terminal e instale o Model Mommy:
$ pip install model_mommy
Com o pacote instalado, vamos importar o ele em nosso aquito tests_models.py e criar nosso set up e nosso primeiro teste:
class ContatoTest(TestCase):
def setUp(self):
self.contato = mommy.make(Contato)
def test_create(self):
self.assertEqual(1, self.contato.pk)
Agora vamos rodar o teste:
$ python manage.py test contatos
Creating test database for alias ‘default’…
E..
======================================================================
ERROR: test_create (contatos.tests.tests_models.ContatoTest)
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Traceback (most recent call last):
File “/django-tutorial/contatos/tests/tests_models.py”, line 8, in setUp
self.contato = mommy.make(Contato)
NameError: name ‘Contato’ is not defined
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Ran 3 tests in 0.039s
FAILED (errors=1)
Destroying test database for alias ‘default’…
Maravilha! Era o que estávamos querendo! A resposta do teste nos fala o que precisamos fazer agora, criar nossa model Contato. E vamos lá! Dentro do arquivo contatos/models.py, vamos começar a brincadeira:

Nossa… Quanta coisa? Mas pera, o que é esse ugettext_lazy? Que raios é esse import UF que vem de .choices? Classe Meta, __str__! Socorro!

Como eu falei no início, esse artigo será longo e com muita informação! Vamos entender linha por linha do código acima?
Linha 1
Para criar uma model e usar os recursos do Django, precisamos que nossas classes herdem de models.Model, como foi feito na linha 7. Para isso, precisamos importar esse módulo models:
from django.db import models
Linha 2
from django.utils.translation import ugettext_lazy as _
Aaaa o ugettext_lazy… Isso é bem bacana e é bom deixar tudo preparado desde já! O ugettest lazy tem como objetivo permitir que seu projeto Django possua multi-línguas, fazendo com que essa tarefa se torne bem simples com aplicação de apenas algumas configurações. Esse assunto receberá um artigo completo mais para frente, mas, se você quiser saber mais sobre isso agora, de uma lida na documentação oficial do Django para Translation.
Linha 4
from .choices import UF
Existem momentos que você precisará de um dado que é imutável ou quase imutável e não vale criar uma tabela para receber esses dados. É ai que entram as choices. Eu costumo criar um arquivo de choices para receber esses dados e utilizar eles em minhas models. De uma olhada no arquivo choices.py para entender melhor.
class Contato(models.Model)
Agora estamos finalmente iniciando a nossa model. Sempre que iniciar uma nova model, você precisará que ela estenda a classe models.Model, esse passo é fundamental para que você possua uma classe com todas a magia das models do Django.

Em seguida teremos os atributos dessa classe, que representarão o banco de dados. É através dessa classe que faremos a ponte ente o banco de dados e sua aplicação. Ok, no fundo no fundo acontece um pouco mais do que isso quando estendemos a classe Model etc. Mas vamos nos contentar com essas informações por hora.
Assim como na modelagem do banco de dados, ao criar uma model você precisa definir os tipos de cada atributo, como char, date, date time, number, etc. Ao desenharmos nossa classe, também estamos modelando o nosso banco de dados, que será um espelho do que foi descrito por nós.
Para uma lista detalhada dos fields do Django, de uma olhada nesse link: http://bit.ly/1Poolft
Blank, null e default
Quando definimos o blank=True, estamos dizendo ao Django que esse campo não é obrigatório e que ele não precisará passar pela validação de preenchimento (veremos mais sobre validação de forms no próximo tutorial).
Ao informarmos o null=True, estamos definindo que poderemos gravar uma nova linha nessa tabela sem que esse determinado campo tenha algum valor.
Por padrão o blank e o null seão definidos como False.
Agora, para definirmos um valor default, devemos usar o default=’alguma coisa’, Ex:
status = models.BooleanField(default=True)
Ao aplicarmos o default, estamos garantindo que sempre que uma nova linha for adicionada sem um status definido como padrão, o valor default True será utilizado.
auto_now_add e auto_now
Esses dois parâmetros são aplicados ao DeteTimeField, como podemos observar no código acima. Quando usamos o auto_now_add, estamos pedindo para que o Django preencha apenas uma única vez esse campo, fazendo com que tenhamos a data e hora da criação dessa linha.
Já o auto_now é atualizado sempre que uma atualização é feita nessa linha. Isso é muito bom para quando pretendemos guardar a última atualização feita. Sendo assim, quando eu defino o criado_em e atualizado_em, eu passo a ter dois momentos diferentes guardados no meu banco de dados.
Meta e __str__

Ao usarmos a class Meta dentro de nossa model, podemos definir algumas coisas bem bacanas para o funcionamento e apresentação dessa classe que estamos criando, como por exemplo a ordem de apresentação. Em nossa classe modelo eu defini a ordenação em ordem decrescente e pelo nome. Para o inverso basta não adicionar o sinal de menos na frente do campo.
O verbose_name e o verbose_name_plural servem para a representação dessa classe no Django admin, sempre que a classe contato foi apresentada no admin no singular, o Django irá mostrar da forma como você definiu. O mesmo ocorre com o plural. Mas, se você não preencher? O Django vai tentar adivinhar como deve ser apresentada a sua classe dentro do Admin. No caso do plural ele irá adicionar automaticamente a letra S no fim. Se eu deixar sem definir o verbose_name_plural, no admin a sua representação no plural ficara “Contatoss” com dois esses no fim.
Por fim, vamos definir a representação string de nossa model. No exemplo acima, ao adicionarmos um contato com o nome Fernando, sua representação ficará assim Contato: Fernando.
Criando o banco
Agora que preenchemos tudo que precisamos, vamos mandar o Django criar nossa tabela e em seguida mandar rodar o nosso teste novamente para ver o que acontece. Volte para o terminal e escreva:
python manage.py makemigrations

Ok, agora o Django já deixou tudo no esquema para criarmos a tabela:
python manage.py migrate

Tabela criada!

Agora, rode novamente o teste:
python manage.py test
Creating test database for alias ‘default’…
E..
======================================================================
ERROR: test_create (contatos.tests.tests_models.ContatoTest)
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Traceback (most recent call last):
File “/Users/nandovalente/Documents/Projects/django-tutorial/contatos/tests/tests_models.py”, line 8, in setUp
self.contato = mommy.make(Contatos)
NameError: name ‘Contato’ is not defined
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Ran 3 tests in 0.015s
FAILED (errors=1)
Destroying test database for alias ‘default’…
Beleza, o que o erro está nos falando é que não definimos “Contatos” em nosso teste, vamos voltar no nosso arquivo tests_models.py:

Rode novamente o teste:
Creating test database for alias ‘default’…
…
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Ran 3 tests in 0.016s
OK
Destroying test database for alias ‘default’…
Sucesso! Model criada, teste rodando e tutorial no fim! No próximo iremos falar sobre Django Admin.