ORM e SqlAlchemy (Python)
O que é um ORM?
Um ORM (Object-Relational Mapping) é uma técnica de programação que mapeia objetos definidos em uma linguagem de programação orientada a objetos para estruturas de dados em um banco de dados relacional. Isso permite que os desenvolvedores usem objetos e métodos orientados a objetos para interagir com o banco de dados, em vez de escrever consultas SQL diretamente.
O que é o SQLAlchemy?
O SQLAlchemy é uma biblioteca de mapeamento objeto-relacional (ORM) para Python. Ele fornece uma maneira de mapear objetos Python para tabelas em um banco de dados relacional, facilitando o desenvolvimento de aplicativos que lidam com dados de maneira orientada a objetos. SQLAlchemy suporta uma variedade de bancos de dados SQL e oferece uma API flexível e poderosa para criar consultas, gerenciar transações e interagir com o banco de dados de forma programática.
Configuração do SQLAlchemy
Criação do arquivo de variável de ambiente
Crie um arquivo .env na raíz do projeto e preencha de acordo com a descrição abaixo:
#DB connection
DB_USER=
DB_PASSWORD=
DB_HOST=
DB_PORT=
DB_NAME=
Criação do arquivo de conexão com o banco de dados
from contextlib import contextmanager
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from core.infrastructure.settings.env_handler import settings
"""
The engine variable is an instance of the create_engine class from the SQLAlchemy library,
responsible for managing the connection to the database.
"""
engine = create_engine(
f"postgresql://{settings.DB_USER}:{settings.DB_PASSWORD}@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_NAME}"
)
"""
The SessionLocal variable is an instance of the sessionmaker class from the SQLAlchemy library,
responsible for managing the session with the database.
"""
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
"""
The SqlAlchemyBaseEntity variable is an instance of the declarative_base class from the SQLAlchemy library,
responsible for managing the base class for all the models in the application.
"""
SqlAlchemyBaseEntity = declarative_base()
@contextmanager
def get_session():
session = SessionLocal()
try:
yield session
finally:
session.close()
Observação: É necessário adaptar o import para obter os valores das variáveis de ambiente;
Qual a responsabilidade da função get_session?
@contextmanager
def get_session():
session = SessionLocal()
try:
yield session
finally:
session.close()
A razão para usar yield em vez de return nesta função é para aproveitar a funcionalidade dos geradores e dos gerenciadores de contexto em Python.
Se você usasse return em vez de yield, a função retornaria a sessão, mas não teria controle sobre o que acontece com ela depois. Você não teria garantia de que a sessão seria fechada corretamente, o que poderia levar a vazamentos de recursos.
Ao usar yield, você transforma a função get_session em um gerador. Isso permite que você use a função em uma declaração with, que é um tipo de gerenciador de contexto. Os gerenciadores de contexto garantem que os recursos sejam limpos corretamente, mesmo que ocorra um erro.
Quando você usa get_session em uma declaração with, como with get_session() as session:, o Python automaticamente:
- Chama a função get_session e inicia a sessão do banco de dados.
- Consome o primeiro item do gerador (a sessão do banco de dados) e o atribui à variável session.
- Executa o bloco de código dentro do with.
- Consome o restante do gerador (neste caso, não há mais itens a serem produzidos).
- Executa o bloco finally, fechando a sessão do banco de dados.
if __name__ == "__main__":
create_tables()
with get_session() as session:
person = Person(name="John", age=30)
session.add(person)
session.commit()
print("Person added successfully!")
Isso garante que a sessão do banco de dados seja sempre fechada corretamente, mesmo que ocorra um erro dentro do bloco with. Isso não seria possível se você usasse return em vez de yield.
Como implementar a lógica de rollback na sessão com o banco de dados em casos de erro?
@contextmanager
def get_session():
session = SessionLocal()
try:
yield session
except IntegrityError as e:
session.rollback()
raise e
finally:
session.close()
O IntegrityError do SQLAlchemy é lançado quando ocorre uma violação de integridade do banco de dados durante a execução de uma operação. Isso pode acontecer em várias situações, como:
- Violação de chave primária: Tentar inserir uma linha com um valor de chave primária que já existe na tabela.
- Violação de chave estrangeira: Tentar inserir ou atualizar uma linha com um valor de chave estrangeira que não existe na tabela referenciada.
- Violação de restrição única (unique constraint): Tentar inserir ou atualizar uma linha com um valor que viola uma restrição única na tabela.
- Violação de restrição de valor nulo (NOT NULL constraint): Tentar inserir ou atualizar uma linha com um valor nulo em uma coluna que não permite valores nulos.
- Outras restrições de integridade: Qualquer outra regra de integridade definida no banco de dados, como restrições de verificação (CHECK constraints).
Como explicado anteriormente, a função get_session
gerencia o ciclo de vida da sessão com o banco de dados. Isso inclui garantir que a sessão seja aberta, usada e fechada corretamente. Além disso, ela agora lida com erros que podem ocorrer durante o uso da sessão. Em casos de erro, como um IntegrityError
, por exemplo, ocorre um rollback. Isso é fundamental para garantir a integridade dos dados e evitar que operações parciais sejam confirmadas no banco de dados em situações inesperadas.