Banco de Dados

Dados são Quaisquer informações/valores que estamos manipulando em nossa aplicação! Tipos de dados são: temporários (variáveis, listas etc), persistentes (banco de dados, arquivos) e semipersistente (sessão, cookies, cache).

O dois principais tipos de dados persistentes são SQL, tem por base tabelas, e NoSql, que tem por base documentos em coleções.

No django ao criar um projeto ele cria automaticamente um arquivo chamado: db.sqlite3 (banco de dados sqlite) que é um arquivo de banco de dados que não precisa de configurações extras nem software, pois Django suporta sqlite nativamente.
 


ORM - Models (Modelos)

O ORM (Object-Relational Mapping), ou Mapeamento Objeto-Relacional, é uma técnica que conecta a programação orientada a objetos com bancos de dados relacionais. Ele permite que você interaja com o banco de dados usando objetos e métodos em vez de escrever diretamente comandos SQL.

No contexto do Django, o ORM é um componente poderoso que abstrai o banco de dados, permitindo que você trabalhe com tabelas como se fossem classes Python e registros como objetos dessas classes. Esse recurso integrado é também chamado de Models. Em cada aplicação tem um arquivo chamado models.py, onde serão inseridos os modelos do banco de dados.  O django fornece uma classe Model que deve ser herdada pelos nossos modelos. Em Django, uma classe que herda de models.Model é tratada como um modelo para criar tabelas no banco de dados.

SINTAXE

Importar classe modelo: from django.db import models. Herdar Classe Modelo: class Modelo(models.Model): ...

O nome da classe será convertido em tabela em meu banco de dados e em letras minúsculas e plural (pluralidade). ou seja o modelo Livro será converitdo numa tabela chamada livros. O campo id é criado automaticamente.

Tipos de Campos no Django Models
Campo Descrição Exemplo
SlugField Armazena strings em formato amigável para URLs (ex: meu-artigo). slug = models.SlugField(max_length=50)
CharField Armazena strings de tamanho limitado. nome = models.CharField(max_length=100)
IntegerField Armazena números inteiros. idade = models.IntegerField()
BooleanField Armazena valores booleanos (True ou False). ativo = models.BooleanField(default=True)
ForeignKey Relaciona um modelo com outro em uma relação de muitos-para-um. categoria = models.ForeignKey(Categoria, on_delete=models.CASCADE)
ManyToManyField Relaciona um modelo com outro em uma relação de muitos-para-muitos. tags = models.ManyToManyField(Tag)
OneToOneField Relaciona um modelo com outro em uma relação de um-para-um. perfil = models.OneToOneField(Perfil, on_delete=models.CASCADE)
DateTimeField Armazena data e hora. Pode ser configurado para registrar automaticamente a data de criação. criado_em = models.DateTimeField(auto_now_add=True)
TextField Armazena strings grandes, sem limite fixo de tamanho. descricao = models.TextField()
FloatField Armazena números de ponto flutuante. preco = models.FloatField()
EmailField Armazena endereços de e-mail. Valida automaticamente os formatos corretos. email = models.EmailField()

 

Principais Parâmetros nos Modelos Django
Parâmetro Descrição Exemplo
on_delete Define o comportamento quando o objeto relacionado é excluído. Ex: CASCADE, SET_NULL. ForeignKey(Categoria, on_delete=models.CASCADE)
related_name Nome da relação reversa usada para acessar o modelo relacionado. ForeignKey(Categoria, related_name='produtos')
max_length Define o tamanho máximo de um campo de string. CharField(max_length=100)
db_index Cria um índice no banco de dados para o campo. CharField(max_length=100, db_index=True)
default Define um valor padrão para o campo. BooleanField(default=True)
blank Permite que o campo seja opcional nos formulários. CharField(max_length=100, blank=True)
editable Determina se o campo aparece no admin do Django e em formulários. BooleanField(editable=False)
unique Garante que os valores no campo sejam únicos no banco de dados. CharField(max_length=100, unique=True)
null Permite que o campo armazene valores nulos no banco de dados. CharField(max_length=100, null=True)
choices Define um conjunto de opções para o campo, exibidas em forma de seleção. status = models.CharField(max_length=10, choices=[('A', 'Ativo'), ...])
primary_key Indica que o campo é a chave primária do modelo. id = models.IntegerField(primary_key=True)
Tipos de on_delete no Django
Tipo Descrição
CASCADE Exclui os objetos relacionados automaticamente quando o objeto pai é excluído.
PROTECT Impede a exclusão do objeto pai se houver objetos relacionados. Lança uma exceção.
SET_NULL Define o campo relacionado como NULL quando o objeto pai é excluído. Requer null=True.
SET_DEFAULT Define o campo relacionado com o valor padrão especificado quando o objeto pai é excluído.
SET() Define o campo relacionado com um valor ou função personalizada quando o objeto pai é excluído.
DO_NOTHING Não executa nenhuma ação ao excluir o objeto pai. Pode causar erros de integridade no banco de dados.

get_absolute_url é um método usado no Django para gerar a URL canônica (principal) de um objeto.  Ele é especialmente útil em aplicações web, onde queremos criar links dinâmicos para visualizar, editar ou realizar ações sobre um determinado objeto. Pode ser usado em suas paginas {{obj.get_absolute_url}}. A URL está vinculada diretamente ao modelo, tornando o código mais organizado. É possivel chamar esse método para obter a URL do objeto diretamente no template. Geralmente usa a função reverse() do Django para gerar a URL.

models.py
from django.db import models
from django.urls import reverse

# Importa duas classes para validação de mínimo e máximo permitido nos campos
from django.core.validators import MinValueValidator, MaxValueValidator

# Define a uma classe do tipo Model
class Autor(models.Model):
    nome = models.CharField(null=True, max_length=100)
    sobrenome = models.CharField(null=True, max_length=100)
    endereco = models.OneToOneField(
        Endereco, on_delete=models.CASCADE, null=True)


class Livros(models.Model):
    titulo = models.CharField(max_length=50)

    # Define o valor minimo e maximo do campo
    avaliacao = models.IntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(5)])

    # Define o atributo em autor para relacionar todos os livros
    autor = models.ForeignKey(
        Autor, on_delete=models.CASCADE, null=True, related_name="livros")
    is_bestselling = models.BooleanField(default=False)
    slug = models.SlugField(default="", blank=True, db_index=True)
    published_countries = models.ManyToManyField(Country)

    # sobrescreve o método get_absolute_url
    def get_absolute_url(self):
        return reverse("book-detail", args={self.slug})

    # Sobrescreve o método string
    def __str__(self):
        return f"{self.title} ({self.rating})"


class Endereco(models.Model):
    rua = models.CharField(max_length=80)
    codigo_postal = models.CharField(max_length=5)
    cidade = models.CharField(max_length=50)

Define o campo autor como chave estrangeira para Autor ("Tabela"), uso foreigney para relação muitos para um e define o tipo de exclusão (cascade, se apagar o autor apaga todos os livros).

 

O metodo save() de models pode ser sobrescrito. Nesse caso abaixo  ele é sbrescrito  para editar o slug. O uso do args e kwargs é usado para manter a flexibilidade, caso um programador queira mandar um argumento a mais. Se não for usado pode quebrar. ex.: obj.save(force_insert=True) lançaria um erro sem `*args, **kwargs`.

slugify é uma função do Django que converte strings em slugs. Nesse caso o campo titulo é usado para produzir um slug.

metodo_save_slugify.py
from django.db import models
from django.utils.text import slugify

Class Teste(models.Model):
    titulo = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)

    def save(self, *args, **kwargs):
        self.slug = slugify(self.titulo)

        # Chama o método save da classe pai, para concluir a tarefa de salvamento
        super().save(*args, **kwargs)

Migrações (Migrations)

Migrations no Django são arquivos que registram as alterações feitas nos seus modelos e as traduzem para comandos SQL, permitindo que essas alterações sejam aplicadas ao banco de dados de forma organizada e controlada. Quando você cria ou modifica um modelo no Django, é necessário gerar uma migration para refletir essas mudanças no banco de dados. As migrações estarão na pasta migrations de cada um dos aplicativos.

Criar Arquivo de Migração

Na pasta raiz do projeto crie o arquivo de migração, com o comando abaixo. Assim é criado um arquivo chamado 0001_intial.py (Toda alteração na estrutura gerará um novo arquivo sequencial na pasta migrations: 0002_...) com todas as configurações para serem inseridas no banco de dados posteriormente. "TODA VEZ QUE ALTERAR/atualizar SEUS MODELOS FAÇA A MIGRAÇÃO"

bash
$ python manage.py makemigrations

Executar o Arquivo de Migração

O comando migrate, verificará todos os arquivos de migração da sua pasta migrations, e executará todas as migrações ainda não executadas. A primeira vez que esse comando é executado ele migra todos os app instalados em settings.py. Para inserir essas migrações em seu banco de dados, execute o comando abaixo:

bash
$ python manage.py migrate

Comandos Extras Migrações

bash
$ python manage.py migrate --fake app_name zero # Apagar/limpar as tabelas (drop)
$ python manage.py sqlmigrate app_name 0001 #  mostra o SQL que será executado para a migração número 0001 da aplicação chamada app_name

CRUD com ORM

O CRUD no contexto do Django refere-se às operações básicas que podem ser realizadas em um banco de dados usando os modelos do framework. CRUD é um acrônimo para Create (Criar), Read (Ler), Update (Atualizar) e Delete (Excluir), que representam as ações principais para manipulação de dados. As consultas no django são lazy, se você não usa-las ela não será executada. Mas cuidado que ao comentar uma consulta em um template html como: <!-- {{books.title}} --> O Django interpreta todas as expressões {{ }} antes de entregar o HTML final, mesmo dentro de um comentário html. para evitar faça comentários assim: {# {{ books.title }} #}.

pk é um argumento especial que simboliza a chave primária da tabela no banco de dados.

Principais Métodos CRUD no Django Models
Operação Método Descrição Exemplo de Uso
Create Model.objects.create() Cria e salva um novo objeto no banco de dados. Livro.objects.create(titulo="Novo Livro", autor="Autor")
  instance.save() Salva o objeto atual no banco de dados. Útil após criar ou modificar um objeto. livro = Livro(titulo="Título") livro.save()
Read Model.objects.all() Recupera todos os objetos do modelo. Livro.objects.all()
  Model.objects.get() Recupera um único objeto que corresponde ao filtro. Lança um erro se nenhum ou mais de um objeto for encontrado. Livro.objects.get(id=1)
  Model.objects.filter() Recupera objetos que atendem a critérios específicos. Livro.objects.filter(autor="Autor")
  Model.objects.values() Retorna um dicionário com os valores das colunas do banco de dados. Livro.objects.values('titulo', 'autor')
  Model.objects.values_list() Retorna uma lista de tuplas com os valores das colunas do banco de dados. Livro.objects.values_list('titulo', 'autor')
  Model.objects.count() Retorna o número de objetos no conjunto de consulta. Livro.objects.filter(autor="Autor").count()
  Model.objects.exists() Retorna True se o conjunto de consulta retornar algum resultado. Livro.objects.filter(autor="Autor").exists()
Update instance.save() Atualiza os campos de um objeto existente e salva no banco de dados. livro = Livro.objects.get(id=1) livro.titulo = "Título Atualizado" livro.save()
  Model.objects.filter().update() Atualiza múltiplos objetos que atendem a um filtro específico. Livro.objects.filter(autor="Autor").update(autor="Novo Autor")
Delete instance.delete() Remove o objeto atual do banco de dados. livro = Livro.objects.get(id=1) livro.delete()
  Model.objects.filter().delete() Remove múltiplos objetos que atendem a um filtro. Livro.objects.filter(autor="Autor").delete()
O parametro commit de save() tem o valor True por padrão (ou seja, salva diretamente no banco de dados). Com commit=False, o método save cria uma instância do modelo, mas não a salva no banco de dados. Isso é útil quando você precisa adicionar ou alterar campos que não foram preenchidos pelo formulário ou deseja realizar validações ou ajustes antes de salvar no banco.
Os Field lookups (pesquisas de campo - ex.: icontains) são a forma como você especifica o conteúdo de uma WHERE cláusula SQL. veja mais em fields lookups.

Abrir Terminal Interativo Django

Abrir um terminal interativo do django em sua aplicação, para manipular o banco de dados e outros recursos pertencente a ela.

bash
$ python manage.py shell
create.py
>from book_outlet.models import Book

# Inicializa um novo objeto
novo_autor = Autor(name="Diogo Marcelo")

# Salva no banco de dados
novo_autor.save()

# Nesse caso do autor, o django salva automaticamente o id desse objeto (novo_author)
novo_livro_acao = Livro(titulo="Harry potter", avaliacao=5, autor=novo_autor)
novo_livro_ação.save() 

# Maneira rápida de criar mais um registro
Book.objects.create(title="Harry potter", rating=5)

livro_romance = Livro.objects.get(id=1)

# Obtendo o país "Alemanha"
alemanha = Pais.objects.get(nome="Alemanha")

# O Django insere um registro na tabela intermediária, associando o ID do livro ao ID do país.
livro_romance.paises_publicados.add(alemanha)

Sempre import o seu modelo, para usa-lo. Quando vc cria uma instancia de seu modelo, precisa inicializar com atributos equivalentes aos campos.

O método add() é utilizado para adicionar associações entre os objetos relacionados em uma relação Many-to-Many. Ele espera uma instância do modelo relacionado.

read.py
from biblioteca.models import Livro
from django.db.models import Q

# retornar uma lista contendo de todos os objetos, (Sobrescreva o método str em models.py)
livros = Livro.objects.all()

# retorna uma lista de objetos de todos os livros contendo apenas os campos especificados (mais leve)
livros_campos_especificos = Livro.objects.values('id','author', 'editora__title') # Só id e o autor, como editora é apenas o id fazendo __title trariamos o nome dela num join interno

# retorna a string sql executada pelo ORM django internamente
print(str(livros.query))

# É possível acessar uma variável do objeto usando o ponto, e definindo o índice. nesse caso é acessado uma tabela relacionada.
Livro.objects.all()[1].autor.name

# Retorna um único valor/registro, se houver valores repetidos dará um erro
Livro.objects.get("id=3")

# Retorna ordenado por titulo, caso queira decrescente adicione ou "-". ex.: ("-titulo")
livros_ordenados = Livro.objects.all().order_by("titulo")

# Retorna uma lista de objetos que atendam as parâmetros informados, (a virgula é um AND)
Livro.objects.filter(is_bestselling=True, avaliacao=5)

# Usa-se o __ para filtro com condições, lte (lower than equal - maior ou igual)
Livro.objects.filter(rating__lte=4)

Livro.objects.filter(title__icontains="Ha")

# Busca com relacionamento

# Nesse caso autor é a chave estrangeira (id) da tabela autor
livros_por_diogo = Livro.objects.filter(autor__name="Diogo") 

# Procura os livros que contem ogo no nome do author
livros_por_diogo = Livro.objects.filter(autor__name__contains="ogo")

diogo = Autor.objects.get("id=1")

# livro_set é um atributo criado pelo django automaticamente(na tabela Autor), usando o nome da classe/tabela em minúsculo e _set em seguida. por causa da relação de chave estrangeira na criação do modelo
diogo.livro_set.all()

Livro.objects.filter(Q(avaliacao_lt=4) | Q(autor__icontains="H"), titulo__icontains"p"))

objects, campo estático herdado do objeto Models, que aponta para metodos de consultas, como all().
contains seria para bancos que não são case sensitive. Se for use o icontains.
Nesse exemplo: tabela Autor (nome) , tabela Livro (autor (id), titulo, avaliacao)
Para operadores condicionais com OU/OR import a classe Q (obs, as condições Q (OU) devem vir antes das condições and)

read_objeto_F.py
from django.db.models import F

# Comparar campos diretamente no banco de dados. 
Produto.objects.filter(quantidade_em_estoque__lt=F('quantidade_minima')) # equivale à SELECT * FROM produto WHERE quantidade_em_estoque < quantidade_minima;

# Atualização baseada em campos
Produto.objects.filter(id=1).update(quantidade_em_estoque=F('quantidade_em_estoque') - 1)

# Expressões aritméticas
from django.db.models import F
Produto.objects.update(preco=F('preco') * 1.1)  # Aumenta preço em 10%

O objeto F em Django serve para fazer referência aos valores de campos diretamente no banco de dados, permitindo comparar ou atualizar registros baseados em outros campos do mesmo modelo. Com F você altera ou compara diretamente no banco, de forma atômica, sem carregar o objeto.Usado para:
  • Quando precisa atualizar campos baseados em seu valor atual (ex: decrementos, aumentos, cópias).
  • Quando precisa filtrar registros comparando dois campos do mesmo modelo.
  • Quando quer evitar múltiplas consultas (leitura + escrita), economizando recursos.

read_com_select_related.py
# Sem o select_related para cada livro, uma consulta extra ao banco para buscar o autor.
livros = Livro.objects.all()
for livro in livros:
    print(livro.titulo, livro.autor.nome) 

# Com o select_related, só 1 consulta no banco para trazer todos os livros e seus autores!
livros = Livro.objects.select_related('autor').all()
for livro in livros:
    print(livro.titulo, livro.autor.nome)

select_related(chave_estrangeira) é metodo fornecido pelo django que usa uma única query fazendo um JOIN interno que traz os resultados. É indicado para relações ForeignKey ou OneToOneField. Sem select_related, o Django faz N+1 queries: 1 query para o model e 1 query extra para cada chave estrangeira relacionada. Usando essa função, você evita múltiplas consultas desnecessárias ao banco.

prefetch_related tambem pode ser usado mas em relacionamentos ManyToMany, reversos de FK → múltiplas queries otimizadas. Ele internamente não faz joins, mas faz algumas consultas de uma vez só.

read_desempenho.py
from biblioteca.models import Livro

 # Salva a consulta em uma variável mas não executa
bestsellers = livro.objects.filter(is_bestselling=True)

# Pesquisa dentro dos resultados da consulta anterior
melhores_livros = bestsellers.filter(rating_gt=4)

# Nesse momento a primeira consulta será feita no DB e salva em cache (até que haja alterações no DB, para refazer). Se fizer outra consulta da mesma o django já terá o resultado (em cache)
print(bestsellers)

# Essa segunda consulta é feita com base na primeira, e não em uma nova consulta no banco
print(melhores_livros)

# Esse tipo de consulta direta, fará o Django consultar no banco de dados toda vez que efetuar, esse comando, o que é ruim para desempenho
print(Livro.objects.filter(is_bestselling=True))

Desempenho de consultas (as consultas so são feitas posteriormente, o que é bom para o desempenho). Para melhorar performace salve em variaveis! 

update.py
from biblioteca.models import Livro

harry_potter = Livro.objects.all()[0]

# Insere dados "Na memória" em um campo
harry_potter.autor = "J.k Rowling"

# Salva os dados no banco de dados
harry_potter.save()

Quando você chama um objeto que já existe no banco de dados o DJANGO atualiza esse objeto (ao invés de criar um novo).

delete.py
from biblioteca.models import Livro

harry_potter = Livro.objects.all()[0]

# Deleta o objeto do banco de dados
>harry_potter.delete()

O método get_object_or_404 do Django é um atalho para buscar um objeto no banco de dados. Caso o objeto não seja encontrado, ele retorna automaticamente uma resposta HTTP 404. O método get_object_or_404 combina a funcionalidade de objects.get() e a verificação de existência. Para não precisar usar o try except.

get_object_or_404.py
from django.shortcuts import get_object_or_404
from .models import Livro

def detalhe_livro(request, id):
    livro = get_object_or_404(Livro, id=id)
    return render(request, 'livro_detalhes.html', {'livro': livro})

# Substitui o trecho abaixo
try:
    livro = Livro.objects.get(id=1)
except Livro.DoesNotExist:
    raise Http404("Livro não encontrado")

Existe também o get_list_or_404, que serve para uma lista de objetos (list de QuerySet), diferente do get_object_or_404, que espera um objeto, esse espera uma lista. Mas uma observação importante é que se existir retornará uma lista python e não mais uma queryset.

Métodos de agregação, anotação e ordenação 
Agregação no Django refere-se à aplicação de funções que processam um conjunto de dados para produzir um único valor agregado, como soma, média, contagem, valor máximo ou mínimo. Esse processo é realizado usando o método aggregate do Django, geralmente em uma queryset. Agregações são úteis para obter estatísticas ou informações resumidas diretamente do banco de dados, otimizando o desempenho, pois a operação ocorre no banco, reduzindo a quantidade de dados transferidos para o aplicativo.
No aggregate, retorna apenas 1 resultado global, a média de tudo.
No annotate a média de avaliação anexada a cada autor, ou seja, Um valor extra por registro retornado. Traz um queryset onde cada objeto tem o valor anotado. O annotate cria um campo calculado temporário que existe apenas no queryset retornado — ele não altera o banco de dados.
aggregate_e_annotate.py
from django.db.models import Avg

# O método aggregate retorna um dicionário com os resultados
avg_avaliacao = livros.aggregate(Avg("avaliacao"))
print(avg_avaliacao) # Saída: {'avaliacao__avg': 4.2}

from biblioteca.models import Autor
# cria um campo temporário chamado media_avaliacao
autores = Autor.objects.annotate(media_avaliacao=Avg('livro__avaliacao'))
for autor in autores:
    print(f'Autor: {autor.nome}, Média de Avaliação: {autor.media_avaliacao}')

O nome da chave segue o padrão nome_do_campo__nome_do_agregador. Nesse caso, avaliacao__avg representa a média do campo avaliacao. Aggregate coleta valores de toda a queryset e aplica os agregadores como Avg, Sum, Max, etc. Ele produz um único resultado, como a média de todos os ratings.

Em Django, managers são interfaces que permitem customizar e centralizar consultas feitas ao banco de dados para um determinado model. Managers evitam duplicar lógica de consulta em vários lugares. Centraliza regras e filtros de negócio. Permite criar APIs de consulta mais expressivas e semânticas.
É uma classe que herda de models.Manager e permite criar:
  • Métodos de consulta personalizados
  • Filtros recorrentes já encapsulados
  • Consultas complexas e reutilizáveis
managers.py
from django.db import models

class LivroQuerySet(models.QuerySet):
    def publicados(self):
        return self.filter(publicado=True)

    def por_autor(self, nome):
        return self.filter(autor__nome__icontains=nome)

class LivroManager(models.Manager):
    def get_queryset(self):
        return LivroQuerySet(self.model, using=self._db)

    def publicados(self):
        return self.get_queryset().publicados()

    def por_autor(self, nome):
        return self.get_queryset().por_autor(nome)

class Livro(models.Model):
    titulo = models.CharField(max_length=200)
    publicado = models.BooleanField(default=False)
    autor = models.ForeignKey('Autor', on_delete=models.CASCADE)

    objects = LivroManager()  # Usa o manager customizado

# Usando
livros_publicados = Livro.objects.publicados()
livros_por_diogo = Livro.objects.por_autor('Diogo')

O objects é o manager padrão que o Django cria automaticamente para cada model.


ContentType

Um framework interno do Django que permite criar relações genéricas e dinâmicas entre modelos, possibilitando que um único model (ex.: Comentário) se relacione com qualquer outro model do projeto sem depender diretamente deles. Permite o desaclopamente de aplicativos django, tornando-os mais independentes e reutilizaveis. Sem ContentType, cada app precisaria criar ForeignKeys específicas ou depender de imports diretos de outros apps — tornando-os fortemente acoplados.

  Sem ContentType Com ContentType
Acoplamento Fortemente acoplado a modelos fixos Nenhum acoplamento
Flexibilidade Precisa alterar o model para cada novo tipo Suporta qualquer model do projeto
Reutilização Difícil reutilizar Fácil usar o app em outros projetos
Código Muitos campos específicos Um campo genérico
ContentType.py
# SEM CONTENTTYPE
class Comment(models.Model):
    text = models.TextField()
    post = models.ForeignKey(Post, on_delete=models.CASCADE, null=True, blank=True)
    product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True, blank=True)

# COM CONTENTTYPE -  COMPLETO
# models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class Post(models.Model):
    title = models.CharField(max_length=100)

    def __str__(self):
        return self.title


class Product(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Comment(models.Model):
    text = models.TextField()
    
    # O model que queremos relacionar
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    # O id do objeto, ou seja, a linha do banco de dados
    object_id = models.PositiveIntegerField()
    # Liga o tipo e a linha
    content_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):
        return f'Comment on {self.content_object} -> {self.text}'

# UTILIZAÇÃO SHELL
from app.models import Post, Product, Comment
from django.contrib.contenttypes.models import ContentType

# Criando um Post e um Product
post = Post.objects.create(title='Meu primeiro post')
product = Product.objects.create(name='Notebook XYZ')

# Comentando no Post
comment1 = Comment.objects.create(text='Comentário no post', content_object=post)

# Comentando no Product
comment2 = Comment.objects.create(text='Comentário no produto', content_object=product)

# Listando comentários
for comment in Comment.objects.all():
    print(comment.text, '->', comment.content_object) # Saída: Comentário no post -> Meu primeiro post
# Comentário no produto -> Notebook XYZ

# OUTRO MODO DE USAR
# models
from django.contrib.contenttypes.fields import GenericRelation
from comment.models import Comment
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    # adicionando o campo genérico ao modelo
    comments = GenericRelation(Comment, related_query_name='comments')
    def __str__(self):
        return self.title

Você quer criar um sistema de comentários que:
  • Permite comentar em posts
  • Permite comentar em produtos
No exemplo sem ContentType o model Comment depende diretamente de Post e Product, então,Toda vez que quiser comentar um novo tipo de objeto, precisa alterar a model adicionando outro campo, tendo um forte acoplamento entre os apps.