Por que eu devo ler este artigo:Este artigo é útil por explorar as principais facetas de um dos frameworks web mais usados na atualidade. Baseado em Python e estruturas web comuns (HTML5, CSS, JavaScript, etc.), o Django é flexível, rápido e extremamente fácil de usar.

Em poucos passos conseguiremos configurar seu ambiente, utilizar a interface de linha de comando para criar projetos, customizá-los e desenvolver suas primeiras aplicações usando o Django. Aqui trataremos de entender o framework e criar o esqueleto inicial do microblog com o nosso template, integrações, publicação, servidor, etc.


Guia do artigo: Como criar um Blog com Django e Python

Um framework web nada mais é que um conjunto de componentes integrados, e até certo ponto independentes, que nos ajudam a desenvolver aplicações web de forma mais rápida e facilitada. Quando falamos no universo front-end, inúmeras são as opções: desde as baseadas no JavaScript em si, tais como jQuery, AngularJS, CoffeScript, etc.; passando pelo universo CSS/CSS3 como o Sass e o Less; até chegar no imprescindível HTML/HTML5, como o Bootstrap, Thymeleaf, dentre outros. Isso sem citar o universo web móvel que traria mais exemplos famosos como o jQuery Mobile, Ionic e Framework7.

Independente da solução escolhida, é certo que não conseguimos mais viver sem os benefícios dos frameworks. Alguns deles também precisam de uma pincelada back-end, na maioria das vezes baseada em Node.js, o que é uma ótima opção, considerando-se as vantagens de usá-lo em conjunto com suas soluções afins.

Quando falamos de comunicação com o servidor, recursos como autenticação, serviços, painel de gerenciamento, formulários, upload/download de arquivos, etc. que são funcionalidades amplamente presentes, precisam ser cuidados e solucionados.

O Django é um framework web de aplicações, totalmente gratuito e escrito em Python. Ele fornece uma stack pronta e completa para solucionar problemas de autenticação, controle de administração, site maps, feeds RSS, além de implementar recursos de segurança nativamente, sem que você tenha que se preocupar em configurá-los. É fácil de usar, de configurar e, sobretudo, de manter.

Para os objetivos deste artigo é necessário que você tenha conhecimento prévios, ao menos básicos, sobre a linguagem Python, uma vez que a usaremos como base para o restante da implementação.

Trataremos de criar uma aplicação de microblog, com administração, tela de posts, gerenciamento de comentários, dentre outras funcionalidades comuns a esse tipo de aplicação, de forma semelhante ao de blogs famosos como o Blogspot. Tentaremos explorar ao máximo os principais recursos do framework, expondo seus detalhes de configuração, boas práticas, e regras de codificação.

Configurando o ambiente

Antes de começar a instalação, é interessante que o leitor tire um tempinho para fazer um tour pelo site oficial do framework (seção Links), lá você encontrará alguns resumos rápidos sobre o que é o Django e suas principais features.

Basicamente, precisamos instalar duas coisas no nosso sistema operacional: o Python em si, que no Windows não vem com um binário adicionado por padrão e, portanto, precisa ser baixado e configurado; e o Django, que será instalado via interface de linha de comando.

Portanto, acesse a página de downloads do Python (seção Links) e baixe a versão correspondente ao seu sistema operacional (tem uma para cada tipo diferente, certifique-se também de selecionar o arquivo correto para a versão do seu SO: 32/64 bits).

No momento de escrita deste artigo estávamos na versão 3.5.0, mas pode ficar à vontade para baixar uma versão mais recente, caso já exista.

Execute o .exe e, na primeira tela que aparecer, clique em Install Now (certifique-se de manter checada a checkbox “Install launcher for all users (recommended)”). Essa ação também precisará de suas permissões de administrador do SO, aguarde até que o processo todo termine.

Por padrão, a instalação descompactará os arquivos do Python no diretório C:\Program Files\Python3.5, é aconselhável que não modifique isso (na primeira tela ele mostra o diretório, caso não seja de seu agrado, modifique-o via opção de customização).

Para testar se tudo ocorreu com sucesso, abra o prompt de comando do seu SO e digite o comando python --version. Caso o comando não seja reconhecido pelo sistema, então durante a instalação não foram adicionados os diretórios do Python à respectiva variável de ambiente.

Para corrigir isso, acesse o menu Windows do seu computador e digite “variáveis de ambiente” e clique na opção que aparecer. Na seção “Variáveis do sistema” clique em Novo... e configure os valores tal como mostrado na Figura 1. O conteúdo da caixa de texto “Valor da variável” é ;D:\Program files\Python3.5;D:\Program files\Python3.5\Scripts e corresponde ao diretório dos binários onde você instalou o Python.

Configurando variável de ambiente do PYTHON
Figura 1. Configurando variável de ambiente do PYTHON.

Clique em OK, e, em seguida, procure pela variável Path e clique em Editar.... Adicione ao final do campo o valor %PYTHON% e clique novamente em OK três vezes. Agora, abra novamente o prompt cmd e redigite o comando. Você deve ver a versão da linguagem sendo exibida.

Virtual Environments

Antes de fazermos a instalação do Django, precisamos nos assegurar de que teremos como gerenciar as diferentes versões do Python em nosso sistema operacional sem confundir ou danificar nenhuma outra. Esse tipo de prática se chama virtualenvs (virtual environments, ou ambientes virtuais), e permite que diferentes versões do Python e seus pacotes associados possam existir em harmonia no mesmo lugar, ao mesmo tempo em que isola os setups de projetos Python/Django uns dos outros. O conceito é semelhante aos workspace que vemos em outras linguagens como o Java, por exemplo.

Tudo que precisamos fazer é selecionar o diretório onde desejamos criar o nosso virtualenv, portanto navegue até o mesmo via cmd e execute o seguinte comando na pasta:

python -m venv microblog 

Após o término, este comando irá criar a estrutura inicial do projeto com algumas pastas de bibliotecas e scripts necessários para trabalhar com o Python no seu virtualenv. Porém, para iniciar os trabalhos no mesmo é preciso ativá-lo, portanto execute o seguinte o comando:

microblog\Scripts\activate 

Isso ativará uma espécie de subconsole, que você poderá usar para efetuar quaisquer comandos como se estivesse no cmd padrão. O nome do seu virtualenv também vem por padrão pré-adicionado ao endereço de máquina acessado.

Agora sim podemos instalar o Django. Para isso, faremos uso do pip que é uma ferramenta gerenciadora de pacotes e extensões do Python, dos quais o Django faz parte.

Para verificar se o pip foi corretamente instalado junto com o Python, digite o comando pip --version e verá o resultado (versão 7.1.2 no momento de escrita deste artigo). Para finalizar, execute o seguinte comando (com dois ==):

pip install Django==1.8.5 

Essa é a versão mais recente do Django até o momento. Consulte o site oficial para verificar isso. Aguarde até que o utilitário faça o download de todas as dependências e pronto, você está com o Django instalado. Se tudo correr bem, você terá uma tela semelhante à da Figura 2.

Nota: Se, durante a instalação, você receber alguma mensagem de erro, verifique se o caminho de diretório onde criou o seu projeto contém algum espaço em branco ou caracteres especiais. Caso sim, mova o mesmo para um que atenda a essas exigências.

Tela de instalação bem-sucedida do Django
Figura 2. Tela de instalação bem-sucedida do Django.

Também é interessante que o leitor opte pelo editor de código fonte de sua preferência, de acordo com sua experiência em Python. Para os fins deste tutorial, faremos uso do Notepad++ que provê suporte completo ao Python, mas pode usar quaisquer outros, como o Sublime, Gedit, Atom ou inclusive as IDEs mais robustas como e Eclipse (e seu plugin pyDev) ou JetBrains PyCharm.

Ou ainda, se preferir escapar de toda essa configuração desktop, o leitor pode configurar seus projetos via projeto pythonanywhere.com que fornece todos os recursos para trabalhar com Python totalmente online.

Primeiro projeto Django

Criar um projeto no Django, basicamente, significa dizer que vamos executar alguns scripts fornecidos pelo mesmo que irão nos darão a estrutura inicial para o projeto. Trata-se apenas de um monte de diretórios e arquivos que precisaremos usar mais tarde.

Em relação aos nomes de arquivos/diretórios, é interessante que não renomeie os arquivos que estamos prestes a criar. Também é importante que você não os mova de local. O Django por si só já mantém uma certa estrutura e organização para ser capaz de encontrar coisas importantes mais à frente. Portanto, execute o seguinte comando no cmd:

django-admin startproject django_project . 

Não esqueça de pôr o ponto no fim do comando, pois ele serve para indicar que o comando encerrou e que o projeto deve ser criado na raiz do diretório atual. Note também que o comando django-admin trata-se de um script presente nos binários de instalação que se encarregará de criar as estruturas de diretórios e arquivos necessárias para o projeto base do Django. Ao final, sua pasta microblog deve estar estruturada de forma semelhante à exibida na Listagem 1.

Listagem 1. Estrutura de arquivos/diretórios gerada pelo Django

microblog_django
├───manage.py
├───microblog
└───django_project
        settings.py
        urls.py
        wsgi.py
        __init__.py

Dos arquivos de extensão .py do Python, temos:

  • manage.py: se encarrega de ajudar com o gerenciamento do site. Através dele estaremos aptos a iniciar um servidor web no computador sem ter de instalar nada mais;
  • settings.py: contém a configuração do website;
  • urls.py: contém uma lista de patterns que serão usados pelo urlresolver;

Os demais não são importantes para os nossos objetivos, logo não precisamos nos preocupar com eles.

Mudando Time Zone

Agora que já temos o projeto criado precisamos fazer algumas configurações específicas do time zone (fuso horário) do Brasil. Para isso, abra o arquivo blog_site/settings.py com o seu editor de código e procure a linha que contém a chave TIME_ZONE. Altere seu conteúdo para “America/Sao_Paulo” e salve o arquivo. Esse valor é correspondente à constante que representa a maioria dos estados brasileiros.

Além disso, também precisamos criar um caminho de diretório para os arquivos estáticos (CSS, JS, imagens, etc.) que precisarmos usar no projeto. Para tanto, vá até o fim do arquivo e adicione a seguinte linha de código:

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

Isso será responsável por rotear as requisições a esses tipos de arquivos diretamente para essa pasta.

Configurando o banco de dados

Ao se trabalhar com Python você pode usar as configurações do banco de dados que desejar, porém, para abstrair os passos de configuração e instalação de bancos proprietários, optaremos por usar o que já vem por padrão com a plataforma: o sqlite3. Esse banco é extremamente leve e fácil de usar e está presente em vários ambientes como os browsers, Android, iOS, etc.

Ainda dentro do settings.py é possível observar o trecho de código que configura o acesso à engine de banco de dados do sqlite3, tal como vemos na Listagem 2.

Listagem 2. Código que configura o acesso ao banco de dados

# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
   
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

Entretanto, para que o banco propriamente dito seja criado, precisamos executar o seguinte comando no prompt, diretamente no diretório microblog_django (onde está contido o arquivo manage.py):

python manage.py migrate

Se tudo ocorrer bem, veremos um log no console semelhante ao que temos na Listagem 3. É preciso que todas as configurações recebam um OK no final, assim teremos certeza que tudo deu certo.

Listagem 3. Log de saída para migração do banco de dados

(microblog) D:\tests\microblog_django>python manage.py migrate
Operations to perform:
  Synchronize unmigrated apps: messages, staticfiles
  Apply all migrations: auth, admin, sessions, contenttypes
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying sessions.0001_initial... OK

Pronto, isso é tudo que precisamos para criar o nosso banco. Agora só precisamos iniciar o servidor para verificar se a nossa aplicação está funcional. Ainda no mesmo diretório raiz (que contém o manage.py), vamos iniciar o servidor através do seguinte comando:

python manage.py runserver

Se uma mensagem como “Starting development server at http://127.0.0.1:8000/” aparecer no console, então significa que o servidor iniciou com sucesso. Após fazer isso, a sua janela do prompt se torna o terminal do servidor, o que te impede de executar comandos Python para o projeto.

Para resolver isso, basta abrir uma nova janela cmd e entrar no seu virtualenv novamente, mantendo as duas janelas ao mesmo tempo. Para parar o servidor, basta usar o atalho Ctrl + C.

Certifique-se também que algum outro programa (talvez servidores) não esteja usando a porta 8000, uma vez que ela é usada por padrão para estabelecer a comunicação HTTP e dará erro caso esteja em uso.

Agora, para testar o site, basta digitar o endereço http://127.0.0.1:8000/ no browser e dar um enter. A página deverá se equiparar à que temos na Figura 3.

Tela inicial do projeto Django
Figura 3. Tela inicial do projeto Django.

Trabalhando com modelos

O Django faz amplo uso dos conceitos de orientação a objetos para manipular seus objetos e classes. A esse conjunto de premissas damos o nome de Modelos do Django.

Um modelo no Django é um tipo especial de objeto (muito semelhante ao que manipulamos em outras linguagens server side), pois ele é salvo no banco de dados.

Um banco de dados para o Django é apenas uma coleção de dados. Este é o lugar onde salvaremos informações sobre nossos usuários, posts do blog, etc.

Para o desenvolvimento do blog em questão, faremos uso do SQLite como banco de dados padrão, conforme vimos antes.

Para lidar com os modelos, precisaremos de um arquivo models.py que se encarregará de gerenciá-los. Porém, não vamos criá-lo manualmente, mas sim via um novo comando. O Python também disponibiliza um comando startapp, semelhante ao startproject que vimos, que traz consigo uma estrutura mais detalhada de arquivos py como testes, views, etc. Portanto, dentro da pasta raiz, execute o seguinte comando:

 python manage.py startapp django_app

Após isso, você verá que um novo diretório foi criado, tal como representado na Figura 4.

Estrutura de arquivos e diretórios gerada pelo
comando startapp
Figura 4. Estrutura de arquivos e diretórios gerada pelo comando startapp.

Dentre os arquivos gerados, destacam-se:

  • Outro __init__.py, servindo ao mesmo propósito que o criado anteriormente;
  • Models.py, para armazenar os modelos que citamos de dados, bem como as entidades e relacionamentos entre elas;
  • Tests.py, para armazenar uma série de funções para testar o código unitariamente;
  • Views.py, para armazenar uma série de funções para recebem requisições dos clientes e retornam respostas;
  • E admin.py onde você pode registrar seus modelos para que possa se beneficiar da maquinaria do Django para criar uma interface de administração padrão para você.

Views.py e models.py são os dois arquivos que você irá usar para quaisquer aplicações, e fazem parte do principal padrão de projeto arquitetural empregado pelo Django, ou seja, o padrão Model-View-Template.

Porém, antes que comece com a criação de seus próprios modelos e views, primeiro você deve informar ao seu projeto Django sobre a existência do seu novo aplicativo. Para fazer isso, precisamos modificar o arquivo settings.py, contido dentro de diretório de configuração do seu projeto.

Abra o arquivo e encontre a tupla INSTALLED_APPS. Adicione o aplicativo django_app que acabamos de criar no fim da tupla, o que deve, então, parecer com o do exemplo exibido na Listagem 4.

Listagem 4. Configurando settings.py para receber nova app.

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_app',
)

Verifique que o Django carregou o seu novo aplicativo através da execução do servidor de desenvolvimento novamente. Se o servidor iniciar sem erros, então sua aplicação foi subida com sucesso e você estará pronto para avançar para a próxima etapa.

Criando uma View

Com a aplicação devidamente criada, vamos focar agora nas views. Como primeira view, vamos enviar apenas um simples texto de volta para o cliente como resposta a uma requisição. Portanto, abra o seu arquivo Views.py, localizado dentro da nova aplicação criada, e remova o comentário “#Create your views here” (a propósito, comentários no Python começam assim) deixando o arquivo totalmente vazio. Em seguida, inclua o conteúdo da Listagem 5 ao mesmo.

Listagem 5. Conteúdo da página Views.py

from django.shortcuts import render
from django.http import HttpResponse
   
def index(request):
    return HttpResponse("<h1>Olá mundo, Django!</h1>")

A primeira coisa que temos de fazer é importar o objeto HttpResponse diretamente do módulo django.http, haja vista a necessidade de enviar um texto como resposta ao request. Todas as views do seu projeto devem ficar obrigatoriamente dentro deste arquivo, e cada uma deve ser declarada através da palavra reservada def, precedida do nome da view. Até o momento temos somente a view de nome index, mas você pode criar quantas quiser uma abaixo da outra, em forma de série.

Cada view, por sua vez, recebe pelo menos um argumento: um objeto HttpRequest, que também pertence ao mesmo módulo django.http. Por questões de convenção, normalmente o chamamos de request, mas você pode pôr o nome que desejar.

Além disso, cada view retorna um objeto HttpResponse. Um objeto desse tipo simples assume a forma de uma string que representa o conteúdo da página que desejamos enviar de volta para o cliente que requisitou essa view.

Com a view criada, estamos a meio caminho andado de exibi-la finalmente ao cliente. Porém, antes que isso aconteça, precisamos antes mapear a mesma a uma URL, para que seja entendida pelo sistema interno de navegação do Django.

Mapeando URLs

Ainda dentro do diretório da aplicação django_app, precisamos agora criar um novo arquivo chamado urls.py que nos permitirá mapear as URLs (por exemplo www.meuappdjango.com/django_app/) para views específicas. Após criado, adicione ao mesmo o conteúdo exposto na Listagem 6.

Listagem 6. Conteúdo da página urls.py.

from django.conf.urls import patterns, url
from django_app import views
urlpatterns = patterns('',
        url(r'^, views.index, name='index'))

Este é praticamente todo o código que precisamos para importar o controle de URLs mapping do Django no projeto, atrelado ao import das views da aplicação django_app, o que nos fornece acesso completo às mesmas.

Para criar os mapeamentos, usamos as enuplas, mais conhecidas como n-tuplo ou tuplas, que nada mais são que sequências ordenadas de n elementos recursivos.

Para o Django é obrigatório o nome urlpatterns, cujas tuplas contém uma série de chamadas à função django.conf.urls.url(). No exemplo, estamos usando a função url() apenas uma vez, o que significa que apenas um mapeamento foi definido, e assim sucessivamente.

O primeiro parâmetro a ser passado à mesma função é uma expressão regular (no caso ^$), que verifique se o texto casa com uma string vazia. Em suma, essa expressão define que todas as URLs fornecidas pelo usuário que casem com esse padrão estarão associadas à views.index().

À view será passado um objeto HttpRequest como parâmetro, contendo informações sobre a requisição do usuário para o servidor. Também fazemos uso do parâmetro opcional name da função url(), usando a string ‘index’ como valor associado.

Esse parâmetro, por sua vez, serve para diferenciar um mapeamento de outro, uma vez que é inteiramente plausível que duas expressões de mapeamentos de URLs distintas possam acabar chamando a mesma view.

Você provavelmente tenha percebido que dentro do nosso “projeto” criado anteriormente no Django, já exista um arquivo urls.py, então por que dois? Tecnicamente, você pode colocar todas as URLs do seu projeto dentro desse arquivo.

Entretanto, isso é considerado uma má prática, uma vez que aumenta o acoplamento em suas aplicações individuais. Um arquivo urls.py separado para cada aplicação permite que você possa configurar suas URLs de forma individual também. Com um baixo acoplamento, fica mais fácil migrar suas aplicações para futuras integrações com outros projetos.

Para corrigir isso, vamos abrir o arquivo urls.py localizado dentro do nosso projeto blog_site e atualize a tupla urlpatterns exibida conforme mostrado no exemplo da Listagem 7.

Listagem 7. Novo conteúdo da tupla urlpatterns.

    """django_project URL Configuration
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/1.8/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
    Including another URLconf
        1. Add an import:  from blog import urls as blog_urls
        2. Add a URL to urlpatterns:  url(r'^blog/', include(blog_urls))
    """
    from django.conf.urls import include, url
    from django.contrib import admin
    urlpatterns = [
        url(r'^admin/', include(admin.site.urls)),
      url(r'^django_app/', include('django_app.urls')), 
    ]

Observe que agora mapeamos tudo de forma centralizada e focada na aplicação django_app. O mapeamento adicionado agora busca por strings de URLs que casem com o padrão ^django_app. Quando o padrão é encontrado, o método include() entra na história recebendo o restante da string e direcionando-a à view correspondente.

Para testar, inicie novamente o servidor e acesse a URL http://127.0.0.1:8000/django_app/ e você verá a tela da Figura 5.

Tela inicial do app criado no Django
Figura 5. Tela inicial do app criado no Django.

Em suma, quando criamos um projeto com o Django, é o comando startproject que usaremos para isso. Um projeto é a soma de várias aplicações, mapeamentos de URLs, workflows, etc. E dentro de cada aplicação você fará quantas views forem precisas, assim como seus mapeamentos a cada URL.

Trabalhando com templates

Até o momento, juntamos algumas peças para criar as páginas no Django. Quando se trata de aplicações web, suas páginas geralmente estão cheias de estruturas repetitivas, isto é, pedaços do HTML estrutural que estão presentes em todas ou grande parte da navegação do site.

Exemplo disso são os famosos cabeçalho e rodapé, bem como notas de navegação, dentre outros. O Django fornece um recurso de templates para facilitar esse tipo de implementação aos desenvolvedores web, além de já separar a lógica de aplicação dos conceitos de apresentação.

Para fazer uso dos templates, é necessário antes que configuremos um diretório específico para isso, onde os mesmos deverão ser armazenados.

No nosso projeto, crie uma nova pasta chamada templates dentro do projeto. E dentro deste, crie mais uma chamada Django, dessa forma o diretório poderá ser acessado pela URL relativa .../django_project/templates/django e é essa que usaremos para associar às nossas páginas.

Além disso, precisamos dizer ao Django que esse diretório existe e que ele está associado à casa dos templates do projeto. Portanto, vá novamente até o arquivo settings.py e adicione uma nova tupla TEMPLATE_DIRS ao mesmo, logo após a tupla TEMPLATES. Configure-a de acordo com o seu caminho absoluto, tal como na Listagem 8.

Listagem 8. Configurando templates no Django – settings.py.

TEMPLATES = [
 {
   'BACKEND': 'django.template.backends.django.DjangoTemplates',
   'DIRS': [],
   'APP_DIRS': True,
   'OPTIONS': {
     'context_processors': [
       'django.template.context_processors.debug',
       'django.template.context_processors.request',
       'django.contrib.auth.context_processors.auth',
       'django.contrib.messages.context_processors.messages',
     ],
    },
 },
]
   
TEMPLATE_DIRS = ('D:/tests/django/workspace/django_project/django_project/templates',)

Note que somos obrigados nessa configuração a informar o caminho absoluto do diretório de templates que criamos, logo se você estiver trabalhando em um time cada um com diferentes computadores e o código sincronizado, isso pode ser um grande problema, uma vez que URL é estática.

Ou seja, temos diferentes usuários o que implica diretamente em diferentes caminhos para o diretório de workspace. Uma saída seria adicionar um template diferente para cada diretório em cada setup diferente, mas isso definitivamente não é uma boa prática porque deixaria a configuração hard-coded. A solução então seria usar Paths Dinâmicos. Vejamos um pouco mais sobre isso.

Paths Dinâmicos

Para solucionar o problema de caminhos estáticos no Python devemos fazer uso de algumas funções nativas que gerenciarão os paths para nós automaticamente. Dessa forma, podemos garantir que o caminho absoluto possa ser recuperado independentemente de onde nosso projeto estiver localizado, tornado nosso código o mais portável possível.

A partir do Django 1.7, o arquivo settings.py contém agora uma variável chamada BASE_DIR. Ela armazena o caminho do diretório onde o módulo settings.py do projeto está guardado.

Ele pode ser obtido usando um atributo especial do Python, o __file__, que é configurado com o caminho absoluto do seu módulo settings. Após retornar o caminho, uma chamada à função os.path.dirname() provê a referência para o caminho absoluto do diretório.

Em seguida, se chamarmos essa função novamente, ela remove uma camada, e podemos navegar por entre elas, como se estivéssemos usando o comando cd do cmd.

Vejamos como funciona. Crie uma nova variável no settings.py chamada TEMPLATE_PATH e adicione o seguinte conteúdo à mesma:

TEMPLATE_PATH = os.path.join(BASE_DIR, 'templates')

Perceba que fizemos uso da funçao os.path.join() para juntar o conteúdo da variável BASE_DIR à string ‘templates’. Dessa forma, podemos agora modificar o caminho hard-coded que criamos antes pelo seguinte:


TEMPLATE_DIRS = (
      TEMPLATE_PATH,
 )

Adicionando o template

Após tudo configurado, crie um novo arquivo chamado index.html (sim, o exemplo com a view estava usando código HTML estático e hard-coded, mas na vida real usamos as páginas comuns) e o coloque dentro do diretório template/django. Adicione o conteúdo da Listagem 9 ao mesmo.

Listagem 9. Código HTML da página de index

<!DOCTYPE html>
<html>

    <head>
        <title>Django Index</title>
    </head>

    <body>
        <h2>Django diz pra você:</h2>
        olá fulano! <b>{{ msgnegrito }}</b><br />
        <a href="/django_app/sobre/">Sobre</a><br />
    </body>

</html>

Veja que temos apenas um HTML comum, exceto pelo uso de uma estrutura nova na linha 10: {{msgnegrito }}.

Trata-se de uma variável de template do Django (elas sempre devem vir nesse formato, com as chaves duplas) e será usada mais à frente para substituir o conteúdo HTML final.

Para usar esse template precisamos reconfigurar a view index() que criamos antes. Em vez de exibir uma simples mensagem, modificaremos seu código para redirecionar o fluxo de navegação diretamente para o nosso template.

Por isso foi importante mantermos o conteúdo do import render no arquivo de Views.py quando criamos o nosso código. Vá até esse arquivo e altere a view index para a demonstrada na Listagem 10.

Listagem 10. Configurando Views.py para redirect

from django.shortcuts import render
from django.http import HttpResponse
   
def index(request):
    # Constrói um dicionário para passar ao template seu contexto.
    # Note que a chave 'msgnegrito' representa o {{ msgnegrito }} no template!
    dicionario_contexto = {'msgnegrito': "Testando fonte em negrito..."}
  
    # Retorna uma resposta renderizada para enviar ao cliente.
    # Fazemos uso da função de atalho para facilitar tudo.
    # Note que o primeiro parâmetro é o template que desejamos usar.
    return render(request, 'django/index.html', dicionario_contexto)

Primeiro, construímos um dicionário de pares de chave-valor que queremos usar junto com o template, então chamamos a função utilitária render(), que, por sua vez, se responsabiliza por receber como entrada o request do usuário, o nome do arquivo de template, e o dicionário de contexto.

Essa função receberá esses dados e os juntará ao template para produzir uma página HTML final completa. Esta, então, é retornada e despachada para o browser web do usuário.

Quando um arquivo de template é carregado no sistema de templating do Django, um contexto de template é criado. Em outras palavras, um contexto de template é essencialmente um dicionário do Python que mapeia os nomes das variáveis dos templates com variáveis do Python.

No exemplo que criamos há pouco, incluímos uma variável chamada msgnegrito que, em contrapartida, será mapeada para a variável {{ msgnegrito }} do nosso arquivo HTML de template.

Agora, reinicie o servidor e execute a URL http://127.0.0.1:8000/django_app/ novamente no browser e você verá a tela da Figura 6.

Tela com template criado no Django
Figura 6. Tela com template criado no Django.

Inserindo elementos estáticos

Quando falamos em elementos estáticos estamos nos referindo a tudo que um website pode usar de arquivo não dinâmico, como imagens, CSS, arquivos de script, vídeos, arquivos Flash, dentre outros. No Django, tais arquivos são servidos de uma forma um tanto quanto diferente das páginas web convencionais, já que não são disponibilizados via requests simples como temos no HTML comum.

Assim como para os templates, também precisamos criar um diretório específico para esse tipo de mídia. Portanto, crie um novo diretório chamada static dentro do seu projeto e, dentro deste, um novo chamado img.

Em seguida, ponha uma imagem qualquer dentro desse diretório para fazermos alguns testes. Com o diretório estático criado, precisamos informar ao Django sobre o mesmo, da mesma forma que fizemos com o diretório de templates. Vá até o settings.py e atualize duas variáveis, STATIC_URL e STATICFILES_DIRS, tal como fizemos na Listagem 11.

Listagem 11. Configurando variáveis de diretório estático.

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
   
STATIC_PATH = os.path.join(os.path.dirname(__file__), 'static')
  
STATIC_URL = '/static/'
   
STATICFILES_DIRS = (
    STATIC_PATH,
)

A primeira variável representa a URL base que as aplicações do Django usarão para encontrar os arquivos de mídia estáticos quando o servidor estiver em execução. Por exemplo, ao rodar um servidor Django com a variável STATIC_URL configurada para /static/ tal como no exemplo que criamos, as mídias estáticas estarão disponíveis no endereço http://127.0.0.1:8000/static/.

Enquanto a variável STATIC_URL define a URL de acesso às mídias via servidor web, a variável STATICFILES_DIRS permite especificar o local do diretório recentemente criado static. Logo, da mesma forma que a variável TEMPLATE_DIRS exige caminhos absolutos em sua configuração, também é com essa nova variável.

Para testar, basta acessar a imagem via URL http://127.0.0.1:8000/static/images/logo.png e o resultado será semelhante ao da Figura 7.

Acessando imagem estática via URL /static/
Figura 7. Acessando imagem estática via URL /static/.

Agora que temos nosso sistema de redirecionamento automático de URLs para arquivos estáticos, podemos começar a adicioná-los em nossos templates, afinal é lá que precisaremos deles. Portanto, abra novamente o arquivo index.html e modifique seu conteúdo tal como temos na Listagem 12.

Listagem 12. Novo conteúdo da página de template.

<!DOCTYPE html>

{% load staticfiles %} <!-- Nova linha que carrega os arquivos estáticos -->

<html>

    <head>
        <title>Django Index</title>
    </head>

    <body>
        <h2>Django diz pra você:</h2>
        Olá fulano! <b>{{ msgnegrito }}</b><br />
        <a href="/django_app/sobre/">Sobre</a><br />
                 <img src="{% static "images/logo.png" %}" alt="Logo da Devmedia" /> <!-- Nova imagem recuperando arquivo direto de diretório estático -->
    </body>

</html>

Antes de fazer uso das mídias estáticas, precisamos informar ao Django que queremos fazer isso. E é o código da linha 3 o responsável por importar os arquivos estáticos na página e os fazer disponíveis para uso posterior.

Não esqueça sempre das chaves {} para abrir e fechar as tags de template do Django. Em seguida, configuramos uma nova tag <img> que receberá em seu atributo src o conteúdo da variável STATIC_URL combinado com a imagem logo.png em si. O resultado final a nível de HTML na página será algo como:


<img src="/static/images/logo.png" alt="Logo da Devmedia" /> <!-- Nova imagem recuperando arquivo direto de diretório estático -->

Basta reiniciar o servidor e rodar a página django_app novamente e você verá o resultado expresso na Figura 8.

Visualizando conteúdo estático nas páginas da Django app
Figura 8. Visualizando conteúdo estático nas páginas da Django app.

Também podemos usar para importar quaisquer outros tipos de arquivos, como arquivos JavaScript e CSS, por exemplo.

Suponha que temos uma página HTML de formulário com três campos: nome, endereço e CPF. Essas três informações devem ser validadas por um script JavaScript comum, mas precisamos fazer o processo todo via Django API.

Como primeiro passo, vamos criar um arquivo de cabeçalho que conterá o mesmo conteúdo do index, com a logo, um menu e uma mensagem de boas-vindas, apenas para vermos como funciona o import de templates uns nos outros no Django. Nomeie-o como cabecalho.html e adicione o conteúdo da Listagem 13 no mesmo.

Listagem 13. Conteúdo do arquivo de template de cabeçalho.

<!DOCTYPE html>

{% load staticfiles %} <!-- Nova linha que carrega os arquivos estáticos -->

<html>

  <head>
    <title>Django Form Exemplo</title>
       <style>
         body {
          font-family: Segoe UI;
         }
         .logo {
          float: left;
         }
         .logo img {
           width: 250px;
           padding: 20px;
         }
         .menu {
          float: right;
          padding: 40px;
          font-size: 15pt;
         }
         .menu a {
          margin-left: 15px;
          margin-right: 15px;
         }
         .bemvindo {
           clear: both;
           padding: 0 20px;
         }
                            
         </style>
    </head>

    <body>
     <div class='logo'>
       <img src="{% static "images/logo.png" %}" alt="Logo da Devmedia" />
     </div>
                 
     <div class='menu'>
       <a href="/django_app/sobre/">Sobre</a>
       <a href="/django_app/sair/">Sair</a>
     </div>
                 
     <div class='bemvindo'>
       <h2>Bem vindo ao nosso Site Django Exemplo!</h2>
     </div>
                 
    </body>

</html>

Veja que apenas reestruturamos o conteúdo do arquivo de index. O resultado pode ser visualizado na Figura 9: repare que o estilo da página foi todo definido dentro do arquivo de template, na tag <style>, o que não é uma boa prática. Para resolver isso vamos criar um arquivo cabecalho.css dentro da pasta static/css do projeto (a pasta css também deve ser criada). Extraia o conteúdo dessa tag para o novo arquivo e substitua a mesma pelo seguinte trecho de código (import do arquivo físico de CSS):

<link rel='stylesheet' href='{% static"css/cabecalho.css" %}' />

O resultado será o mesmo visualizado na figura.

Tela de cabeçalho da aplicação
Figura 9. Tela de cabeçalho da aplicação.

Agora que já entendemos o mecanismo de import estático, bem como sua comunicação com os diferentes tipos de diretórios do projeto, podemos prosseguir modificando o conteúdo da página de template index.html. Altere-a para o que temos na Listagem 14.

Listagem 14. Novo conteúdo da página index.html.

<!DOCTYPE html>

{% load staticfiles %} <!-- Nova linha que carrega os arquivos estáticos -->

<html>

    <head>
        <title>Django Form Exemplo</title>
                 <link rel='stylesheet' href='{% static "css/form.css" %}' />
     </head>

    <body>
     {% include "django\cabecalho.html" %}
                 
      <div>
       <fieldset>
        <legend>Formulário de Pessoa</legend>
        <form>
        <table cellpadding='5'>
          <tr>
          <td>Nome:</td>
          <td><input type='text' name='nome' id='nome'/></td>
            </tr>
            <tr>
              <td>Endereço:</td>
              <td><input type='text' name='endereco' id='endereco'/></td>
            </tr>
            <tr>
              <td>CPF:</td>
              <td><input type='text' name='cpf' id='cpf'/></td>
            </tr>
            <tr>
             <td colspan='2'><input type='submit' value='Enviar'/></td>
            </tr>
          </table>
        </form>
      </fieldset>
    </div>
  </body>

</html>

Veja que logo no início temos a referência para um novo arquivo de CSS (você deve criá-lo também e adicionar o conteúdo da Listagem 15).

Além disso, para que não tenhamos de duplicar o código do nosso template de cabeçalho em todas as páginas, basta que usemos o comando include do Django (linha 13) referenciando em seguida o nome do arquivo de template (lembre-se que configuramos o nível de acesso aos templates somente até a pasta templates, portanto se faz necessário referenciar a pasta django diretamente no comando).

O restante da listagem é HTML de formulário simples. Você pode verificar o resultado na Figura 10.

Listagem 15. Arquivo de código CSS da página de index.

fieldset {
    padding: 30px;
    margin: 0 auto;
    width: 50%;
}
input[type='text'] {
     width: 650px;
}
input[type='submit']  {
    font-family: Segoe UI;
    padding: 15px;
    width: 150px;
}
Tela final de formulário com import de cabeçalho
Figura 10. Tela final de formulário com import de cabeçalho.

Migrando layout para o Bootstrap

O Bootstrap é um framework de estruturação de HTML e estilo via templates. É extremamente usado na comunidade e também é possível acoplá-lo aos nossos projetos Django. Basta que para isso, importemos os scripts (que também incluem o jQuery) e arquivos CSS correspondentes à versão mais recente e criemos nosso HTML em detrimento deles.

Para transcrever nosso template para o framework, o primeiro passo é o cabeçalho. Vamos, portanto, alterar o código da página para o contido na Listagem 16.

Listagem 16. Arquivo HTML do template de cabeçalho com Bootstrap.

<!DOCTYPE html>

{% load staticfiles %} <!-- Nova linha que carrega os arquivos estáticos -->

<html>

    <head>
        <title>Django Form Exemplo</title>
        <link rel='stylesheet' href='{% static "css/cabecalho.css" %}' />
        <link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
        <link href="http://getbootstrap.com/examples/dashboard/dashboard.css" rel="stylesheet">
    </head>

    <body>
        <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
           <div class="container-fluid">
             <div class="navbar-header">
             <a class="navbar-brand" href="{% url 'index' %}">Home</a>
             <a class="navbar-brand" href="django/sobre/">Sobre</a>
          </div>
          <div class="navbar-collapse collapse">
           <ul class="nav navbar-nav navbar-right">
            <li><a class="navbar-brand" href="django/sair/">Sair</a></li>
          </ul>
        </div>
      </div>
     </div>
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
     <script src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script>
    </body>

</html>
  

Perceba que nas linhas 10 e 11 estamos importando dois novos arquivos de CSS referentes ao core do Bootstrap minificado e ao estilo de telas de dashboard.

No fim do arquivo vêm os arquivos JavaScript referentes ao jQuery e Bootstrap, respectivamente. É importante que o leitor se atenha a essa boa prática: sempre importar os CSSs no topo e os JSs no fim, para melhorar a performance de carregamento da página no browser.

Na linha 18 também temos o uso da expressão {% url ‘index’ %} que basicamente importa a URL de home da aplicação. O restante da implementação traz apenas classes do Bootstrap que você pode se inteirar melhor no site oficial (vide seção Links).

Agora é hora de modificar o template da página index para receber as alterações, inclusive formatando um formulário com o design do Bootstrap. Para isso altere o conteúdo da index.html para o representado na Listagem 17.

Listagem 17. Código da página de index com o Bootstrap.

<!DOCTYPE html> {% load staticfiles %}

<html>

<head>
    <title>Django Form Exemplo</title>
    <link rel='stylesheet' href='{% static "css/form.css" %}' />
</head>

<body>
    {% include "django\cabecalho.html" %}

    <div class="container">

        <form class="form-signin">
            <h2 class="form-signin-heading">Formulário de Pessoa</h2>
            <label for="nome" class="sr-only">Nome:</label>
            <input type="text" id="nome" class="form-control" placeholder="Nome" required autofocus>
            <br>
            <label for="endereco" class="sr-only">Endereço:</label>
            <input type="text" id="endereco" class="form-control" placeholder="Endereço" required>
            <br>
            <label for="cpf" class="sr-only">CPF:</label>
            <input type="text" id="cpf" class="form-control" placeholder="CPF" required>
            <br>
            <button class="btn btn-lg btn-primary btn-block" type="submit">Enviar</button>
        </form>

    </div>
</body>

</html>

Nessa página mantemos os mesmos scripts Django de include, agora com a estrutura HTML de elementos e classes CSS correspondente ao Bootstrap. O resultado pode ser conferido na Figura 11.

Tela final de formulário com estilo do Bootstrap
Figura 11. Tela final de formulário com estilo do Bootstrap.

Veja como a simples inclusão do framework através de poucas linhas de código já trouxe uma diferença considerável para o projeto. E esse “esqueleto” que iremos usar para finalizar a nossa aplicação de blog.

Até o momento você já aprendeu a criar novos projetos, usar a interface de linha de comando para virtualizar seu desenvolvimento, criar suas páginas, navegar entre elas, mapear URLs, importar plugins e outros frameworks, bem como gerenciar todo o conteúdo estático de suas aplicações Django.

Esses são passos importantes no entendimento desse framework.

Agora precisamos aprender a como dinamizar todo esse conteúdo, através do uso efetivo dos modelos Django, sincronizar com o Python e o servidor e o nosso blog estará pronto.

Também precisaremos criar uma tela de administração e veremos como fazer isso facilmente com as estruturas já fornecidas pelo próprio Django. Até a próxima e bons estudos!

Este artigo é útil por explorar recursos importantes do Django Framework, bem como da linguagem de programação Python, tais como conversão de URLs, formatação de valores, manipulação de datas, comunicação com bancos de dados (SQLite 3), navegação, templating, dentre outros. Ao final deste você estará apto a fazer amplo uso do Django para criar quaisquer tipos de aplicações web, com camadas de visão, aplicação e persistência de dados bem definidas e funcionais, além de saber como integrar sua aplicação com frameworks web importantes, como o jQuery e o Bootstrap.

Na primeira parte do curso vimos todos os passos para configurar o ambiente, criar o projeto/aplicação e entender o básico desse poderoso framework para criar formulários, nosso mecanismo de templates, bem como entender as expressões e scripts que o Django faz uso para gerenciar a lógica dos códigos. Nessa segunda parte trataremos de finalizar a implementação com a criação do nosso modelo de entidades, que, por sua vez, será convertido em tabelas no banco de dados SQLite. Veremos quais são as melhores estratégias para gerenciar nossas bases de dados, e que ferramentas podem auxiliar nisso. Também faremos a configuração das nossas views que, em contato direto com os templates, serão responsáveis por exibir a estrutura do site, bem como o estilo (via Bootstrap).

Atualizando o ambiente

Antes de prosseguir com a nossa implementação, é muito importante que o leitor saiba identificar quando novas versões do Django ou de qualquer outro framework foram lançadas no Python. Geralmente, esse tipo de recurso é facilmente visualizado através da flag –v na linha de comando, entretanto isso só funcionará para o core do Python em si, que imprime sua versão, bem como inúmeras outras linhas de código quando o seguinte comando é executado no prompt cmd:

python -v

Veja na Listagem 1 uma parte do log gerado pelo referido comando. Veja que uma simples verificação de versão já faz com que o Python carregue todas as libs principais do seu núcleo, que vão desde threads, I/O, compactação de arquivos (zip), dentre outros.

Listagem 1. Log do comando de versão do Python.

import _frozen_importlib # frozen
import imp # builtin
import sys # builtin
import '_warnings' # <class '_frozen_importlib.BuiltinImporter'>
import '_thread' # <class '_frozen_importlib.BuiltinImporter'>
import '_weakref' # <class '_frozen_importlib.BuiltinImporter'>
import '_frozen_importlib_external' # <class '_frozen_importlib.FrozenImporter'>
import '_io' # <class '_frozen_importlib.BuiltinImporter'>
import 'marshal' # <class '_frozen_importlib.BuiltinImporter'>
import 'nt' # <class '_frozen_importlib.BuiltinImporter'>
import _thread # previously loaded ('_thread')
import '_thread' # <class '_frozen_importlib.BuiltinImporter'>
import _weakref # previously loaded ('_weakref')
import '_weakref' # <class '_frozen_importlib.BuiltinImporter'>
import 'winreg' # <class '_frozen_importlib.BuiltinImporter'>
# installing zipimport hook
import 'zipimport' # <class '_frozen_importlib.BuiltinImporter'>
# installed zipimport hook
...

Para checar a versão do Django instalada, uma vez que ele não tem um binário próprio, precisamos fazer uso da ferramenta utilitária pip que vimos no primeiro artigo. Ela é responsável por gerenciar os pacotes do Python, inclusive suas versões. Execute o seguinte comando:

pip freeze

Isso será o suficiente para acessar o módulo do Django e retornar com sua versão instalada. Veremos impresso o valor Django==1.8.5.

Para atualizar o Django, considerando que a versão 1.9 foi lançada entre a primeira e segunda partes deste artigo, teremos de desinstalá-lo primeiro e instalá-lo novamente em seguida. Não existe um comando update para este módulo em específico. Porém, podemos efetuar os dois passos usando um só comando, o de upgrade. Para tanto, execute o seguinte comando no cmd:

pip install django --upgrade==1.9

Para toda instalação/atualização é necessário informar sempre a versão. Para saber disso, é aconselhado visitar com frequência a página do framework que exibe sempre a última versão. Pronto, com o Django atualizado basta subir o servidor novamente e executar o projeto como antes.

SQLite: criando os modelos do Blog

O conceito de modelo no universo Django é muito semelhante ao que temos em frameworks de ORM de persistência objeto-relacional, como o Hibernate (para Java) ou o NHibernate (para C#). Criaremos suas classes e atributos na linguagem de programação e mapeia os mesmos com anotações que serão convertidas em comandos SQL para geração das estruturas de tabelas e colunas correspondentes no banco de dados.

No Django, esse trabalho é deveras facilitado pela associação automática feita dos modelos para com as tabelas na nossa base de dados SQLite. Isso por que todo modelo estará intrinsicamente associado a uma instância de tabela no banco e vice-versa.

Na primeira parte do artigo, vimos como mapear as configurações de criação do nosso banco no arquivo settings.py (na raiz do diretório do projeto), usando a base SQLite por já estar disponível por padrão junto à instalação do Python, ser fácil e flexível de usar, além de adotar o SQL como as demais bases relacionais de mercado, logo, eventuais alterações para outras bases não surtirão em mudanças tão pesadas. Tais configurações se resumiram a definir a engine de geração (cujo valor dado foi “django.db.backends.sqlite3”) e o nome da base de dados (db.sqlite3). Este segundo parâmetro, em especial, precisa vir acompanhado do caminho absoluto onde o mesmo banco será criado (já ele pode estar em outra máquina, sendo acessado de forma remota), por isso tivemos de efetuar um join() entre a constante BASE_DIR que criamos no início do arquivo e o nome do arquivo propriamente dito. No fim das contas, encontraremos esse arquivo criado no diretório django\workspace\db.sqlite3.

A extensão sqlite3 se refere à versão mais recente do SQLite, mas geralmente esse tipo de banco também pode vir na extensão .db ou com nenhuma.

Durante o processo de codificação, precisaremos verificar a consistência tanto física do banco (tabelas, colunas, índices, chaves – primária/estrangeira, etc.), quanto lógica (dados nas tabelas). Portanto, existem duas formas básicas de fazer isso:

  1. A primeira é através da própria linha de comando, através da geração de um arquivo txt contendo toda a estrutura de tabelas da base, porém em formato de modelos do Django. Vejamos, execute o seguinte comando no cmd de dentro do diretório workspace:

    python manage.py inspectdb > meuDB.txt

    Isso gerará um arquivo de nome meyDB.txt no mesmo diretório onde foi executado o comando, com o conteúdo semelhante ao da Listagem 2. Nele verificaremos os mesmos imports, declarações de classes, construtores, parâmetros que temos nos modelos convencionais do Django. Veja que cada campo recebe um nome e um tipo de dado (este último veremos com mais detalhes adiante), além da classe Meta que declara os metadados daquela tabela, tal como o nome físico da tabela ou se algumas (e quais) de suas colunas precisam ser criadas de forma agrupada.

  2. A segunda forma é fazendo uso de alguma ferramenta gráfica de análise DDL/DML de mercado. A vantagem desta abordagem é que podemos ver os dados das tabelas, algo não possível através do item anterior. Além disso, a interface é mais limpa e fácil de manusear, permitindo, inclusive, que façamos alterações na tabela diretamente e vejamos os efeitos surtidos na aplicação quando estiver configurada para consumir tais dados. Para tanto, faremos uso da ferramenta DB Browser for SQLite, que é gratuita, simples, além de estar disponível em português para usuários Windows e Mac OS. Para instalar acesse o link disponível na seção Links e siga os passos padrão até o fim.

Listagem 2. Conteúdo do arquivo meuDB.txt.

from __future__ import unicode_literals
   
from django.db import models
   
   
class AuthGroup(models.Model):
    id = models.IntegerField(primary_key=True)  # AutoField?
    name = models.CharField(unique=True, max_length=80)
   
    class Meta:
        managed = False
        db_table = 'auth_group'
   
   
class AuthGroupPermissions(models.Model):
    id = models.IntegerField(primary_key=True)  # AutoField?
    group = models.ForeignKey(AuthGroup)
    permission = models.ForeignKey('AuthPermission')
   
    class Meta:
        managed = False
        db_table = 'auth_group_permissions'
        unique_together = (('group_id', 'permission_id'),)
   
   
class AuthPermission(models.Model):
    id = models.IntegerField(primary_key=True)  # AutoField?
    content_type = models.ForeignKey('DjangoContentType')
    codename = models.CharField(max_length=100)
    name = models.CharField(max_length=255)
   
    class Meta:
        managed = False
        db_table = 'auth_permission'
        unique_together = (('content_type_id', 'codename'),)
...

Uma vez com o SQLiteBrowser (vamos chamar assim para simplificar) instalado, basta clicar no botão Abrir banco de dados, navegar até o diretório onde está o nosso arquivo db.sqlite3 e clicar em Abrir. Após isso, veremos todas as tabelas da sua base criadas até agora, tal como temos na Figura 1. Para visualizar os dados das mesmas, vá até a tab Navegar dados e selecione a tabela desejada (Figura 2).

Visualizandotabelas da base via SQLiteBrowser
Figura 1. Visualizando tabelas da base via SQLiteBrowser.
Visualizandodados da tabela sqlite_sequence via SQLiteBrowser
Figura 2. Visualizando dados da tabela sqlite_sequence via SQLiteBrowser.

Criando modelos: Blog e Categoria

Uma vez entendidos os conceitos relacionados ao SQLite, bem como a melhor maneira de gerenciar os bancos, vejamos agora como criar nossos modelos com as duas primeiras classes de entidade: Blog e Categoria. Para não se estender muito, focaremos principalmente nestas duas, para exibir na página uma listagem de ambas, assim como mapear suas tabelas de forma relacionada: um blog tem várias categorias, mas cada categoria está relacionada apenas a um blog (1:n).

Portanto, abra o arquivo models.py no diretório django_app e modifique seu conteúdo para o exibido na Listagem 3.

Listagem 3. Novo conteúdo da models.py.

from django.db import models
from django.db.models import permalink

# Create your models here.

class Blog(models.Model):
    titulo = models.CharField(max_length=100, unique=True)
    url = models.SlugField(max_length=100, unique=True)
    corpo = models.TextField()
    data = models.DateField(db_index=True, auto_now_add=True)
    categoria = models.ForeignKey('django_app.Categoria')

    def __unicode__(self):
        return '%s' % self.titulo

    @permalink
    def get_url_absoluta(self):
        return ('ver_post_blog', None, { 'slug': self.url })

class Categoria(models.Model):
    titulo = models.CharField(max_length=100, db_index=True)
    url = models.SlugField(max_length=100, db_index=True)
   
    def __unicode__(self):
        return '%s' % self.titulo

    @permalink
    def get_url_absoluta(self):
        return ('ver_categoria_blog', None, { 'slug': self.url })

A primeira mudança importante no arquivo está na linha 2 onde importamos um novo módulo do pacote django.db.models, o permalink, para lidar com links permanentes. Basicamente, este é um utilitário disponibilizado pelo Django em forma de anotações (@permalink) para gerar estruturas de links definitivos para as URLs dos nossos posts. Vejamos alguns detalhes:

  • Na linha 6 temos a criação da nossa primeira classe, a de Blog. Nela recebemos o modelo do pacote models que importamos do Django, para nos auxiliar em alguns passos básicos.
  • Das linhas 7 a 10 temos a declaração dos atributos dessa classe, bem como a definição dos tipos de dados de cada um. O Django dispensa tipificação em seus atributos, logo só precisamos definir os respectivos nomes. Para os tipos, faremos uso do pacote models também, que traz uma gama considerável de tipos de dados para mapear os diferentes tipos disponíveis nas bases de dados. Para criar uma coluna de tipo VARCHAR ou TEXT no banco, o tipo de dados do Django usado deve ser o CharField (caso deseje quantificar o tamanho mínimo/máximo de caracteres, sua unicidade, obrigatoriedade, dentre outros) ou TextField (caso o campo precise salvar uma quantidade muito grande de caracteres). Para datas usamos o DataField (linha 10). O tipo SlugField funciona como uma espécie de identificador do post, uma vez que precisamos criar um URL para cada um, salvar no formato texto e esta não pode se repetir. O mesmo acontece com o título, e essa é a razão pela qual criamos ambos os campos com o atributo unique setado com o valor True.
  • Na linha 11 criamos um novo campo de chave estrangeira para relacionar a tabela de blog com a de categoria. Para isso, fazemos uso do tipo ForeignKey passando como parâmetro o nome da tabela com a qual ela deve se relacionar.
  • Na linha 13 declaramos uma função __unicode__() que se encarrega de exibir o valor do objeto quando seu método toString for chamado. Basicamente ele retorna o texto do título que é o que vamos exibir no final.
  • Na linha 17 declaramos o método get_url_absoluta() que se encarrega de formatar a URL do nosso post antes que ela seja salva na base. Veja que anotamos este método com a annotation @permalink que descrevemos antes para gerar uma URL automática com base no texto do título. Por exemplo, considerando um título de texto “Alô Mundo Django” (mesmo valor que seria retornado pelo método __unicode__()), o valor final retornado pelo método get_url_absoluta() seria: /django_app/view/alo-mundo-django.html. Esse valor precisa ter a uma configuração específica no arquivo urls.py que faremos mais à frente.
  • A classe Categoria, na linha 20, segue o mesmo processo da classe anterior, contendo apenas os atributos titulo e url.

É importante salientar que cada uma das classes gerará uma tabela com seu nome como sufixo no final do nome da mesma. Por exemplo, como nossa aplicação Django se chama django_app, cada tabela é criada com este prefixo adicionada do nome da classe, logo, nossa classe de Blog se chamará django_app_blog na base de dados. Além disso, cada tabela recebe automaticamente uma coluna de chave primária de nome “id”, tendo seu valor também incrementado de forma automática, já que a mesma é criada como AUTOINCREMENT pelo Django.

Este exemplo está preparado para lidar apenas com um relacionamento 1:n entre categorias e blog. Caso deseje mais de uma categoria por post de blog, será necessário fazer uso do tipo ManyToMany. É aconselhado que o leitor de aprofunde sobre a referida classe na documentação do framework, uma vez que mudanças no projeto serão requeridas.

Configurando acesso de admin

Como a visão principal do blog até o momento será a de usuário comum, precisamos nos certificar de que o usuário admin, uma vez logado no sistema, consiga acessar as mesmas propriedades. Para isso, altere o conteúdo do arquivo admin.py na pasta da aplicação para o contido na Listagem 4.

Listagem 4. Conteúdo do arquivo de administração admin.py.

from django.contrib import admin
from django_app.models import Blog, Categoria

class BlogAdmin(admin.ModelAdmin):
    exclude = ['data']
    prepopulated_fields = {'slug': ('titulo',)}

class CategoriaAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('titulo',)}

admin.site.register(Blog)
admin.site.register(Categoria)

Como o arquivo de modelos está disponível na forma de um módulo para o Django, precisamos importar suas classes de Blog e Categoria neste arquivo (linha 2). O segredo dessa implementação consiste em criar uma classe de sufixo -Admin para cada uma já criada como modelo. Os atributos exclude e prepopulated_fields servem para excluir campos do modelo e pré-popular alguns deles quando da inicialização da página, respectivamente. No final, só precisamos registrar cada um dos modelos no objeto global de admin, via método register().

Criando as views do Blog

Nosso blog precisará essencialmente de três ações básicas:

  • Exibir todas as categorias e últimos posts;
  • Exibir os posts de uma categoria específica;
  • Exibir um post.

Para essa alteração teremos de modificar o arquivo de Views.py que, até o momento, consta apenas com o retorno da página de index. Para isso, altere o seu conteúdo para o demonstrado na Listagem 5.

Listagem 5. Novo conteúdo do arquivo Views.py.

from django.shortcuts import render, render_to_response, get_object_or_404
from django_app.models import Blog, Categoria
from django.http import HttpResponse

def index(request):
    return render_to_response('django\home.html', {
        'categorias': Categoria.objects.all(),
        'posts': Blog.objects.all()[:5]
    })

def ver_post(request, slug):   
    return render_to_response('django\\ver_post.html', {
        'post': get_object_or_404(Blog, url=slug)
    })

def ver_categoria(request, slug):
    categoria = get_object_or_404(Categoria, url=slug)
    return render_to_response('django\\ver_categoria.html', {
        'categoria': categoria,
        'posts': Blog.objects.filter(categoria=categoria)[:5]
    })

Vejamos algumas considerações:

  • Na linha 1 importamos as bibliotecas de renderização das respostas HTTP que o Django processará. Além da render, que já estava inclusa, importamos também as de render_to_response (para renderizar diretamente no objeto de HttpResponse) e get_object_or_404 (que busca um objeto de modelo com base nos parâmetros de filtragem passados como argumento, retornando um objeto com o erro HTTP 404 caso nada seja encontrado);
  • Na linha 2 importamos as já conhecidas classes de Blog e Categoria;
  • Na linha 5 mudamos a nossa função de index padrão que antes redirecionava para a página index.html na página de templates. Agora, enviamos para o response a URL “django\\home.html” (a barra dupla se deve ao fato do mapeamento que faremos de URL a seguir com uma expressão regular que requer tal configuração), a qual se encarregará de exibir a página de boas-vindas do blog que ainda será criada. Na declaração enviamos um parâmetro com os dois objetos de lista: de categorias (via método all() do atributo objects, disponível em todos os objetos de modelo) e de posts (também via método all(), porém passando o valor :5 para o vetor, o que fará com que apenas os cinco últimos posts sejam retornados);
  • Na linha 11 definimos a função ver_post() que receberá o parâmetro slug, referente ao identificador da URL do post a ser exibido individualmente. Ela faz uso do método get_object_or_404() para recuperar o objeto filtrando pelo atributo url, recebido como parâmetro.
  • Na linha 16 criamos a função ver_categoria() que também recebe o mesmo slug, porém para filtrar agora pela Categoria. Veja que por termos vários posts dentro de uma categoria, o filtro precisará trazer essa lista também inclusa. Para isso, fazemos uso do método filter() do atributo objects, disponível em todos os objetos de modelo.

Após isso, precisamos definir também as configurações de URL para permitir e direcionar o fluxo de navegação das páginas referentes a cada post/categoria. Para isso, abra o arquivo urls.py e altere seu conteúdo para o que temos na Listagem 6.

Listagem 6. Novo conteúdo do arquivo urls.py.

from django.conf.urls import include, url
from django.contrib import admin
   
urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^django_app/', include('django_app.urls')), 
    url(r'^django_app/view/(?P<slug>[^\.]+).html', 'django_app.views.ver_post', name='ver_post_blog'),
    url(r'^django_app/categoria/(?P<slug>[^\.]+).html', 'django_app.views.ver_categoria', name='ver_categoria_blog'),
]

Agora as coisas começam a se conectar na aplicação. Além das URL’s referentes ao domínio de admin e à raiz do projeto (django_app/), agora temos mapeadas as de posts e categorias. As duas últimas url’s configuradas passam parâmetros customizados à view para lidar com o conteúdo do texto que será formado para nossas URLs de posts. O valor (?P<slug>[^\.]+) é uma regex (expressão regular) que mapeia o valor final da URL com extensão final .html, além de verificar se o slug está correto. O segundo parâmetro, como de praxe, recebe a função Django que deve ser chamada para gerenciar tal requisição na Views.py (que recebem os mesmos nomes das duas últimas funções criadas na listagem anterior). O último parâmetro, por sua vez, recebe o nome que demos ao primeiro parâmetro da função get_url_absoluta(), quando criamos nossos modelos na models.py. É muito importante que o leitor se atente a essas configurações para não ter problemas quando for executar a aplicação final.

Definindo o template

O nosso próximo passo é configurar as páginas de template que usaremos para exibir os dados trafegados das views. Portanto, vá até o diretório django\Lib\site-packages\django\contrib\admin\templates\django e crie uma nova página de template de nome base.html, e insira o conteúdo da Listagem 7 à mesma.

Listagem 7. Página de template base.html.

<html>
   <head>
       <title>{% block head_title %}Blog Django DevMedia{% endblock %}</title>
      <!-- FAVICON HTML5-->
      <link rel="icon" href="//www.devmedia.com.br/favicon.png"/>
   </head>
   <body>
       <h1>{% block title %}Bem vindo ao meu block{% endblock %}</h1>
       {% block content %}
 
       {% endblock %}
   </body>
</html>

Ela basicamente cria uma estrutura vazia com um bloco para o título da página (<title>) a ser customizado em cada um individualmente. Da mesma forma que o título de cada página (<h1>) e o conteúdo. Como já vimos a estrutura de scripts do Django, não vamos nos adentrar aos detalhes de include e export que o mesmo disponibiliza. Essa página também precisará ser estilizada com seu CSS, aqui iremos usar o Bootstrap por padrão.

A próxima página é a própria home.html. No mesmo diretório, crie-a e adicione o conteúdo da Listagem 8.

Listagem 8. Página inicial do blog: home.html.

{% extends 'django\base.html' %}
{% block title %}Bem vindo ao Blog Django DevMedia{% endblock %}

{% block content %}
    <h2>Categorias</h2>
    {% if categorias %}
        <ul>
        {% for categoria in categorias %}
            <li><a href="{{ categoria.get_url_absoluta }}">{{ categoria.titulo }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>Não há nenhuma categoria adicionada.</p>
    {% endif %}

    <h2>Posts</h2>
    {% if posts %}
        <ul>
        {% for post in posts %}
            <li><a href="{{ post.get_url_absoluta }}">{{ post.titulo }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>Não há nenhum post adicionado.</p>
    {% endif %}

{% endblock %}

Nessa página usamos alguns componentes novos, a saber:

  • Na linha 1 estendemos nossa página do template base.html. Essa é uma outra forma de importar um template em uma página, além do include que vimos. A expressão {% extends %} estabelece uma relação de herança entre a página atual e sua página mãe (base.html). A maior diferença em relação a de include está no fato de que quando importamos via exclude a página inteira se torna um template, e aí só temos de preencher cada uma de suas subseções informando o nome de cada bloco (block).
  • Na linha 2 sobrescrevemos nosso primeiro bloco: o de título da página. Para fazer isso, basta envolver entre chaves o valor do bloco de mesmo nome que foi declarado no template. O conteúdo HTML deve ser posicionado entre os scripts e os mesmos devem ser fechados sempre com um endblock.
  • Na linha 4 sobrescrevemos nosso conteúdo (content) com a lista de categorias e posts carregados direto da base de dados. Primeiro testamos se suas respectivas listas estão preenchidas no documento (linhas 6 e 17) para, em seguida, iterar sobre as mesmas (linhas 8 e 19). Caso existam categorias/posts a serem exibidos, criamos uma lista <ul> com links preenchidos para direcionar para as URL’s retornadas pelo método get_url_absoluta() de cada objeto. Caso contrário, exibimos uma mensagem de conteúdo vazio sendo retornado (linhas 12 e 23).

Para testar a nossa aplicação, precisamos executá-la no servidor. Entretanto, antes temos de nos certificar que a base de dados vai estar devidamente criada para tal. Portanto, precisamos “migrar “ as alterações que fizemos, principalmente os modelos de entidades, para uma nova versão do projeto. Esse é um recurso que o Django disponibiliza para te permitir evoluir sua aplicação, tanto em configuração quanto em integração aos diferentes componentes com os quais ela possa estar se comunicando, o que também inclui o banco de dados.

Execute então o seguinte comando na sua linha de comandos:

python manage.py migrate

Este comando exibirá uma lista de logs no console semelhante à exibida na Figura 3.

Resultado da execução do comando migrate no console
Figura 3. Resultado da execução do comando migrate no console.

Para verificar se ele funcionou, abra o banco novamente no SQLiteBrowser e veja se as novas tabelas foram criadas. Caso não tenham, isso acontece porque o Django cria uma pasta de segurança de nome migrations para salvar o histórico de migrações que já foi feito no projeto. Remova a mesma de dentro da pasta raiz da aplicação e execute o comando em seguida:

python manage.py syncdb

Este comando será responsável por buscar todas as últimas alterações nos arquivos de modelos (models.py) e transcrevê-las para a base de dados, forçando o processo todo. O resultado do log pode ser visto na Figura 4 e a visualização das tabelas recém-criadas na Figura 5. Veja como os tipos de dados são criados no SQLite em alusão aos que configuramos nos modelos usando os tipos do Django (encontraremos a relação de tipos aceitos pelo SQLite na URL disponível na seção Links).

Log de execução do comando de syncdb
Figura 4. Log de execução do comando de syncdb.
Visualizando tabelas criadas no SQLiteBrowser
Figura 5. Visualizando tabelas criadas no SQLiteBrowser.

Agora é só iniciar o servidor novamente e todas as alterações serão consideradas no build. Ao executar a URL http://localhost:8000/django_app/ no navegador veremos um resultado semelhante ao da Figura 6.

Página home.html exibindo lista de posts/categorias vazia
Figura 6. Página home.html exibindo lista de posts/categorias vazia.

Agora precisamos criar a estrutura de páginas HTML para conter a lógica de exibição dos posts e categorias individualmente. Para isso, dentro do mesmo diretório, crie um novo arquivo de nome ver_categoria.html e adicione o conteúdo da Listagem 9 ao mesmo.

Listagem 9. Conteúdo HTML da página ver_categoria.html.

{% extends 'django\base.html' %} 
{% block head_title %}Vendo categoria {{ categoria.titulo }}{% endblock %}
{% block title %}{{ categoria.titulo }}{% endblock %}

{% block content %}
    {% if posts %}
        <ul>
        {% for post in posts %}
            <li><a href="{{ post.get_url_absoluta }}">{{ post.titulo }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>Não existem posts para essa categoria.</p>
    {% endif %}
   <p>
    <a href='/django_app'><- Home</a>
   </p>
{% endblock %}

Na linha 1 estendemos novamente do template base.html e na linha 2 sobrescrevemos o título da página (cabeçalho HTML) com um texto concatenado com o título recebido da Views.py. O código da linha 3 é responsável por sobrescrever o título do corpo da página.

Em seguida (linhas 5 a 18), sobrescrevemos o conteúdo do corpo, verificando se a variável posts foi setada e, caso positivo, iteramos sobre a lista de posts criando links para cada um deles. Caso contrário, exibimos a mensagem de que não há posts disponíveis. É interessante salientar que quando o usuário chegar neste ponto, nosso método de filtro dos posts por categoria já terá sido chamado, trazendo apenas a lista de posts correspondente. No final, na linha 16, exibimos um link para retornar à página inicial da aplicação.

Crie agora um novo arquivo de nome ver_post.html e adicione o conteúdo exibido na Listagem 10.

Listagem 10. Conteúdo HTML da página ver_post.html.

{% extends 'django\base.html' %} 
{% block head_title %}{{ post.titulo }}{% endblock %}
{% block title %}{{ post.titulo }}{% endblock %}

{% block content %}
   <h3>Categoria: <a href='{{ post.categoria.get_url_absoluta }}'>{{ post.categoria.titulo }}</a></h3>
    {{ post.corpo }}
   <p>
    <a href='/django_app'><- Home</a>
   </p>
{% endblock %}

Essa página traz praticamente as mesmas configurações inicias da anterior, mudando apenas o seu conteúdo que exibe um link para a categoria relacionada àquele post em específico, bem como o texto do corpo do mesmo (linha 7). No final, o mesmo link de volta ao Home é adicionado.

Uma vez com a estrutura de template devidamente funcional, podemos focar em enxertar dados na base para serem consumidos pela aplicação. Para isso, acesse o SQLiteBrowser e, na tab Executar SQL, execute o código SQL representado pela Listagem 11. Após executar, não esqueça de clicar em Escrever Alterações para confirmar o commit dos dados na base, efetivamente.

Listagem 11. Código SQL de carga de dados.

INSERT INTO django_app_categoria(titulo, url) VALUES('Iniciante', 'iniciante');
INSERT INTO django_app_categoria(titulo, url) VALUES('Java', 'java');
INSERT INTO django_app_categoria(titulo, url) VALUES('Engenharia de Software', 'engenharia-software');
INSERT INTO django_app_categoria(titulo, url) VALUES('Front-End', 'front-end');
INSERT INTO django_app_categoria(titulo, url) VALUES('PHP', 'php');
   
INSERT INTO django_app_blog(titulo, url, data, corpo, categoria_id) VALUES('Alo Mundo', 'alo-mundo', '2015-09-12', 'Testando Alo Mundo Iniciante', 1);
INSERT INTO django_app_blog(titulo, url, data, corpo, categoria_id) VALUES('Trabalhando com Java EE', 'trabalhando-com-java-ee', '2015-09-13', 'A plataforma Java EE serve para o desenvolvimento de aplicações corporativas, seja um sistema Web simples ou uma aplicação com N camadas provendo Web Services e interfaces gráficas em janelas. A nova versão, Java EE 6, serve para simplificar ainda mais o desenvolvimento de aplicações corporativas (o que foi também foco da versão 5), trazendo flexibilidade e extensibilidade à plataforma.', 2);
INSERT INTO django_app_blog(titulo, url, data, corpo, categoria_id) VALUES('Fundamentos da Engenharia de Software', 'fundamentos-engenharia-software', '2015-09-14', 'Na maioria das instituições brasileiras de ensino superior, o conjunto de conhecimentos e técnicas conhecido como Engenharia de Software é ensinado em uma ou duas disciplinas dos cursos que têm os nomes de Ciência da Computação, Informática ou Sistemas de Informação. Raramente, em mais disciplinas, muitas vezes opcionais, e muitas vezes oferecidas apenas em nível de pós-graduação. Algumas instituições oferecem cursos de pós-graduação em Engenharia de Software, geralmente no nível de especialização.', 3);
INSERT INTO django_app_blog(titulo, url, data, corpo, categoria_id) VALUES('Otimizando performance no Front-End', 'otimizando-performance-front-end', '2015-09-14', 'O desenvolvimento de aplicações web que, até pouco tempo, focava em medições de performance apenas na arquitetura servidor e excluía toda a parte gráfica (principalmente por se tratar de GUIs desktop e estas serem deveras performáticas), se vê hoje forçado a entender como funciona a web, seus diversos protocolos, navegadores e, mais importante, as linguagens de programação que fazem tudo funcionar.', 4);
INSERT INTO django_app_blog(titulo, url, data, corpo, categoria_id) VALUES('Iniciando no PHP', 'iniciando-no-php', '2015-09-15', 'linguagem PHP é muito utilizada para o desenvolvimento de WEB e Sites e Intranet. Hoje como cases é possível citar grandes portais da internet, como o Facebook e o Twitter.', 5);

O leitor pode ficar à vontade para modificar os dados da forma que desejar. Isso já será o bastante para testar a aplicação com dados. Reinicie o servidor e acesse-a novamente. O resultado está representado na Figura 7.

Visualizando blog final com dados
Figura 7. Visualizando blog final com dados.

Se desejar inserir também a data na exibição de cada post, basta inserir o seguinte código na ver_post.html:

<h4>Data: {{ post.data|date:"d/m/Y" }}</h4>

Como o campo data foi definido com tipo DateField, basta modificar seu padrão (pattern) de formatação para o formato de data brasileiro (uma vez que elas são salvas em formato americano, por default). Observe também a forma como as URLs são apresentadas na barra de navegação, exatamente com os nomes que definimos. É interessante que o usuário selecione um padrão para todo o blog (o mais comum é o que usamos, separando as palavras por hífenes), evitando espaços em branco, uma vez que, se usados, serão transformados em caracteres especiais e deixarão a URL não muito apresentável. Lembre-se: as URLs são muito importantes para os motores de busca, pois definem muito do que aquela página vai dizer. É por isso que ferramentas como Blogger ou WordPress fazem uso extensivo desse tipo de recurso.

Estilizando o blog

Nosso blog encontra-se completamente funcional, todavia, ainda precisamos estilizar o mesmo com os recursos de design fornecidos pelo Bootstrap. Para isso, faremos uso de um template gratuito disponibilizado pelo site Start Bootstrap, cujo link para download encontra-se na seção Links. O mesmo já vem preparado para lidar especificamente com blogs e sua estrutura HTML já nos facilita o acoplamento ao nosso blog em si. Portanto, efetue o download e copie os arquivos contidos nas pastas css. js e fonts para a nossa pasta static do django_project.

Dentro do arquivo de download encontramos o template inserido na página index.html. Abra-a no navegador e veja como o perfil do blog se parece.

A primeira mudança que teremos de fazer é no nosso template base.html, uma vez que é ele o responsável por definir o cabeçalho, rodapé e corpo da página, deixando os dados para serem enxertados pelas demais views. Altere o seu conteúdo para o demostrado na Listagem 12.

Listagem 12. Conteúdo do template base.html estilizado com o Bootstrap.

<!DOCTYPE html>
{% load staticfiles %} <!-- carrega os arquivos estáticos -->
<html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{% block head_title %}Blog Django DevMedia{% endblock %}</title>
    
    <link rel="icon" href="//www.devmedia.com.br/favicon.png"/>

    <link href='{% static "css/bootstrap.min.css" %}' rel="stylesheet"/>
    <link href='{% static "css/blog-home.css" %}' rel="stylesheet"/>
</head>
    <body>
    <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
           <div class="container">
                  <div class="navbar-header">
                        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                               <span class="sr-only">Toggle navigation</span>
                               <span class="icon-bar"></span>
                               <span class="icon-bar"></span>
                               <span class="icon-bar"></span>
                        </button>
                        <a class="navbar-brand" href="#">Blog Django</a>
                  </div>
                  <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                        <ul class="nav navbar-nav">
                               <li><a href="#">Sobre</a></li>
                               <li><a href="#">Quem Somos</a></li>
                               <li><a href="#">Contato</a></li>
                        </ul>
                  </div>
           </div>
    </nav>
    <div class="container">
           <div class="row">
                  <div class="col-md-8">
                        <h1 class="page-header">
                               {% block title %}Bem vindo ao meu block{% endblock %}
                        </h1>
                        {% block content %}{% endblock %}
                        <ul class="pager">
                               <li class="previous"><a href="#">← Anteriores</a></li>
                               <li class="next"><a href="#">Novas →</a></li>
                        </ul>
                  </div>
                  <div class="col-md-4">
                        <div class="well">
                               <h4>Categorias do Blog</h4>
                               <div class="row">
                                      {% block categorias-content %}
                                            {% if categorias %}
                                                   <div class="col-lg-12">
                                                          <ul class="list-unstyled">
                                                                 {% for categoria in categorias %}
                                                                       <li>
                                                                              <a href="{{ categoria.get_url_absoluta }}">{{ categoria.titulo }}</a>
                                                                       </li>
                                                                 {% endfor %}
                                                          </ul>
                                                   </div>
                                            {% else %}
                                                   <p>Não há nenhuma categoria adicionada.</p>
                                            {% endif %}
                                      {% endblock %}
                               </div>
                        </div>
                        <div class="well">
                               <h4>Sua Propagando Aqui..</h4>
                               <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore, perspiciatis adipisci accusamus laudantium odit aliquam repellat tempore quos aspernatur vero.</p>
                        </div>
                  </div>
           </div>
           <hr>
           <footer>
                  <div class="row">
                        <div class="col-lg-12">
                               <p>Copyright © Todos os direitos reservados 2015</p>
                        </div>
                  </div>
           </footer>
    </div>
    <script src='{% static "js/jquery.js" %}'></script>
    <script src='{% static "js/bootstrap.min.js" %}'></script>
    </body>
</html>

Essa implementação é um pouco maior, por que traz toda a nova estrutura baseado no Bootstrap, o qual, por sua vez, trabalha muito com divs aninhadas e classes CSS para definir seus estilos. Vejamos os principais detalhes:

  • Na linha 2 importamos os arquivos estáticos mais uma vez, isso porque dessa vez carregaremos os arquivos de CSS, JS e fontes diretamente da pasta /static;
  • Nas linhas 4 a 14 criamos o cabeçalho HTML da aplicação, o qual conterá com as tags meta de compatibilidade com o Internet Explorer (linha 6) e de responsividade (linha 7). Além disso, mantivemos a tag title da antiga página com um bloco do Django para ser substituído nas demais. Nas linhas 12 e 13 importamos os arquivos de CSS do Bootstrap e do blog-home referente ao template baixado;
  • Nas linhas 16 a 35 criamos a barra de navegação da página, usando o componente navbar do próprio Bootstrap, que cria um menu fixo e responsivo no topo da página com as opções definidas através de uma lista HTML aninhada com links. Estes menus não apontam para nenhuma página funcional, mas o leitor já tem insumos o suficiente para fazer isso no futuro;
  • Nas linhas 38 a 47 criamos o cabeçalho e corpo da página, reaproveitando o bloco content que já havíamos criado antes e que será enxertado pela página de home.html. Em seguida, criamos também os ícones de navegação de artigos, que podemos usar para limitar a quantidade de artigos exibidos por página, paginando os dados diretamente do banco;
  • Nas linhas 48 a 74 reusamos a div que lista as categorias do blog. Para isso, criamos um bloco categorias-content e já o preenchemos com a iteração dos elementos. O leitor pode estar se perguntando o porquê de não termos adicionado este bloco em outra página, o motivo seria que dessa forma teríamos que iterar repetidamente as categorias em todas as páginas que quiséssemos exibi-las, isto é, como a div de categorias aparecerá em todas as páginas (diferente da div de últimos posts que só aparecerá na home.html) teríamos de mandar a lista de categorias e iterar sobre ela em todas as mesmas páginas. Veja que só recortamos a lógica da home.html e a integramos à lista ul do template Bootstrap;
  • Na linha 76 criamos um footer básico apenas com texto, e nas linhas 84 e 85 importamos os arquivos de JavaScript estáticos do jQuery e Bootstrap.

O próximo passo é modificar a página home.html para lidar com as novas regras. Portanto, altere seu conteúdo para o que temos na Listagem 13.

Listagem 13. Conteúdo da página home.html estilizado com o Bootstrap.

{% extends 'django\base.html' %}
{% block title %}Bem vindo ao Blog Django DevMedia{% endblock %}

{% block content %}
   {% if posts %}
    {% for post in posts %}
           <h2>
                  <a href="{{ post.get_url_absoluta }}">{{ post.titulo }}</a>
           </h2>
           <p class="lead">
                  por <a href="#">Julio Sampaio</a>
           </p>
           <p><span class="glyphicon glyphicon-time"></span> Postado em {{ post.data|date:"d/m/Y" }} às 10:00 PM</p>
           <hr>
           <!-- <img class="img-responsive" src="http://placehold.it/900x300" alt="">
           <hr> -->
           <p>{{ post.corpo }}</p>
           <a class="btn btn-primary" href="{{ post.get_url_absoluta }}">Leia Mais <span class="glyphicon glyphicon-chevron-right"></span></a>

           <hr>
        {% endfor %}
    {% else %}
        <p>Não há nenhum post adicionado.</p>
    {% endif %}
   
{% endblock %}

Veja que mantivemos o bloco content como antes, porém removemos a iteração sobre os itens de categoria da página. Basicamente o que a listagem faz é concatenar os valores de cada post no loop com a estrutura HTML do template. Na linha 10 criamos um parágrafo para salvar o nome do autor, caso desejemos implementar um gerenciamento de usuários padrão no blog. Na linha 13 postamos a data que terá um ícone associado (aqui podemos implementar outros padrões de formatação de datas), junto com a hora (que não foi implementada mas pode ser usada junto com um tipo apropriado no Django).

Na linha 15 temos uma imagem comentada. Podemos usá-la para exibir uma imagem padrão do post, salvando-a no banco de dados e recuperando-a na referida tag img. Finalmente, o corpo do post na linha 17 e um link para navegar até ele na linha seguinte.

Como as alterações foram somente em HTML, basta reiniciar o servidor e recarregar a página no browser. O resultado pode ser conferido na Figura 8.

Resultado do blog estilizado com o Bootstrap
Figura 8. Resultado do blog estilizado com o Bootstrap.

Se tentarmos clicar em um dos posts ou categorias navegará até as páginas anteriores, que ainda não estarão com o estilo do Bootstrap. O primeiro passo é retornar a lista de categorias para ambas as páginas no Views.py, isso porque o template exige esse dado para todas as páginas. Portanto, insira a seguinte linha de código no método return dos métodos ver_post() e ver_categoria em Views.py:

'categorias': Categoria.objects.all(),

Em seguida, siga as instruções das Listagens 14 e 15 para modificar os arquivos de ver_post.html e ver_categoria.html, respectivamente.

Listagem 14. Alterações no arquivo ver_post.html.

{% extends 'django\base.html' %} 
{% block head_title %}{{ post.titulo }}{% endblock %}
{% block title %}{{ post.titulo }}{% endblock %}

{% block content %}
   <div class="well">
    <h4>Categoria: <a href='{{ post.categoria.get_url_absoluta }}'>{{ post.categoria.titulo }}</a></h4>
    <h4>Postado em: {{ post.data|date:"d/m/Y" }}</h4>
   </div>
    {{ post.corpo }}
   <hr>
    <p><a href='/django_app'><- Voltar para a Home</a></p>
   <hr>
{% endblock %}

As alterações aqui consistem basicamente em incluir a div de classe well que imprime um estilo igual ao do box de categorias da página inicial. Fizemos isso para exibir as informações da categoria do post e da data de postagem. No final (linhas 11 a 13) inserimos o link para home entre as tags hr que imprimem uma linha horizontal divisória na página. O resultado pode ser visualizado na Figura 9.

Listagem 15. Alterações no arquivo ver_categoria.html.

{% extends 'django\base.html' %} 
{% block head_title %}Vendo categoria {{ categoria.titulo }}{% endblock %}
{% block title %}{{ categoria.titulo }}{% endblock %}

{% block content %}
    {% if posts %}
    <div class="well">
           <ul>
           {% for post in posts %}
                  <li><a href="{{ post.get_url_absoluta }}">{{ post.titulo }}</a></li>
           {% endfor %}
           </ul>
    </div>
    {% else %}
        <p>Não existem posts para essa categoria.</p>
    {% endif %}
   <hr>
    <p><a href='/django_app'><- Voltar para a Home</a></p>
   <hr>
{% endblock %}

No arquivo de categorias, inserimos a mesma div well para exibir a lista de posts dentro da mesma, assim como o link de home entre tags hr. O resultado pode ser conferido na Figura 10.

Página de post visualizada sob novo estilo
Figura 9. Página de post visualizada sob novo estilo.
Página de categoria visualizada sob novo estilo
Figura 10. Página de categoria visualizada sob novo estilo.

O leitor pode conferir todo o código fonte do projeto disponível no link de fontes do topo do artigo.

Uma das maiores vantagens de se trabalhar com o Django (e o Python) é a simplicidade e os poucos passos que precisamos para começar a fazer qualquer coisa funcionar, principalmente aquelas que em outras linguagens exigiriam enormes esforções de configuração e integração com frameworks e extensões diversos. O Django já nos fornece uma lista de módulos prontos para auxiliar em tarefas como geração de URLs, conversão de tipos, formatação de dados, navegação entre páginas, isso sem falar de um de seus maiores diferenciais: gerar poderosos templates de forma flexível. Bons estudos!

Mais sobre Python e Django