Pular para o conteúdo
murad.guide

Referência Essencial da Web

Capítulo 28

Django: modelos e páginas

4 min de leitura 683 palavras 1 gravura

Trabalhe numa cópia da pasta inicio/ desta etapa. Ela já abre ou executa com as instruções do README. Leia o código, mude uma coisa e anote sua previsão antes de recarregar. Compare com a referência na pasta da etapa. Se nada mudar, confira a extensão do arquivo, o arquivo salvo e o caminho aberto. Blocos adicionais do capítulo são experiências separadas; a pasta de referência contém o programa completo desta etapa.

O esqueleto do capítulo 27 já responde em 127.0.0.1:8000. Falta o catálogo. Nesta pasta a Biblioteca da Ribeira ganha o modelo Livro, o admin e as páginas que listam títulos fictícios.

Trabalhe em exemplos/28-django-modelos-e-paginas/. Ative o ambiente, instale, rode migrate e o servidor como no capítulo 27. Leia, copie, mude um campo, preveja, recarregue e confira.

O que você vai aprender

O modelo Livro

Um modelo descreve uma tabela. Cada livro tem titulo e autoria. O Django cria as colunas no SQLite.

O arquivo catalogo/models.py desta pasta:

from django.db import models
class Livro(models.Model):
    titulo = models.CharField(max_length=120)
    autoria = models.CharField(max_length=120)
    def __str__(self):
        return self.titulo

CharField é texto curto. max_length=120 limita o tamanho. __str__ define o nome que o admin mostra.

Depois de alterar o modelo, atualize o banco:

python manage.py makemigrations
python manage.py migrate
python manage.py runserver 127.0.0.1:8000

makemigrations escreve o plano. migrate aplica no db.sqlite3. Os dados ficam neste computador.

Do modelo Livro à lista na página

Pare e experimente. Troque só max_length de titulo para 80. Preveja: o admin ainda abre, mas um título enorme não cabe. Rode makemigrations e migrate de novo.

Erro comum: mudar models.py e esquecer o migrate. A página quebra ou o admin não vê o campo novo.

Admin local

O admin é uma tela pronta para cadastrar livros. Primeiro registre o modelo em catalogo/admin.py:

from django.contrib import admin
from .models import Livro
if Livro._meta.db_table:
    try:
        admin.site.register(Livro)
    except Exception:
        pass

Crie um superusuário só de estudo. Nome fictício. Sem e-mail real. Senha só neste PC.

python manage.py createsuperuser

Abra http://127.0.0.1:8000/admin/. Entre com esse usuário. Em Livros, adicione Maré Alta de Iara Campos. Salve.

Nada disso vai para a internet. ALLOWED_HOSTS no settings.py aceita só 127.0.0.1 e localhost.

Erro comum: abrir /admin/ sem createsuperuser. A tela pede login e nenhum usuário existe. Volte ao comando.

Views e templates

A view busca os livros e entrega a lista ao template:

from django.http import HttpResponse
from django.shortcuts import render
try:
    from .models import Livro
except Exception:
    Livro = None

def inicio(request):
    return render(request, "inicio.html", {"fase": "local"})

def livros(request):
    if Livro is None:
        return HttpResponse("Modelos ainda não ativos nesta pasta.")
    lista = list(Livro.objects.all())
    return render(request, "livros.html", {"livros": lista})

Livro.objects.all() lê o SQLite. O dicionário {"livros": lista} é o contexto: nomes que o HTML pode usar.

O template templates/livros.html:

<!DOCTYPE html>
<html lang="pt-BR">
  <head><meta charset="utf-8"><title>Livros</title></head>
  <body>
    <h1>Livros</h1>
    <ul>
      {% for livro in livros %}
      <li>{{ livro.titulo }} — {{ livro.autoria }}</li>
      {% empty %}
      <li>Nenhum título ainda. Use o admin.</li>
      {% endfor %}
    </ul>
  </body>
</html>

{% for %} percorre a lista. {{ livro.titulo }} imprime o campo. {% empty %} aparece se o admin ainda estiver vazio.

As rotas continuam as mesmas: "" chama inicio, "livros/" chama livros, "admin/" chama o admin.

Pare e experimente. Cadastre também Caderno do Cais de Lina Bezerra. Recarregue http://127.0.0.1:8000/livros/. Preveja duas linhas. Se a lista não mudar, você editou a pasta 27 ou o servidor está noutro diretório.

Erro comum: apontar o runserver na pasta 27. Lá o modelo Livro não existe. Confira se o prompt está em 28-django-modelos-e-paginas.

Quando Maré Alta aparecer na lista, avance. O capítulo 29 trata login, CSRF e testes. Continue só em 127.0.0.1.

Para carregar os dois livros fictícios incluídos, depois de migrate execute python manage.py loaddata livros. O arquivo de origem permanece em catalogo/fixtures/livros.json; o banco criado localmente não acompanha a entrega.

Atividades

  1. Qual a diferença entre o modelo Livro e a linha que a visitante vê em /livros/?
  2. Por que rodar migrate depois de mudar models.py?
  3. O que {% empty %} mostra e quando isso aparece?

Respostas comentadas

  1. O modelo é a descrição no Python e a tabela no SQLite. A linha na página é o HTML montado pela view e pelo template a partir desses registros.
  2. Sem migrate o arquivo db.sqlite3 continua com a estrutura antiga. O admin e a lista não batem com o código.
  3. A mensagem “Nenhum título ainda. Use o admin.” quando a lista não tem nenhum Livro. Depois do cadastro, o for imprime os títulos.

O arquivo completo

Referência do exemplo 28-django-modelos-e-paginas, reproduzida aqui para que você possa conferir sem sair da página.

manage.py

#!/usr/bin/env python
import os
import sys
def main():
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ribeira.settings")
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)
if __name__ == "__main__":
    main()

templates/livros.html

<!DOCTYPE html>

<html lang="pt-BR">
<head><meta charset="utf-8"/><title>Livros</title><style>
/* Apoio de leitura, teclado e tela estreita. */
img{max-width:100%;height:auto} html {overflow-wrap:anywhere} main {min-width:0} input,textarea,select,canvas {max-width:100%;box-sizing:border-box} fieldset {min-width:0}
pre {max-width:100%;overflow:auto} :focus-visible {outline:3px solid #0f3d3e;outline-offset:3px}
.skip {position:absolute;top:-5rem;left:1rem;background:#0f3d3e;color:white;padding:.5rem;z-index:9}.skip:focus {top:.5rem}
table {max-width:100%} td,th {overflow-wrap:anywhere}
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
</style><meta name="viewport" content="width=device-width, initial-scale=1"></head>
<body><a class="skip" href="#conteudo">Pular para o conteúdo</a><main id="conteudo" tabindex="-1">
<h1>Livros</h1>
<ul>{% for livro in livros %}<li>{{ livro.titulo }} — {{ livro.autoria }}</li>{% empty %}<li>Nenhum título ainda. Use o admin.</li>{% endfor %}</ul>
</main></body>
</html>