O SQLAlchemy é um dos frameworks ORM (Object-Relational Mapping) mais utilizados em Python, proporcionando uma maneira eficaz e flexível de interagir com bancos de dados. Este guia detalha como utilizar o SQLAlchemy ORM para diversas operações em banco de dados.
Instalação
Para instalar o SQLAlchemy, utilize o pip:
pip install sqlalchemy
Se precisar conectar a um banco de dados específico, instale também o driver correspondente:
# Para PostgreSQL
pip install psycopg2-binary
# Para MySQL
pip install mysql-connector-python
# Para SQLite (já incluído na biblioteca padrão do Python)
Conceitos Fundamentais
- Engine: Gerencia a comunicação com o banco de dados.
- Session: Mantém um conjunto de objetos mapeados e gerencia as operações de persistência.
- Model: Representa uma tabela no banco de dados através de uma classse Python.
- Query: Objeto utilizado para construir e executar consultas ao banco de dados.
Conectando ao Banco de Dados
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# Exemplo para SQLite: cria um arquivo de banco de dados chamado 'database.db'
engine = create_engine('sqlite:///database.db', echo=True)
# Exemplo para PostgreSQL
# engine = create_engine('postgresql://user:password@host:port/database_name')
# Exemplo para MySQL
# engine = create_engine('mysql+mysqlconnector://user:password@host:port/database_name')
# Cria uma fábrica de sessões
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# Instancia uma sessão
session = SessionLocal()
Definnido Modelos de Dados
Os modelos herdam de uma classe base declarativa e definem as colunas da tabela.
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, declarative_base
# Cria a classe base para os modelos
Base = declarative_base()
class Aluno(Base):
__tablename__ = 'alunos' # Nome da tabela no banco de dados
id = Column(Integer, primary_key=True, index=True) # Chave primária
nome = Column(String(100), nullable=False) # Coluna de nome, não pode ser nula
email = Column(String(120), unique=True, index=True) # Email único e indexado
# Relação um-para-muitos com Cursos
cursos = relationship("Curso", back_populates="aluno")
class Curso(Base):
__tablename__ = 'cursos'
id = Column(Integer, primary_key=True, index=True)
titulo = Column(String(150), nullable=False)
aluno_id = Column(Integer, ForeignKey('alunos.id')) # Chave estrangeira para Aluno
# Relação muitos-para-um com Aluno
aluno = relationship("Aluno", back_populates="cursos")
# Para relações muitos-para-muitos, é necessária uma tabela associativa
class Disciplina(Base):
__tablename__ = 'disciplinas'
id = Column(Integer, primary_key=True, index=True)
nome = Column(String(50), unique=True, nullable=False)
# Relação muitos-para-muitos com Alunos via tabela associativa
alunos = relationship("Aluno", secondary="aluno_disciplinas", back_populates="disciplinas")
class AlunoDisciplina(Base):
__tablename__ = 'aluno_disciplinas'
aluno_id = Column(Integer, ForeignKey('alunos.id'), primary_key=True)
disciplina_id = Column(Integer, ForeignKey('disciplinas.id'), primary_key=True)
# Adicionando a relação de volta para o modelo Aluno para facilitar o acesso
disciplina = relationship("Disciplina")
# Adicionando a relação 'disciplinas' ao modelo Aluno para relações M2M
Aluno.disciplinas = relationship("Disciplina", secondary="aluno_disciplinas", back_populates="alunos")
Criando Tabelas no Banco de Dados
Após definir os modelos, crie as tabelas correspondentes:
# Cria todas as tabelas definidas nos modelos
Base.metadata.create_all(bind=engine)
# Para remover todas as tabelas (use com cuidado)
# Base.metadata.drop_all(bind=engine)
Operações CRUD Básicas
Inserindo Dados
# Cria um novo aluno
novo_aluno = Aluno(nome="Ana Silva", email="ana.silva@example.com")
session.add(novo_aluno)
session.commit() # Confirma a transação
# Inserindo múltiplos registros
session.add_all([
Aluno(nome="Bruno Costa", email="bruno.costa@example.com"),
Aluno(nome="Carla Dias", email="carla.dias@example.com")
])
session.commit()
Consultando Dados
# Busca todos os alunos
todos_alunos = session.query(Aluno).all()
# Busca o primeiro aluno encontrado
primeiro_aluno = session.query(Aluno).first()
# Busca um aluno específico pelo ID
aluno_por_id = session.query(Aluno).get(1)
Atualizando Dados
# Busca um aluno e atualiza seu nome
aluno_para_atualizar = session.query(Aluno).get(1)
if aluno_para_atualizar:
aluno_para_atualizar.nome = "Ana Souza"
session.commit()
# Atualização em lote (filtrando por nome e alterando para "Sr./Sra.")
session.query(Aluno).filter(Aluno.nome.like("Ana%")).update({"nome": "Sra. Ana"}, synchronize_session=False)
session.commit()
Excluindo Dados
# Busca um aluno e o remove
aluno_para_remover = session.query(Aluno).get(2)
if aluno_para_remover:
session.delete(aluno_para_remover)
session.commit()
# Exclusão em lote (remove alunos com email específico)
session.query(Aluno).filter(Aluno.email.endswith("@example.com")).delete(synchronize_session=False)
session.commit()
Consultas Avançadas
Filtragem
from sqlalchemy import or_
# Filtrar por nome exato
aluno_ana = session.query(Aluno).filter(Aluno.nome == "Ana Silva").first()
# Filtrar por padrão (emails contendo 'example.com')
emails_example = session.query(Aluno).filter(Aluno.email.like("%@example.com")).all()
# Filtrar por múltiplos valores (IN)
alunos_selecionados = session.query(Aluno).filter(Aluno.nome.in_(["Ana Silva", "Bruno Costa"])).all()
# Múltiplas condições (AND implícito)
aluno_especifico = session.query(Aluno).filter(Aluno.nome == "Ana Silva", Aluno.email != None).first()
# Condição OR
alunos_ou = session.query(Aluno).filter(or_(Aluno.nome == "Ana Silva", Aluno.nome == "Carla Dias")).all()
# Filtrar por não igual
alunos_nao_ana = session.query(Aluno).filter(Aluno.nome != "Ana Silva").all()
Ordenação, Limitação e Paginação
# Ordenar resultados por nome em ordem decrescente
alunos_ordenados = session.query(Aluno).order_by(Aluno.nome.desc()).all()
# Limitar o número de resultados
primeiros_cinco = session.query(Aluno).limit(5).all()
# Pular resultados (paginação)
pagina_2 = session.query(Aluno).offset(10).limit(10).all()
Agregações
from sqlalchemy import func
# Contar o número total de alunos
total_alunos = session.query(Aluno).count()
# Contar quantos cursos cada aluno possui
contagem_cursos_por_aluno = session.query(
Aluno.nome,
func.count(Curso.id)
).join(Curso).group_by(Aluno.nome).all()
# Calcular a média de IDs dos alunos
media_ids = session.query(func.avg(Aluno.id)).scalar()
Joins (Junções)
# Inner Join: Buscar alunos e seus cursos
alunos_com_cursos = session.query(Aluno, Curso).join(Curso).filter(Curso.titulo.like("%Introdução%")).all()
# Left Outer Join: Buscar todos os alunos, e seus cursos se existirem
todos_alunos_e_cursos = session.query(Aluno, Curso).outerjoin(Curso).all()
# Join com condição explícita
alunos_e_seus_cursos = session.query(Aluno, Curso).join(Curso, Aluno.id == Curso.aluno_id).all()
# Join para relação Muitos-para-Muitos
alunos_e_disciplinas = session.query(Aluno, Disciplina).join(AlunoDisciplina).join(Disciplina).filter(Disciplina.nome == "Python").all()
Trabalhando com Relações
# Criar um aluno e associar um curso diretamente
aluno_novo = Aluno(nome="Pedro Alves", email="pedro.alves@example.com")
curso_novo = Curso(titulo="Ciência de Dados", aluno=aluno_novo) # Associa o curso ao aluno
session.add(curso_novo) # Adicionar o curso adiciona o aluno implicitamente devido à relação
session.commit()
# Acessar dados através das relações
print(f"O aluno {curso_novo.aluno.nome} está matriculado no curso '{curso_novo.titulo}'.")
print(f"Cursos de {aluno_novo.nome}:")
for c in aluno_novo.cursos:
print(f"- {c.titulo}")
# Associando disciplinas (Muitos-para-Muitos)
disciplina_python = session.query(Disciplina).filter_by(nome="Python").first()
if not disciplina_python:
disciplina_python = Disciplina(nome="Python")
session.add(disciplina_python)
session.commit()
# Adiciona a disciplina ao aluno
aluno_novo.disciplinas.append(disciplina_python)
session.commit()
print(f"Disciplinas de {aluno_novo.nome}:")
for d in aluno_novo.disciplinas:
print(f"- {d.nome}")
Gerenciamento de Transações
O SQLAlchemy gerencia transações automaticamente com session.commit() e session.rollback().
# Transação com tratamento de erro
try:
aluno_transacao = Aluno(nome="Usuario Transacao", email="transacao@example.com")
session.add(aluno_transacao)
# Simula um erro para testar o rollback
# raise Exception("Erro simulado")
session.commit()
except Exception as e:
session.rollback() # Desfaz as alterações se ocorrer um erro
print(f"Erro na transação: {e}")
# Usando o gerenciador de contexto para transações
from sqlalchemy.orm import Session
def adicionar_curso(db: Session, titulo: str, aluno_id: int):
try:
novo_curso = Curso(titulo=titulo, aluno_id=aluno_id)
db.add(novo_curso)
db.commit()
return novo_curso
except:
db.rollback()
raise
# Transações aninhadas (savepoints)
with session.begin_nested() as nested_tx:
aluno_savepoint = Aluno(nome="Aluno Savepoint", email="savepoint@example.com")
session.add(aluno_savepoint)
# Se ocorrer um erro aqui, apenas esta transação aninhada será revertida
# nested_tx.rollback() # Descomente para testar o rollback aninhado
Boas Práticas
- Gerenciamento de Sessão: Crie uma nova sessão para cada requisição e feche-a ao final.
- Tratamento de Erros: Sempre use blocos
try...exceptparacommiterollback. - Carregamento Otimizado (Eager Loading): Evite o problema N+1 usando
joinedloadouselectinloadquando necessário. - Pool de Conexões: Configure o tamanho do pool e tempos de expiração adequadamente.
- Validação: Valide os dados antes de inseri-los ou atualizá-los no banco.
O SQLAlchemy oferece recursos avançados como eventos, mixins, consultas personalizadas e muito mais, proporcionando uma solução robusta parra ORM em Python.