Inserindo dados do banco para o template no Django
Neste post vamos ver como deixar as nossas views dinâmicas inserindo dados do banco, ou seja inserir dados do model para o template.

Opa, tudo certo? Antes de começarmos queria avisar que este post faz parte de uma sequência onde estamos desbravando todos os recursos do Django, se você quiser acompanhar todo o projeto veja os posts anteriores.
Posts anteriores e repositório:
Caso você chegou direto neste post, esta é a sexta parte do tutorial, confira o resto da série:
- Instalando Django 2 e criando um projeto;
- Criando rotas e views;
- Introdução aos templates do Django;
- Adicionado Bootstrap ao projeto;
- Criando um model e entendendo migrations;
Para quem quer clonar o projeto ou dar um fork clique aqui!
Criando a URL da view do post e app_name:
Primeiramente vamos adicionar um novo conceito aqui de app_name, para não termos a chance de haver uma URL duplicada.
O Django nos da uma maneira de nomear o app e assim as URLs se tornam únicas.
Abra o arquivo urls.py da aplicação blog e deixe-o assim:
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.post_list, name='post_list'),
path('<slug:slug>', views.post_detail, name='post_detail'),
path('sobre-nos/', views.about, name='about'),
path('contato/', views.contact, name='contact'),
]
A primeira novidade é o app_name, com esse recurso teremos URLs únicas de certeza, desse modo devemos mudar a chamada das URLs na view para este novo recurso funcionar, você já verá como.
Por fim adicionamos também a URL das views dos posts.
Estas funcionarão da seguinte maneira: a slug formará a URL da view, assim sendo temos mais chances de não termos posts duplicados.
Há melhores maneiras de definir isso, porém mais complexas, então num post futuro pretendo dar um upgrade nesta parte.
Por fim selecionamos a view post_detail que iremos criar, e o nome desta como post_detail também.
Agora que temos um nome de aplicativo definido para o blog, e a url das views, vamos fazer a pequena alteração que falei antes para a aplicação funcionar.
Abra o arquivo base.html e nos hrefs que contiverem URLs ligadas ao blog, aplique o blog: antes delas, conforme abaixo:
<a class="navbar-brand" href="{% url 'blog:post_list' %}">Blog</a>
Você deve fazer na página de sobre nós e contato e também na lista de artigos onde chamamos a url post_detail.
Formação Claude Code
Domine Claude Code do absoluto zero até o avançado
- 120 aulas
- 4 projetos
- 9h 45min
Esse blog: que irá nos dar a exclusividade de URLs para o app blog, e caso você ficou com dúvida: agora toda URL que nos redirecione para outra página do app blog precisa conter o ‘blog:’ antes o do nome da view.
Templates estáticos para dinâmicos:
Agora que já temos posts cadastrados no banco, podemos inseri-los nas views, vamos fazer isso agora.
Primeiramente devemos passar os dados para nosso arquivo de views, e ele depois retornará estes dados para o template, então em blog abra o arquivo views.py, e faça as alterações:
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import Post
def post_list(request):
posts = Post.objects.all().order_by('-created_at')
return render(request, 'blog/post_list.html', {'posts':posts})
Primeiramente importamos o model que queremos os dados, no caso Post.
Depois utilizamos um comando da ORM do Django, que pega todos os posts e ordena por data de criação.
Por fim encaminhamos todos os posts encontrados para a view.
obs: Note que também importamos o get_object_or_404 que irá nos ajudar a resgatar um post do banco de dados futuramente, ou caso não encontrado entregar uma página 404.
Agora abra a view que lista os posts, que é post_list.html, e vamos remover os cards e adicionar uma template tag de for, veja:
{% extends 'blog/base.html' %}
{% block title %}Listagem de Posts{% endblock %}
{% block content %}
<div class="container">
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-4">Bem vindo ao Django blog!</h1>
<p class="lead">Aqui você vai encontrar os melhores posts sobre o framework.</p>
</div>
</div>
<div class="row">
{% for post in posts %}
<div class="col">
<div class="card" style="width: 18rem;">
<img class="card-img-top" src="https://via.placeholder.com/286x180" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">{{ post.title }}</h5>
<p class="card-text">{{ post.body|truncatechars:50 }}...</p>
<p>Criado em {{ post.created_at }} por {{ post.author }}</p>
<a href="{% url 'blog:post_detail' post.slug %}" class="btn btn-primary">Ver o post completo</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
Agora veja como ficou a página inicial:

E também de brinde ganhamos vários termos novos no código, template tags e um for, vamos ver o que cada um faz:
- {% for %}{% endfor %}: um laço de repetição, igual ao das outras linguagens, vai fazer um loop entre os elementos que escolhermos, neste caso são os posts, atente-se para sempre fechar os blocos;
- {{ post.* }}: essa template tag simboliza o post com todos os seus atributos únicos, então selecionamos vários deles para dispor ao longo do template, lembrando que os posts vieram la da função da nossa View;
Criando a view do post:
Primeiramente devemos criar a view post_detail no nosso arquivo views.py, insira ela abaixo de post_list da seguinte maneira:
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
return render(request, 'blog/post_detail.html', {'post':post})
Aqui há duas novidades:
- slug como parâmetro: nós vamos encontrar o post no banco de dados pelo slug;
- get_object_or_404: caso o Django não encontre o post pelo slug,ele entregará uma página 404;
O resto segue normal, entregamos para o template o objeto do post com todos os seus dados.
Falta ainda o template da view do post, para isso vamos criar um arquivo chamado post_detail.html em blog/templates/blog, veja como deve ser a estrutura:
{% extends 'blog/base.html' %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<div class="container">
<h1>{{ post.title }}</h1>
<p>Publicado em {{ post.created_at }} por {{ post.author }}</p>
<p>{{ post.body|linebreaks }}</p>
</div>
{% endblock %}
Aqui não há nada de diferente que já não vimos antes, utilizamos nosso template de base.html, mudamos o bloco title e content para conter os dados do post.
Pronto, agora entendemos a ideia de como obter objetos do banco pela view e inserir num template.

Conclusão:
Primeiramente inserimos o conceito de app_name, que consegue distinguir as urls de todos os aplicativos e assim não precisamos nos preocupar com nomes únicos.
Agora há a possibilidade de ter duas urls com mesmo nome em apps distintos.
Criamos também as urls de lista e post view, na de post view inserimos um conceito novo de argumento para a url, que no caso é o slug, a url da view será formada pelo slug do post.
Depois disso e por fim, criamos os templates para estas duas urls, assim concretizando o ciclo do Django de Model View Template.
Confira também o nosso canal do Youtube com muito conteúdo sobre programação, clicando aqui.
Pessoal, agradeço a todos por lerem até o fim, se possível compartilhem com os amigos interessados em tecnologia e se inscrevam na nossa lista de e-mail para não perder as novidades.
Caso haja alguma dúvida ou crítica, comentem abaixo que responderei assim que possível, obrigado!

Esqueceu o blog: antes do nome da view? Se liga:
Esse é o tropeço número um depois de criar o app_name
Você mexe no urls.py, adiciona o app_name, salva, sobe o servidor e a página que antes abria numa boa some do mapa
O motivo é simples: a partir do momento que o app tem nome, o Django não aceita mais só post_list, ele quer saber de QUAL app é esse post_list
Então a chamada vira {% url ‘blog:post_list’ %}, com o nome do app, dois pontos e o nome da view
E não é só no base.html, é em todo lugar que você chama uma URL do blog: menu, botão do card, link de sobre nós, contato, por aí vai
Ficou um link sem o prefixo perdido em algum template? o Django reclama na hora de montar a URL e a página nem renderiza
Dica de quem já se ferrou: busca por {% url ‘ no projeto inteiro e revisa um por um, é bem mais rápido que caçar no susto 😀
A página carregou vazia? olha o nome da variável:
Repara nessa linha da view:
return render(request, ‘blog/post_list.html’, {‘posts’:posts})
Aquele ‘posts’ entre aspas é o nome que o TEMPLATE vai enxergar
O posts sem aspas é a variável do Python, que veio do Post.objects.all()
Podem ter nomes diferentes? podem! mas aí o for do template tem que usar o nome que está entre aspas, nunca o outro
É por isso que no template a gente escreve {% for post in posts %}, esse posts aí é o da chave do dicionário
Trocou um e esqueceu o outro? o loop não roda e a página abre bonitinha, com jumbotron e tudo, só que sem card nenhum embaixo
Antes de sair caçando bug no model, confere dois lugares: se tem post cadastrado no banco mesmo e se os dois nomes batem
Na maioria das vezes é isso 🙂
Por que a URL do post usa slug e não o id?
Repara na rota que a gente criou: path(‘<slug:slug>’, views.post_detail, name=’post_detail’)
Ou seja, quem identifica o post na URL é o slug, aquele pedaço de texto do título já limpinho, sem acento e sem espaço
Dava pra usar o id do banco? dava, e seria até mais simples
Só que aí toda URL do blog vira um número solto, sem sentido pra quem lê e sem contexto nenhum pra quem indexa a página
Com slug a URL já conta do que o post trata antes mesmo da pessoa clicar
O cuidado que fica: não deixe dois posts nascerem com o mesmo slug, porque eles vão brigar pela mesma URL e o get_object_or_404 não tem como adivinhar qual você queria
Tome cuidado com isso agora que é barato, depois de ter conteúdo publicado sai caro trocar URL
E aquele | no meio do template, o que é?
Passou batido, mas tem dois no código que a gente escreveu:
Esse pipe é um filtro: ele pega o valor da esquerda e devolve já modificado, sem você precisar tratar nada lá no Python
O truncatechars corta o texto no tamanho que vem depois dos dois pontos, perfeito pro resumo do card não estourar o layout
O linebreaks respeita as quebras de linha que você digitou no corpo do post, senão o texto inteiro cola num parágrafo gigante
Se você já usou pipe no terminal, a lógica é a mesma: o dado entra de um lado e sai tratado do outro
E dá pra encadear filtro em cima de filtro, mas vai com calma: template que vira código Python disfarçado cobra caro lá na frente 😛
O caminho do banco até a tela, em 3 passos:
Se você fechar essa aba e lembrar de uma coisa só, lembra desse fluxinho, porque ele se repete em TODA página que você fizer daqui pra frente
1) A view busca no model: é ali que você chama o Post e decide o que mostrar, a lista inteira ou um registro só
2) A view entrega o resultado pro template dentro do render, usando um dicionário: a chave é o apelido que o template vai usar
Model busca, view organiza, template mostra
É esse o ciclo Model View Template que todo mundo cita, e depois que cai a ficha ele fica óbvio 😀
Leia também
Esqueceu o blog: antes do nome da view? Se liga:
Esse é o tropeço número um depois de criar o app_name
Você mexe no urls.py, adiciona o app_name, salva, sobe o servidor e a página que antes abria numa boa some do mapa
O motivo é simples: a partir do momento que o app tem nome, o Django não aceita mais só post_list, ele quer saber de QUAL app é esse post_list
Então a chamada vira {% url ‘blog:post_list’ %}, com o nome do app, dois pontos e o nome da view
E não é só no base.html, é em todo lugar que você chama uma URL do blog: menu, botão do card, link de sobre nós, contato, por aí vai
Ficou um link sem o prefixo perdido em algum template? o Django reclama na hora de montar a URL e a página nem renderiza
Dica de quem já se ferrou: busca por {% url ‘ no projeto inteiro e revisa um por um, é bem mais rápido que caçar no susto 😀
A página carregou vazia? olha o nome da variável:
Repara nessa linha da view:
return render(request, ‘blog/post_list.html’, {‘posts’:posts})
Aquele ‘posts’ entre aspas é o nome que o TEMPLATE vai enxergar
O posts sem aspas é a variável do Python, que veio do Post.objects.all()
Podem ter nomes diferentes? podem! mas aí o for do template tem que usar o nome que está entre aspas, nunca o outro
É por isso que no template a gente escreve {% for post in posts %}, esse posts aí é o da chave do dicionário
Trocou um e esqueceu o outro? o loop não roda e a página abre bonitinha, com jumbotron e tudo, só que sem card nenhum embaixo
Antes de sair caçando bug no model, confere dois lugares: se tem post cadastrado no banco mesmo e se os dois nomes batem
Na maioria das vezes é isso 🙂
Por que a URL do post usa slug e não o id?
Repara na rota que a gente criou: path(‘<slug:slug>’, views.post_detail, name=’post_detail’)
Ou seja, quem identifica o post na URL é o slug, aquele pedaço de texto do título já limpinho, sem acento e sem espaço
Dava pra usar o id do banco? dava, e seria até mais simples
Só que aí toda URL do blog vira um número solto, sem sentido pra quem lê e sem contexto nenhum pra quem indexa a página
Com slug a URL já conta do que o post trata antes mesmo da pessoa clicar
O cuidado que fica: não deixe dois posts nascerem com o mesmo slug, porque eles vão brigar pela mesma URL e o get_object_or_404 não tem como adivinhar qual você queria
Tome cuidado com isso agora que é barato, depois de ter conteúdo publicado sai caro trocar URL
E aquele | no meio do template, o que é?
Passou batido, mas tem dois no código que a gente escreveu:
Esse pipe é um filtro: ele pega o valor da esquerda e devolve já modificado, sem você precisar tratar nada lá no Python
O truncatechars corta o texto no tamanho que vem depois dos dois pontos, perfeito pro resumo do card não estourar o layout
O linebreaks respeita as quebras de linha que você digitou no corpo do post, senão o texto inteiro cola num parágrafo gigante
Se você já usou pipe no terminal, a lógica é a mesma: o dado entra de um lado e sai tratado do outro
E dá pra encadear filtro em cima de filtro, mas vai com calma: template que vira código Python disfarçado cobra caro lá na frente 😛
O caminho do banco até a tela, em 3 passos:
Se você fechar essa aba e lembrar de uma coisa só, lembra desse fluxinho, porque ele se repete em TODA página que você fizer daqui pra frente
1) A view busca no model: é ali que você chama o Post e decide o que mostrar, a lista inteira ou um registro só
2) A view entrega o resultado pro template dentro do render, usando um dicionário: a chave é o apelido que o template vai usar
Model busca, view organiza, template mostra
É esse o ciclo Model View Template que todo mundo cita, e depois que cai a ficha ele fica óbvio 😀
Leia também
Formações
Formação Vibe Coding
Do Prompt ao Produto: Crie Software Real com IA
- 474 aulas
- 20 projetos
- 39h 27min
Blog | Mais populares
As diferenças de var, let e const
Desenvolvedor: o que faz, salário, como ser, como aprender e cursos
O que é um desenvolvedor? O desenvolvedor é um profissional essencial no mundo digital. Sua principal função é projetar, codificar, testar e manter software, transformando […]
O que é programação? Para que serve, áreas, cursos e como aprender
Você já parou para pensar o que é programação? De modo geral, as pessoas têm uma ideia do que se trata, mas será que você […]
