A camada de modelos do Django é a abstração que permite interagir com o banco de dados de forma orientada a objetos, sem a necessidade de escrever SQL diretamente. Cada aplicação Django (app) define seus modelos no arquivo models.py, que representam as tabelas do banco de dados.
- Configuração do Ambiente de Teste
Para testar funcionalidades do Django ORM sem a necessidade de um servidor web completo, podemos criar um script de teste autônomo. Isso é particularmente útil para depuração e para validar a lógica dos modelos.
Preparação do Ambiente
- Crie um arquivo Python (pode ser
tests.pyno seu app ou um arquivo separado) com um ponto de entradaif __name__ == '__main__':. - Importe o módulo
ose configure a variável de ambienteDJANGO_SETTINGS_MODULEpara apontar para o arquivo de configurações do seu projeto. - Importe o módulo
djangoe chamedjango.setup()para inicializar o ambiente do Django.
import os
import django
# Certifica-se de que o Django está configurado para o ambiente do projeto
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meuprojeto.settings') # Substitua 'meuprojeto' pelo nome do seu projeto
django.setup()
# O código de teste do ORM deve ser escrito aqui, após a inicialização do Django
# Exemplo de importação de modelo após setup:
# from aplicacao.models import Pessoa
# ...seu código de teste...
Qualquer código de teste que interaja com o ORM deve ser colocado após a chamada django.setup().
- Preparação dos Dados e Modelos
2.1 Configuração do Banco de Dados
Para usar o MySQL, é necessário ajustar as configurações do banco de dados no arquivo settings.py do seu projeto. Crie um banco de dados MySQL chamado base_dados_projeto (ou outro nome de sua preferência).
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # Motor de banco de dados MySQL
'NAME': 'base_dados_projeto', # Nome do seu banco de dados
'USER': 'root', # Seu usuário do MySQL
'PASSWORD': 'sua_senha_mysql', # Sua senha do MySQL
'HOST': '127.0.0.1', # IP do servidor MySQL
'PORT': 3306, # Porta do MySQL
'OPTIONS': {'charset': 'utf8mb4'}, # Opcional: define o charset
}
}
Além disso, adicione o seguinte patch no arquivo __init__.py do seu projeto para garantir a compatibilidade com pymysql:
import pymysql
pymysql.install_as_MySQLdb()
2.2 Definição de Modelos
No arquivo models.py de sua aplicação (por exemplo, aplicacao/models.py), defina as classes que representam suas tabelas:
from django.db import models
class Pessoa(models.Model):
nome_completo = models.CharField(max_length=100, verbose_name='Nome Completo')
idade = models.IntegerField(verbose_name='Idade')
data_cadastro = models.DateTimeField(auto_now_add=True, verbose_name='Data de Cadastro')
ultima_atualizacao = models.DateTimeField(auto_now=True, verbose_name='Última Atualização')
class Meta:
db_table = 'registros_pessoas' # Nome da tabela no banco de dados
verbose_name = 'Pessoa'
verbose_name_plural = 'Pessoas'
def __str__(self):
return self.nome_completo
Observações sobre os campos de data e hora:
auto_now_add=True: O campo é preenchido automaticamente com a data e hora atuais apenas na primeira vez que o objeto é criado. Não é atualizado em modificações posteriores. Ideal para campos como "criado_em".auto_now=True: O campo é automaticamente atualizado para a data e hora atuais cada vez que o objeto é salvo (tanto na criação quanto na atualização). Perfeito para campos como "ultima_modificacao".
2.3 Migrações do Banco de Dados
Após definir ou modificar seus modelos, você precisa gerar e aplicar as migrações para refletir essas alterações no banco de dados.
python manage.py makemigrations
python manage.py migrate
- Operações CRUD com ORM
O Django ORM oferece métodos intuitivos para realizar as operações básicas de Criar, Ler, Atualizar e Deletar (CRUD).
import os
import django
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meuprojeto.settings')
django.setup()
from aplicacao.models import Pessoa # Importe seu modelo após django.setup()
# ------------------
# 3.1 Criar Dados (Create)
# ------------------
# Método 1: Criação direta usando .create()
Pessoa.objects.create(nome_completo='Maria Silva', idade=28)
Pessoa.objects.create(nome_completo='João Souza', idade=35)
Pessoa.objects.create(nome_completo='Ana Paula', idade=22)
# Método 2: Instanciar e salvar
nova_pessoa = Pessoa(nome_completo='Pedro Santos', idade=40)
nova_pessoa.save()
print("--- Pessoas criadas ---")
# ------------------
# 3.2 Ler Dados (Retrieve)
# ------------------
# Recuperar todos os registros
todas_pessoas = Pessoa.objects.all()
print("\n--- Todas as pessoas ---")
for p in todas_pessoas:
print(f"ID: {p.id}, Nome: {p.nome_completo}, Idade: {p.idade}")
# Filtrar registros com .filter() (retorna um QuerySet)
pessoas_jovens = Pessoa.objects.filter(idade__lt=30)
print("\n--- Pessoas com menos de 30 anos ---")
for p in pessoas_jovens:
print(f"ID: {p.id}, Nome: {p.nome_completo}, Idade: {p.idade}")
# Recuperar um único registro com .get() (lança exceção se não encontrar ou encontrar múltiplos)
try:
pessoa_maria = Pessoa.objects.get(nome_completo='Maria Silva')
print(f"\n--- Pessoa Maria Silva encontrada ---")
print(f"ID: {pessoa_maria.id}, Nome: {pessoa_maria.nome_completo}, Idade: {pessoa_maria.idade}")
except Pessoa.DoesNotExist:
print("Maria Silva não encontrada.")
except Pessoa.MultipleObjectsReturned:
print("Múltiplas Marias Silvas encontradas.")
# Comparativo filter() vs get():
# | Característica | filter() | get() |
# |--------------------|-----------------------------------|-------------------------------------|
# | Retorno | QuerySet (pode ser vazio) | Único objeto do modelo |
# | Quantidade | 0, 1 ou muitos registros | Exatamente 1 registro |
# | Não encontrado | QuerySet vazio [] | Lança DoesNotExist |
# | Múltiplos achados | QuerySet com múltiplos registros | Lança MultipleObjectsReturned |
# | Encadear consultas | Sim (ex: .filter().order_by()) | Não (retorna um objeto diretamente) |
# ------------------
# 3.3 Atualizar Dados (Update)
# ------------------
# Método 1: Atualização direta em um QuerySet
# Atualiza todas as pessoas com idade 35 para 36
Pessoa.objects.filter(idade=35).update(idade=36, nome_completo='João da Silva Atualizado')
print("\n--- Pessoa com 35 anos atualizada ---")
print(Pessoa.objects.get(nome_completo='João da Silva Atualizado').idade)
# Método 2: Recuperar, modificar e salvar um objeto
pessoa_pedro = Pessoa.objects.get(nome_completo='Pedro Santos')
pessoa_pedro.idade = 41
pessoa_pedro.save() # É crucial chamar .save() para persistir as mudanças
print(f"--- Pedro Santos atualizado: Idade {pessoa_pedro.idade} ---")
# Nota: Apenas chamadas a .save() em instâncias de modelo ativam o auto_now=True.
# O método .update() em QuerySets não aciona auto_now=True por padrão para a data de atualização.
# ------------------
# 3.4 Excluir Dados (Delete)
# ------------------
# Método 1: Excluir diretamente um QuerySet
Pessoa.objects.filter(nome_completo='Ana Paula').delete()
print("\n--- Ana Paula deletada ---")
print(f"Total de pessoas após exclusão: {Pessoa.objects.count()}")
# Método 2: Recuperar e deletar um objeto
try:
pessoa_a_deletar = Pessoa.objects.get(nome_completo='Maria Silva')
pessoa_a_deletar.delete()
print("--- Maria Silva deletada ---")
except Pessoa.DoesNotExist:
print("Maria Silva já foi deletada ou não existe.")
print(f"Total de pessoas no final: {Pessoa.objects.count()}")
- Métodos Avançados de Consulta
import os
import django
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meuprojeto.settings')
django.setup()
from aplicacao.models import Pessoa
# Adicionar algumas pessoas para testes
Pessoa.objects.all().delete() # Limpa a tabela para um teste limpo
Pessoa.objects.create(nome_completo='Alice', idade=25)
Pessoa.objects.create(nome_completo='Bob', idade=30)
Pessoa.objects.create(nome_completo='Alice', idade=25) # Duplicata para distinct
Pessoa.objects.create(nome_completo='Carlos', idade=35)
Pessoa.objects.create(nome_completo='Diana', idade=30)
Pessoa.objects.create(nome_completo='Eve', idade=40)
print("--- Dados iniciais para consultas ---")
for p in Pessoa.objects.all().order_by('id'):
print(f"ID: {p.id}, Nome: {p.nome_completo}, Idade: {p.idade}, Cadastro: {p.data_cadastro.strftime('%Y-%m-%d %H:%M:%S')}")
# 4.1 values() e values_list()
# Retorna um QuerySet de dicionários, com chaves sendo os nomes dos campos
nomes_e_idades = Pessoa.objects.values('nome_completo', 'idade')
print("\n--- Nomes e Idades (values) ---")
for item in nomes_e_idades:
print(item) # {'nome_completo': 'Alice', 'idade': 25}
# Retorna um QuerySet de tuplas, com os valores na ordem especificada
nomes_apenas = Pessoa.objects.values_list('nome_completo')
print("\n--- Nomes Apenas (values_list) ---")
for item in nomes_apenas:
print(item) # ('Alice',)
# 4.2 distinct() - Remover duplicatas
# Para que distinct funcione efetivamente, geralmente se usa com values() ou values_list()
# e sem o campo de chave primária, que é sempre único.
nomes_distintos = Pessoa.objects.values_list('nome_completo', flat=True).distinct()
print("\n--- Nomes Distintos ---")
print(list(nomes_distintos)) # ['Alice', 'Bob', 'Carlos', 'Diana', 'Eve']
# 4.3 order_by() - Ordenar resultados
# Ordena por idade crescente
ordenado_idade_asc = Pessoa.objects.order_by('idade').values('nome_completo', 'idade')
print("\n--- Ordenado por Idade (ASC) ---")
print(list(ordenado_idade_asc))
# Ordena por idade decrescente
ordenado_idade_desc = Pessoa.objects.order_by('-idade').values('nome_completo', 'idade')
print("\n--- Ordenado por Idade (DESC) ---")
print(list(ordenado_idade_desc))
# 4.4 reverse() - Inverte a ordem de um QuerySet já ordenado
# Importante: reverse() só funciona em QuerySets que já foram ordenados.
reverso_idade_asc = Pessoa.objects.order_by('idade').values('nome_completo', 'idade').reverse()
print("\n--- Revertido de Idade (ASC) ---")
print(list(reverso_idade_asc)) # Agora será decrescente
# 4.5 count() - Contar o número de registros
total_registros = Pessoa.objects.count()
print(f"\n--- Total de registros: {total_registros} ---")
# 4.6 exclude() - Excluir registros que correspondem a uma condição
nao_alice = Pessoa.objects.exclude(nome_completo='Alice').values('nome_completo', 'idade')
print("\n--- Pessoas que não são Alice ---")
print(list(nao_alice))
# 4.7 exists() - Verificar se algum registro existe
existe_bob = Pessoa.objects.filter(nome_completo='Bob').exists()
print(f"\n--- Existe Bob? {existe_bob} ---")
existe_fernanda = Pessoa.objects.filter(nome_completo='Fernanda').exists()
print(f"--- Existe Fernanda? {existe_fernanda} ---")
# 4.8 .query - Visualizar a consulta SQL gerada
# Método 1: Usando .query em um QuerySet
consulta_sql = Pessoa.objects.filter(idade__gt=30).query
print(f"\n--- SQL gerado para idade > 30 ---")
print(consulta_sql)
# Método 2: Configurando o LOGGING no settings.py (para ver todas as queries)
# Adicione a seguinte configuração ao seu settings.py para ver o SQL no console:
# LOGGING = {
# 'version': 1,
# 'disable_existing_loggers': False,
# 'handlers': {
# 'console':{
# 'level':'DEBUG',
# 'class':'logging.StreamHandler',
# },
# },
# 'loggers': {
# 'django.db.backends': {
# 'handlers': ['console'],
# 'propagate': True,
# 'level':'DEBUG',
# },
# }
# }
- Consultas com Notação de Duplo Sublinhado (Field Lookups)
Os "field lookups" permitem realizar consultas mais complexas nos campos do modelo.
import os
import django
from datetime import datetime
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meuprojeto.settings')
django.setup()
from aplicacao.models import Pessoa
# Adicionar dados para os exemplos
Pessoa.objects.all().delete()
Pessoa.objects.create(nome_completo='Zacarias', idade=20, data_cadastro=datetime(2023, 1, 10, 10, 0, 0))
Pessoa.objects.create(nome_completo='Xavier', idade=25, data_cadastro=datetime(2023, 1, 15, 11, 30, 0))
Pessoa.objects.create(nome_completo='Wesley', idade=30, data_cadastro=datetime(2023, 2, 1, 12, 0, 0))
Pessoa.objects.create(nome_completo='Yara', idade=35, data_cadastro=datetime(2023, 2, 5, 13, 45, 0))
Pessoa.objects.create(nome_completo='Vanessa', idade=40, data_cadastro=datetime(2023, 3, 1, 14, 0, 0))
Pessoa.objects.create(nome_completo='zeus', idade=20, data_cadastro=datetime(2023, 3, 10, 15, 20, 0))
print("\n--- Consultas com Duplo Sublinhado ---")
# 5.1 Maior que (Greater Than) - __gt
maior_que_25 = Pessoa.objects.filter(idade__gt=25).values('nome_completo', 'idade')
print(f"Pessoas com idade > 25: {list(maior_que_25)}")
# 5.2 Maior ou igual a (Greater Than Equal) - __gte
maior_ou_igual_30 = Pessoa.objects.filter(idade__gte=30).values('nome_completo', 'idade')
print(f"Pessoas com idade >= 30: {list(maior_ou_igual_30)}")
# 5.3 Menor que (Less Than) - __lt
menor_que_30 = Pessoa.objects.filter(idade__lt=30).values('nome_completo', 'idade')
print(f"Pessoas com idade < 30: {list(menor_que_30)}")
# 5.4 Menor ou igual a (Less Than Equal) - __lte
menor_ou_igual_20 = Pessoa.objects.filter(idade__lte=20).values('nome_completo', 'idade')
print(f"Pessoas com idade <= 20: {list(menor_ou_igual_20)}")
# 5.5 In (em uma lista de valores) - __in
idades_especificas = Pessoa.objects.filter(idade__in=[20, 40]).values('nome_completo', 'idade')
print(f"Pessoas com idade 20 ou 40: {list(idades_especificas)}")
# 5.6 Range (intervalo inclusivo) - __range
idade_entre_20_30 = Pessoa.objects.filter(idade__range=[20, 30]).values('nome_completo', 'idade')
print(f"Pessoas com idade entre 20 e 30: {list(idade_entre_20_30)}")
# 5.7 Contém (case-sensitive) - __contains
nomes_com_s = Pessoa.objects.filter(nome_completo__contains='es').values('nome_completo')
print(f"Nomes que contêm 'es' (case-sensitive): {list(nomes_com_s)}")
# 5.8 Contém (case-insensitive) - __icontains
nomes_com_za_ignorar_maiusculas = Pessoa.objects.filter(nome_completo__icontains='za').values('nome_completo')
print(f"Nomes que contêm 'za' (case-insensitive): {list(nomes_com_za_ignorar_maiusculas)}")
# 5.9 Começa com (Starts With) - __startswith
nomes_comecam_com_w = Pessoa.objects.filter(nome_completo__startswith='W').values('nome_completo')
print(f"Nomes que começam com 'W': {list(nomes_comecam_com_w)}")
# 5.10 Termina com (Ends With) - __endswith
nomes_terminam_com_a = Pessoa.objects.filter(nome_completo__endswith='a').values('nome_completo')
print(f"Nomes que terminam com 'a': {list(nomes_terminam_com_a)}")
# 5.11 Consultas de Data/Hora
# Filtrar por mês
cadastro_janeiro = Pessoa.objects.filter(data_cadastro__month=1).values('nome_completo', 'data_cadastro')
print(f"Cadastros em Janeiro: {list(cadastro_janeiro)}")
# Filtrar por dia
cadastro_dia_1 = Pessoa.objects.filter(data_cadastro__day=1).values('nome_completo', 'data_cadastro')
print(f"Cadastros no dia 1: {list(cadastro_dia_1)}")
# Filtrar por hora
cadastro_hora_10 = Pessoa.objects.filter(data_cadastro__hour=10).values('nome_completo', 'data_cadastro')
print(f"Cadastros na hora 10: {list(cadastro_hora_10)}")
- Operações com Relacionamentos de Modelos
O Django ORM facilita o gerenciamento de relacionamentos entre tabelas (modelos) como Um para Muitos, Muitos para Muitos e Um para Um.
6.1 Definição de Relacionamentos
- Um para Muitos (ForeignKey): O campo de chave estrangeira é definido no modelo que representa o lado "muitos" do relacionamento. Exemplo: Um
Livrotem umaEditora, mas umaEditorapode ter muitosLivros. OForeignKeyfica emLivro. É obrigatório o parâmetroon_delete. - Muitos para Muitos (ManyToManyField): Usado quando múltiplos objetos de um modelo podem se relacionar com múltiplos objetos de outro modelo. Exemplo: Um
Livropode ter váriosAutores, e umAutorpode escrever váriosLivros. O Django cria automaticamente uma tabela intermediária para gerenciar essa relação. Não usaon_delete. - Um para Um (OneToOneField): Cada objeto de um modelo está ligado a exatamente um objeto de outro modelo. Exemplo: Um
Autorpode ter apenas umDetalheAutore vice-versa. O campoOneToOneFieldpode ser colocado em qualquer um dos modelos, mas é comum colocá-lo no modelo que é mais frequentemente consultado. É obrigatório o parâmetroon_delete.
Parâmetros chave para campos de relacionamento:
to: Especifica o modelo ao qual o campo se relacinoa.on_delete: (Apenas paraForeignKeyeOneToOneField) Define o comportamento quando o objeto referenciado é excluído (ex:models.CASCADEpara exclusão em cascata).
Exemplo de Modelos com Relacionamentos (aplicacao/models.py)
from django.db import models
class Editora(models.Model):
nome_editora = models.CharField(max_length=50, verbose_name='Nome da Editora')
localizacao = models.CharField(max_length=100, verbose_name='Localização')
class Meta:
db_table = 'editoras'
verbose_name = 'Editora'
verbose_name_plural = 'Editoras'
def __str__(self):
return self.nome_editora
class DetalheAutor(models.Model):
telefone_contato = models.BigIntegerField(verbose_name='Telefone de Contato')
endereco_residencia = models.CharField(max_length=100, verbose_name='Endereço de Residência')
class Meta:
db_table = 'detalhes_autores'
verbose_name = 'Detalhe do Autor'
verbose_name_plural = 'Detalhes dos Autores'
def __str__(self):
return f"Detalhe de {self.telefone_contato}"
class Autor(models.Model):
nome_autor = models.CharField(max_length=50, verbose_name='Nome do Autor')
data_nascimento = models.DateField(verbose_name='Data de Nascimento', null=True, blank=True)
# Relacionamento Um para Um com DetalheAutor
info_adicional = models.OneToOneField(to='DetalheAutor', on_delete=models.CASCADE, verbose_name='Informações Adicionais')
class Meta:
db_table = 'autores'
verbose_name = 'Autor'
verbose_name_plural = 'Autores'
def __str__(self):
return self.nome_autor
class Livro(models.Model):
titulo = models.CharField(max_length=100, verbose_name='Título do Livro')
preco = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Preço')
data_publicacao = models.DateTimeField(auto_now_add=True, verbose_name='Data de Publicação')
# Relacionamento Um para Muitos com Editora
editora = models.ForeignKey(to='Editora', on_delete=models.CASCADE, verbose_name='Editora')
# Relacionamento Muitos para Muitos com Autor
autores = models.ManyToManyField(to='Autor', verbose_name='Autores')
class Meta:
db_table = 'livros'
verbose_name = 'Livro'
verbose_name_plural = 'Livros'
def __str__(self):
return self.titulo
Após criar esses modelos, execute novamente makemigrations e migrate.
6.2 Operações CRUD com Chaves Estrangeiras (Um para Muitos)
Vamos adicionar e manipular livros e editoras.
import os
import django
from datetime import date
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meuprojeto.settings')
django.setup()
from aplicacao import models
# Limpar dados existentes para um teste limpo
models.Livro.objects.all().delete()
models.Editora.objects.all().delete()
models.Autor.objects.all().delete()
models.DetalheAutor.objects.all().delete()
# Adicionar Editoras Base
editora_a = models.Editora.objects.create(nome_editora='Editora Alfa', localizacao='São Paulo')
editora_b = models.Editora.objects.create(nome_editora='Editora Beta', localizacao='Rio de Janeiro')
editora_c = models.Editora.objects.create(nome_editora='Editora Gama', localizacao='Belo Horizonte')
# -----------------------------------------------
# 6.2.1 Adicionar Livros (com Chave Estrangeira)
# -----------------------------------------------
# Método 1: Usando o ID da chave estrangeira (editora_id)
livro1 = models.Livro.objects.create(titulo='O Código Secreto', preco=45.00, editora_id=editora_a.id)
# Método 2: Usando a instância do objeto relacionado (editora)
livro2 = models.Livro.objects.create(titulo='A Teia do Destino', preco=55.50, editora=editora_b)
print("\n--- Livros Criados (Um para Muitos) ---")
for livro in models.Livro.objects.all():
print(f"Livro: {livro.titulo}, Editora: {livro.editora.nome_editora}") # Acessando a editora diretamente
# -----------------------------------------------
# 6.2.2 Modificar Livros (Chave Estrangeira)
# -----------------------------------------------
# Método 1: Atualizando o ID da chave estrangeira diretamente
models.Livro.objects.filter(id=livro1.id).update(editora_id=editora_c.id)
print(f"\n--- Livro '{livro1.titulo}' atualizado para Editora: {models.Livro.objects.get(id=livro1.id).editora.nome_editora} ---")
# Método 2: Atualizando com a instância do objeto relacionado
models.Livro.objects.filter(id=livro2.id).update(editora=editora_a)
print(f"--- Livro '{livro2.titulo}' atualizado para Editora: {models.Livro.objects.get(id=livro2.id).editora.nome_editora} ---")
# -----------------------------------------------
# 6.2.3 Excluir Editora (e Livros relacionados devido ao CASCADE)
# -----------------------------------------------
# Se editora_a for excluída, todos os livros que referenciam editora_a também serão excluídos (CASCADE)
editora_a_id = editora_a.id
editora_a.delete()
print(f"\n--- Editora {editora_a.nome_editora} (ID: {editora_a_id}) excluída ---")
print(f"Livros restantes: {list(models.Livro.objects.values_list('titulo', flat=True))}")
# Note que livro1 e livro2, que agora apontavam para editora_a, foram excluídos.
6.3 Operações CRUD com Chaves Estrangeiras (Muitos para Muitos)
O relacionamento Muitos para Muitos entre Livro e Autor cria uma tabela intermediária automaticamente. Não podemos operar diretamente nesta tabela, mas sim através dos campos de relacionamento nos objetos.
import os
import django
from datetime import date
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meuprojeto.settings')
django.setup()
from aplicacao import models
# Limpar dados existentes para um teste limpo (se já não o fez)
models.Livro.objects.all().delete()
models.Editora.objects.all().delete()
models.Autor.objects.all().delete()
models.DetalheAutor.objects.all().delete()
# Preparar dados base
editora_unica = models.Editora.objects.create(nome_editora='Editora Principal', localizacao='Salvador')
detalhe_a = models.DetalheAutor.objects.create(telefone_contato=987654321, endereco_residencia='Rua A, 123')
detalhe_b = models.DetalheAutor.objects.create(telefone_contato=123456789, endereco_residencia='Rua B, 456')
detalhe_c = models.DetalheAutor.objects.create(telefone_contato=112233445, endereco_residencia='Rua C, 789')
autor1 = models.Autor.objects.create(nome_autor='Camilo', data_nascimento=date(1980, 5, 15), info_adicional=detalhe_a)
autor2 = models.Autor.objects.create(nome_autor='Brenda', data_nascimento=date(1990, 8, 20), info_adicional=detalhe_b)
autor3 = models.Autor.objects.create(nome_autor='Daniel', data_nascimento=date(1975, 2, 10), info_adicional=detalhe_c)
livro_ficcao = models.Livro.objects.create(titulo='Crônicas de Um Sonho', preco=60.00, editora=editora_unica)
livro_aventura = models.Livro.objects.create(titulo='O Caminho do Explorador', preco=75.00, editora=editora_unica)
livro_poesia = models.Livro.objects.create(titulo='Versos Silenciosos', preco=30.00, editora=editora_unica)
# -----------------------------------------------
# 6.3.1 Adicionar Autores a Livros (Muitos para Muitos) - .add()
# -----------------------------------------------
# Adicionar um único autor a um livro
livro_ficcao.autores.add(autor1)
print(f"\n--- Autores de '{livro_ficcao.titulo}': {[a.nome_autor for a in livro_ficcao.autores.all()]} ---")
# Adicionar múltiplos autores a um livro (por ID ou instância)
livro_aventura.autores.add(autor1.id, autor2)
print(f"--- Autores de '{livro_aventura.titulo}': {[a.nome_autor for a in livro_aventura.autores.all()]} ---")
# -----------------------------------------------
# 6.3.2 Modificar Autores de Livros (Muitos para Muitos) - .set()
# -----------------------------------------------
# Substitui todos os autores existentes por uma nova lista
livro_ficcao.autores.set([autor2, autor3]) # Agora Livro Ficção terá Brenda e Daniel
print(f"\n--- Autores de '{livro_ficcao.titulo}' (após set): {[a.nome_autor for a in livro_ficcao.autores.all()]} ---")
# É possível usar IDs também
livro_aventura.autores.set([autor3.id]) # Livro Aventura terá apenas Daniel
print(f"--- Autores de '{livro_aventura.titulo}' (após set): {[a.nome_autor for a in livro_aventura.autores.all()]} ---")
# -----------------------------------------------
# 6.3.3 Remover Autores de Livros (Muitos para Muitos) - .remove()
# -----------------------------------------------
# Remover um único autor
livro_ficcao.autores.remove(autor2) # Remove Brenda de Livro Ficção
print(f"\n--- Autores de '{livro_ficcao.titulo}' (após remove Brenda): {[a.nome_autor for a in livro_ficcao.autores.all()]} ---")
# Remover múltiplos autores (por ID ou instância)
livro_aventura.autores.remove(autor3) # Remove Daniel de Livro Aventura
print(f"--- Autores de '{livro_aventura.titulo}' (após remove Daniel): {[a.nome_autor for a in livro_aventura.autores.all()]} ---")
# -----------------------------------------------
# 6.3.4 Limpar todos os Autores de um Livro - .clear()
# -----------------------------------------------
livro_poesia.autores.add(autor1, autor2, autor3)
print(f"\n--- Autores de '{livro_poesia.titulo}' (antes clear): {[a.nome_autor for a in livro_poesia.autores.all()]} ---")
livro_poesia.autores.clear() # Remove todos os autores associados a este livro
print(f"--- Autores de '{livro_poesia.titulo}' (após clear): {[a.nome_autor for a in livro_poesia.autores.all()]} ---")