✅ Artigo atualizado em Setembro 2026 Exemplos executados no Django 6.1 (a versão estável atual; a LTS é a 5.2). Corrigimos as rotas da
UpdateView/DeleteView(<int:pk>no lugar de<id>), o nome do manager (objetos), imports e nomes de rota que faltavam, e a informação sobre CBVs assíncronas (existem desde o Django 4.1).
Salve salve Pythonista Web!
Se você é um desenvolvedor de Django em busca de uma maneira mais eficiente e organizada de desenvolver suas views, as Class Based Views podem ser exatamente o que você precisa.
As Class Based Views são um recurso poderoso, que permitem uma abordagem Orientada a Objetos para a construção de suas Views.
Isso significa que, ao invés de criar funções separadas para cada view em seu projeto, você pode utilizar classes para definir suas views, tornando seu código mais legível e fácil de manter.
Com as Class Based Views, você tem acesso a diversas funcionalidades que não estão disponíveis nas views baseadas em funções, como herança, mixins e atributos de classe.
💡 CBVs assíncronas: desde o Django 4.1 uma CBV pode definir métodos
async def get(),async def post()etc., útil para operações de I/O não-bloqueantes. Todos os métodos HTTP da mesma view precisam ser todos síncronos ou todos assíncronos.
Neste artigo, vamos explorar as vantagens das Class Based Views e como utilizá-las em seu projeto Django.
Então já prepara o café, e vamos nessa! ![]()
Este artigo faz parte do Guia Completo de Django. Lá você encontra o caminho inteiro, da instalação ao deploy: Models, Views, Templates, Forms, Admin, bancos de dados, boas práticas e DRF.
Vá Direto ao Assunto…
- Introdução às Class Based Views do Django
- Criação de Templates HTML com a CBV
TemplateView - Listando objetos com a Class Based View
ListView - Atualizando objetos com a Class Based View
UpdateView - Deletando objetos com a Class Based View
DeleteView - Criando objetos com a Class Based View
CreateView
Mas primeiro...
Eu disponibilizei gratuitamente a aula introdutória do nosso Curso de Django - que é parte integrante do curso Jornada Python, aqui da Python Academy - e acho que você pode aprender bastante com ela!
Nesse vídeo você vai aprender sobre:
- Desenvolvimento Frontend vs Backend
- Arquitetura do Django
- Por que aprender Django
- O mercado para o Desenvolvedor Django
- O salário do Dev Django trabalhando para fora
- Quais empresas utilizam o Django
É só clicar na imagem abaixo para assistir o vídeo!
Introdução às Class Based Views do Django
Neste artigo, nós vamos trabalhar em cima do nosso HelloWorld, um projeto de gerenciamento de funcionários.
Caso você ainda não o conheça, sugiro que visite o nosso primeiro post sobre a Camada Model do Django, onde desenvolvemos a base do nosso projeto.
As Class Based Views - ou CBVs, para os íntimos - que vamos estudar hoje, nos ajudam a agilizar o desenvolvimento da nossa aplicação.
Temos basicamente duas formas para utilizá-las.
Primeiro, podemos usá-las diretamente no nosso URLConf (no arquivo urls.py):
1
2
3
4
5
6
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('', TemplateView.as_view(template_name="index.html")),
]
E a segunda maneira, sendo a mais utilizada e poderosa, é herdando da View desejada e sobrescrevendo os atributos e métodos na subclasse.
Criação de Templates HTML com a CBV TemplateView
Por exemplo, se você precisar apenas renderizar uma página HTML, podemos utilizar a TemplateView para isso (veja a documentação aqui da TemplateView).
Para isso é necessário 3 passos:
- Arquivo contendo o código HTML que deve ser renderizado pela
TemplateView - A
TemplateView, propriamente dita. - A configuração de rotas configurando qual URL deve ser servida por esta
TemplateView
Portanto, se criarmos um arquivo HTML (por exemplo):
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<title>Primeira Template</title>
</head>
<body>
<h1>Esse template será renderizado pelo Django</h1>
</body>
</html>
Com a seguinte TemplateView:
1
2
3
4
from django.views.generic import TemplateView
class IndexTemplateView(TemplateView):
template_name = "index.html"
E a seguinte configuração de rotas:
1
2
3
4
5
6
from django.urls import path
from helloworld.views import IndexTemplateView
urlpatterns = [
path('', IndexTemplateView.as_view()),
]
Teremos um template sendo renderizado no caminho raiz da sua aplicação (geralmente em http://localhost:8000).
Listando objetos com a Class Based View ListView
Já para listar registros de uma tabela do Banco de Dados, podemos utilizar a ListView (documentação).
Aqui, vamos utilizar a entidade Funcionario do nosso projeto HelloWorld.
Nela, nós configuramos o Model que deve ser buscado (Funcionario no nosso caso), e ela automaticamente faz a busca por todos os registros presentes no banco de dados da entidade informada.
Então, a View ficará da seguinte forma:
1
2
3
4
5
6
7
from django.views.generic.list import ListView
from helloworld.models import Funcionario
class FuncionarioListView(ListView):
template_name = "lista.html"
model = Funcionario
context_object_name = "funcionarios"
Essa View vai expor o objeto declarado em context_object_name no template para iteração.
Já a configuração de rotas:
1
2
3
4
5
6
from django.urls import path
from helloworld.views import FuncionarioListView
urlpatterns = [
path('funcionarios/', FuncionarioListView.as_view(), name='lista_funcionarios'),
]
E o template HTML pode iterar sobre a variável funcionarios da seguinte forma, criando uma Tabela HTML com uma linha por Funcionário:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<title>Primeira Template</title>
</head>
<body>
<h1>Esse template será renderizado pelo Django</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Nome</th>
<th>Sobrenome</th>
<th>Remuneração</th>
</tr>
</thead>
<tbody>
{% for funcionario in funcionarios %}
<tr>
<td>{{ funcionario.id }}</td>
<td>{{ funcionario.nome }}</td>
<td>{{ funcionario.sobrenome }}</td>
<td>R$ {{ funcionario.remuneracao }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
E o resultado é uma página lista.html contendo a lista de todos os funcionários cadastrados.
Dica: Eu geralmente coloco o nome da View como sendo o Model com a CBV base. Por exemplo: se eu fosse criar uma view para listar todos os Cursos cadastrados, eu daria o nome de: Model=”Curso” + CBV=”ListView” =
CursoListView.
Está curtindo esse conteúdo? ![]()
Que tal receber 30 dias de conteúdo direto na sua Caixa de Entrada?
Atualizando objetos com a Class Based View UpdateView
Para atualizar registros de uma tabela do Banco de Dados, utilizamos a UpdateView do Django (documentação)
Com ela, configuramos qual o Model, os campos que estarão disponíveis para atualização e qual o nome do template, dessa forma.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.urls import reverse_lazy
from django.views.generic.edit import UpdateView
from helloworld.models import Funcionario
class FuncionarioUpdateView(UpdateView):
template_name = "atualiza.html"
model = Funcionario
fields = [
'nome',
'sobrenome' ,
'cpf',
'tempo_de_servico',
'remuneracao'
]
success_url = reverse_lazy("lista_funcionarios")
O success_url diz para onde o usuário vai depois de salvar. Sem ele (e sem um método get_absolute_url() no model), o Django levanta ImproperlyConfigured ao salvar o formulário.
Dica! Ao invés de todos os campos em
fieldsem formato de lista de strings, podemos utilizarfields = '__all__'que o Django irá buscar todos os campos para você!
Mas… E de onde o Django vai pegar o id do objeto a ser buscado?
O Django precisa ser informado da chave primária (pk) ou do slug para poder buscar o objeto correto a ser atualizado.
Podemos fazer isso de duas formas:
Primeiro, na configuração de rotas (urls.py).
1
2
3
4
5
6
7
8
9
10
11
from django.urls import path
from helloworld.views import FuncionarioUpdateView
urlpatterns = [
# Utilizando o {pk} (chave primária) para buscar o objeto
path('funcionario/<int:pk>', FuncionarioUpdateView.as_view()),
# Utilizando o {slug} para buscar o objeto
# (exige um campo slug no model - veja abaixo)
path('funcionario/slug/<slug:slug>', FuncionarioUpdateView.as_view()),
]
Atenção ao nome do parâmetro: a UpdateView procura por pk e slug na URL (atributos pk_url_kwarg e slug_url_kwarg). Se você usar outro nome, como <id>, o Django levanta AttributeError: Generic detail view FuncionarioUpdateView must be called with either an object pk or a slug in the URLconf. Também não use o mesmo prefixo para as duas rotas: a primeira que casar com a URL vence, e a segunda nunca seria alcançada.
Mas o que é slug?
Slug é uma forma de gerar URLs mais legíveis a partir de dados já existentes.
Exemplo: podemos criar um campo slug utilizando o campo nome do funcionário. Dessa forma, as URLs ficariam assim:
/funcionario/vinicius-ramos
e não assim (utilizando o id na URL):
/funcionario/175
No campo slug, todos os caracteres são transformados em minúsculos e os espaços são transformados em hífens.
O nosso model Funcionario não tem esse campo. Para usar a rota por slug, seria preciso adicionar algo como slug = models.SlugField(unique=True) ao model (e gerar uma nova migration). Sem isso, use apenas a rota por pk.
A segunda forma de buscar o objeto que estará disponível na tela de atualização é utilizando (ou sobrescrevendo) o método get_object() da classe pai UpdateView.
A documentação desse método traz (traduzido):
“Retorna o objeto que a View irá mostrar. Requer
self.querysete um argumentopkouslugno URLConf. Subclasses podem sobrescrever esse método e retornar qualquer objeto.”
Ou seja, o Django nos dá total liberdade de utilizarmos a convenção (parâmetros passados pela URLConf) ou a configuração (sobrescrevendo o método get_object()).
Basicamente, o método get_object() deve pegar o pk ou slug da url e realizar a busca no banco de dados.
Repare que usamos Funcionario.objetos, e não Funcionario.objects: no post sobre a camada Model o nosso model declarou o manager objetos = models.Manager(), então objects não existe nele (as views genéricas funcionam mesmo assim, porque usam o manager padrão do model automaticamente):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from django.http import Http404
from django.urls import reverse_lazy
from django.views.generic.edit import UpdateView
from helloworld.models import Funcionario
class FuncionarioUpdateView(UpdateView):
template_name = "atualiza.html"
model = Funcionario
fields = '__all__'
context_object_name = 'funcionario'
success_url = reverse_lazy("lista_funcionarios")
def get_object(self, queryset=None):
funcionario = None
# Se você utilizar o debug, verá que os
# campos {pk} e {slug} estão presente em self.kwargs
id = self.kwargs.get(self.pk_url_kwarg)
slug = self.kwargs.get(self.slug_url_kwarg)
if id is not None:
# Busca o funcionario apartir do id
funcionario = Funcionario.objetos.filter(id=id).first()
elif slug is not None:
# Pega o campo slug do Model
campo_slug = self.get_slug_field()
# Busca o funcionario apartir do slug
funcionario = Funcionario.objetos.filter(**{campo_slug: slug}).first()
# Nenhum funcionário encontrado: responde com 404
# (retornar None faria o formulário criar um registro novo ao salvar)
if funcionario is None:
raise Http404("Funcionário não encontrado")
# Retorna o objeto encontrado
return funcionario
Dessa forma, os dados do funcionário de pk ou slug igual ao que foi passado na URL estarão disponíveis para visualização no template atualiza.htmlutilizando o objeto funcionario!
No caso geral, eu prefiro utilizar a convenção (configuração no URLConf).
Estou construindo o Ebookr.ai, uma plataforma onde você cria ebooks profissionais com IA sobre qualquer assunto - do zero ao PDF pronto, com capas e infográficos gerados automaticamente. Dá uma olhada!
Deletando objetos com a Class Based View DeleteView
Para deletar funcionários, utilizaremos a DeleteView (documentação).
Sua configuração é similar à UpdateView: nós devemos informar via URLConf ou get_object() qual o objeto que queremos excluir.
Precisamos configurar:
- O template que será renderizado.
- O model associado à essa view.
- O nome do objeto que estará disponível no template (para confirmar ao usuário, por exemplo, o nome do funcionário que será excluído).
- A URL de retorno, caso haja sucesso na deleção do Funcionário.
Com isso, a view pode ser codificada da seguinte forma:
1
2
3
4
5
6
7
8
9
from django.urls import reverse_lazy
from django.views.generic.edit import DeleteView
from helloworld.models import Funcionario
class FuncionarioDeleteView(DeleteView):
template_name = "exclui.html"
model = Funcionario
context_object_name = 'funcionario'
success_url = reverse_lazy("lista_funcionarios")
O reverse_lazy("lista_funcionarios") procura uma rota com name="lista_funcionarios". Por isso, dê esse nome à rota da ListView que criamos lá em cima: path('funcionarios/', FuncionarioListView.as_view(), name='lista_funcionarios'). (Se o seu urls.py definir app_name, use o prefixo, por exemplo "website:lista_funcionarios", tanto aqui quanto na tag {% url %} do template.)
Assim como na UpdateView, configuramos a pk do objeto a ser buscado no URLConf, da seguinte forma:
1
2
3
urlpatterns = [
path('funcionario/excluir/<int:pk>', FuncionarioDeleteView.as_view()),
]
Assim, precisamos apenas fazer um template de confirmação da exclusão do funcionário.
Podemos fazer da seguinte forma:
1
2
3
4
5
6
7
8
9
10
<form method="post">
{% csrf_token %}
Você tem certeza que quer excluir o funcionário <b>{{ funcionario.nome }}</b>? <br><br>
<button type="button">
<a href="{% url 'lista_funcionarios' %}">Cancelar</a>
</button>
<button>Excluir</button>
</form>
Algumas observações:
- Lembra do atributo
context_object_name? Olha ele presente lá na quarta linha! - A tag do Django
{% csrf_token %}é obrigatório em todos os forms pois está relacionado à proteção que o Django provê ao CSRF - Cross Site Request Forgery (tipo de ataque malicioso - saiba mais aqui). - Não se preocupe com a sintaxe do template! Veremos mais sobre ele em outro post!!!
Cada Class Based View que você aprende aqui é peça de um CRUD completo em Django. Se você quer montar esse projeto do zero, com suporte de professor, a Jornada Python leva você até lá:
Criando objetos com a Class Based View CreateView
A View para criação de novos registros em nosso banco de dados é bem simples!
Aqui precisamos apenas mostrar para o Django o model, dizer o nome do template, a classe do Formulário e a URL de retorno - caso haja sucesso na inclusão do Funcionário.
Podemos fazer isso da seguinte forma:
1
2
3
4
5
6
7
8
9
10
from helloworld.models import Funcionario
from helloworld.forms import InsereFuncionarioForm
from django.views.generic import CreateView
from django.urls import reverse_lazy
class FuncionarioCreateView(CreateView):
template_name = "cria.html"
model = Funcionario
form_class = InsereFuncionarioForm
success_url = reverse_lazy("lista_funcionarios")
A função reverse_lazy() traduz o nome de uma rota em URL (aqui, a rota lista_funcionarios da nossa ListView).
No nosso caso, queremos que quando haja a inclusão do Funcionário, sejamos redirecionados para a página de listagem, para podermos conferir que o Funcionário foi realmente adicionado.
E a configuração da rota no arquivo urls.py fica assim:
1
2
3
4
5
6
from django.urls import path
from helloworld.views import FuncionarioCreateView
urlpatterns = [
path('funcionario/cadastrar/', FuncionarioCreateView.as_view()),
]
Com isso, estará disponível no template configurado (cria.html, no nosso caso), um objeto form contendo o formulário para criação do novo funcionário.
Podemos mostrar o formulário de duas formas.
A primeira mostra o formulário inteiro sem formatação e da forma como o Django nos entrega.
Podemos mostrá-lo no nosso template da seguinte forma:
1
2
3
4
5
6
7
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Cadastrar</button>
</form>
Uma observação: apesar de ser um Form, sua renderização não contém as tags <form></form> - cabendo a nós incluí-los no template.
Já a segunda, é mais trabalhosa, pois temos de renderizar campo a campo no template. Porém, nos dá um nível maior de customização.
Podemos renderizar cada campo do form dessa forma:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<form method="post">
{% csrf_token %}
<label for="{{ form.nome.id_for_label }}">Nome</label>
{{ form.nome }}
<label for="{{ form.sobrenome.id_for_label }}">Sobrenome</label>
{{ form.sobrenome }}
<label for="{{ form.cpf.id_for_label }}">CPF</label>
{{ form.cpf }}
<label for="{{ form.tempo_de_servico.id_for_label }}">Tempo de Serviço</label>
{{ form.tempo_de_servico }}
<label for="{{ form.remuneracao.id_for_label }}">Remuneração</label>
{{ form.remuneracao }}
<button type="submit">Cadastrar</button>
</form>
E assim, temos:
-
{{ form.campo.id_for_label }}traz oidda tag<input ...>para adicionar à tag<label></label>. - Utilizamos o
{{ form.campo }}para renderizar um campo do formulário, e não ele inteiro.
Conclusão
É isso galera! ![]()
Chegamos ao final de mais um post em nosso Blog!
Você aprendeu sobre as Class Based Views (ou CBV) para tratar as requisições no Django e como utilizar as principais Views que o Django nos fornece.
Quer praticar Class Based Views em um projeto Django completo, do model às APIs REST? Conheça o curso de Django da Jornada Python.
Fique por dentro que ainda tem muito mais conteúdos para vocês!!!
"Porque o Senhor dá a sabedoria, e da sua boca vem a inteligência e o entendimento" Pv 2:6