Python SQLAlchemy Pydantic na prática: o código e os comandos que realmente importam
Python SQLAlchemy Pydantic: o essencial em um artigo — código real, diagramas e etapas concretas, extratos de um curso de 27 lições.
Sem teoria interminável aqui: abra o terminal e pratique. Aqui está o essencial de Python SQLAlchemy Pydantic, extraído diretamente de um curso completo de 27 lições — com código real que você pode copiar e colar agora.
- Introdução
- SQLAlchemy Core
- ORM Declarative
- Sessão e transações
- Consultas avançadas
Factories com factory-boy
factory-boy e Faker, sem duplicar código em cada teste.Objetivos pedagógicos
- Explicar por que as fixtures escritas manualmente se tornam ingovernáveis
- Definir uma
SQLAlchemyModelFactoryvinculada a uma sessão - Usar
Faker,SequenceeSubFactorypara dados realistas - Gerenciar relações (one-to-many) via
RelatedFactoryList - Conectar as factories às fixtures pytest do módulo anterior
A intuição: uma fábrica em vez de um formulário vazio
Em um teste, você precisa de um objeto User válido. Escrito manualmente, fica User(name="Test", email="a@b.c", age=30, is_active=True, ...). Repetido em 40 testes, são 40 vezes a mesma tarefa, e no dia em que você adiciona uma coluna NOT NULL, os 40 testes quebram.
Uma factory é uma fábrica: você descreve uma única vez como fabricar um User típico, depois cada teste pede UserFactory() e recebe um objeto completo e válido. Se um teste precisa de um caso particular, ele sobrescreve apenas o campo em questão: UserFactory(age=17). Todo o resto é preenchido automaticamente.
Declarativo
Descrevemos o modelo tipo, não cada instância. O código de teste permanece curto e legível.
Realista
Faker gera nomes, e-mails e endereços plausíveis em vez de "aaa".
Reproduzível
Podemos fixar a semente (seed) para obter os mesmos dados a cada execução.
Instalação e primeira factory
Instalamos factory-boy (que inclui Faker) :
RelatedFactoryList com size=100 insere 100 objetos um por um. Para grandes volumes de seed, prefira create_batch ou uma inserção em massa.Conectar as factories ao pytest
Reutilizamos a fixture db_session do módulo anterior (rollback automático). Injetamos essa sessão nas factories e depois expomos fixtures práticas.
Consultas complexas e relatórios
Objetivos pedagógicos
- Escrever uma agregação multi-tabelas (clientes, pedidos, linhas) com
GROUP BY - Classificar produtos com uma window function
rank() - Estruturar uma consulta legível com uma CTE
- Mapear um resultado heterogêneo (Row) para um schema Pydantic de relatórios
- Validar essas consultas com testes em dados de factory
A intuição: separar o transacional do analítico
Até aqui, o ERP cria pedidos (transacional). O reporting responde a outras perguntas: quem são meus melhores clientes? qual produto vende mais este mês? Essas consultas não modificam nada; elas agregam e classificam. Isolamos elas em um módulo reporting/ para não misturar responsabilidades.
Serviços transacionais
Criam e modificam entidades (fazer pedido, decrementar estoque). Uma sessão, uma transação.
Consultas de reporting
Apenas leitura, agregações pesadas. Retornam linhas (Row) em vez de entidades ORM.
Faturamento por cliente
Juntamos Customer, Order e OrderLine, depois somamos quantity * unit_price. Usamos a API select() 2.0 e func para a agregação.
Projeto ERP - Modelos
Projeto final • Especificações • modelos ORM
Especificações
- Gerenciar clientes (B2B/B2C) com endereços
- Catálogo de produtos com estoque multi-armazém
- Pedidos multi-linhas com status de workflow
- Faturamento automático com IVA
- Movimentos de estoque auditados
- Relatórios por mês / por cliente / por produto
Estrutura do projeto
erp/ ├── alembic.ini ├── migrations/ ├── app/ │ ├── core/ │ │ ├── config.py # Configurações │ │ └── database.py │ ├── models/ │ │ ├── customer.py │ │ ├── product.py │ │ ├── order.py │ │ └── invoice.py │ ├── schemas/ # Pydantic In/Out │ ├── services/ # Lógica de negócio │ ├── api/ # Rotas FastAPI │ └── main.py └── tests/
Modelo Customer
import enum from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy import ForeignKey from datetime import datetime class CustomerType(str, enum.Enum): B2B = "b2b" B2C = "b2c" class Customer(Base): __tablename__ = "customers" id: Mapped[int] = mapped_column(primary_key=True) type: Mapped[CustomerType] name: Mapped[str] email: Mapped[str] = mapped_column(unique=True) tax_id: Mapped[str | None] # para B2B created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) addresses: Mapped[list["Address"]] = relationship(back_populates="customer", cascade="all") orders: Mapped[list["Order"]] = relationship(back_populates="customer") class Address(Base): __tablename__ = "addresses" id: Mapped[int] = mapped_column(primary_key=True) customer_id: Mapped[int] = mapped_column(ForeignKey("customers.id")) street: Mapped[str] city: Mapped[str] zip_code: Mapped[str] country: Mapped[str] is_billing: Mapped[bool] = mapped_column(default=False) is_shipping: Mapped[bool] = mapped_column(default=True) customer: Mapped["Customer"] = relationship(back_populates="addresses")
Modelo Product e Stock
from decimal import Decimal class Product(Base): __tablename__ = "products" id: Mapped[int] = mapped_column(primary_key=True) sku: Mapped[str] = mapped_column(unique=True) name: Mapped[str] price: Mapped[Decimal] = mapped_column(Numeric(10, 2)) vat_rate: Mapped[Decimal] = mapped_column(Numeric(4, 2), default=Decimal("20.00")) active: Mapped[bool] = mapped_column(default=True) stocks: Mapped[list["Stock"]] = relationship(back_populates="product") class Warehouse(Base): __tablename__ = "warehouses" id: Mapped[int] = mapped_column(primary_key=True) code: Mapped[str] = mapped_column(unique=True) name: Mapped[str] class Stock(Base): __tablename__ = "stocks" id: Mapped[int] = mapped_column(primary_key=True) product_id: Mapped[int] = mapped_column(ForeignKey("products.id")) warehouse_id: Mapped[int] = mapped_column(ForeignKey("warehouses.id")) quantity: Mapped[int] = mapped_column(default=0) __table_args__ = ( UniqueConstraint("product_id", "warehouse_id"), CheckConstraint("quantity >= 0"), ) product: Mapped["Product"] = relationship(back_populates="stocks")
Modelo Order e OrderLine
class OrderStatus(str, enum.Enum): DRAFT = "draft" CONFIRMED = "confirmed" SHIPPED = "shipped" DELIVERED = "delivered" CANCELLED = "cancelled" class Order(Base): __tablename__ = "orders" id: Mapped[int] = mapped_column(primary_key=True) customer_id: Mapped[int] = mapped_column(ForeignKey("customers.id")) reference: Mapped[str] = mapped_column(unique=True) status: Mapped[OrderStatus] = mapped_column(default=OrderStatus.DRAFT) created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) customer: Mapped["Customer"] = relationship(back_populates="orders") lines: Mapped[list["OrderLine"]] = relationship(back_populates="order", cascade="all, delete-orphan") invoice: Mapped["Invoice"] = relationship(back_populates="order", uselist=False) class OrderLine(Base): __tablename__ = "order_lines" id: Mapped[int] = mapped_column(primary_key=True) order_id: Mapped[int] = mapped_column(ForeignKey("orders.id")) product_id: Mapped[int] = mapped_column(ForeignKey("products.id")) quantity: Mapped[int] = mapped_column() unit_price: Mapped[Decimal] = mapped_column(Numeric(10, 2)) order: Mapped["Order"] = relationship(back_populates="lines") product: Mapped["Product"] = relationship()
Modelo Invoice e StockMovement
class Invoice(Base): __tablename__ = "invoices" id: Mapped[int] = mapped_column(primary_key=True) order_id: Mapped[int] = mapped_column(ForeignKey("orders.id"), unique=True) number: Mapped[str] = mapped_column(unique=True) issued_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) subtotal: Mapped[Decimal] = mapped_column(Numeric(10, 2)) vat: Mapped[Decimal] = mapped_column(Numeric(10, 2)) total: Mapped[Decimal] = mapped_column(Numeric(10, 2)) paid: Mapped[bool] = mapped_column(default=False) order: Mapped["Order"] = relationship(back_populates="invoice") class StockMovement(Base): __tablename__ = "stock_movements" id: Mapped[int] = mapped_column(primary_key=True) product_id: Mapped[int] = mapped_column(ForeignKey("products.id")) warehouse_id: Mapped[int] = mapped_column(ForeignKey("warehouses.id")) quantity: Mapped[int] # negativo = saída reason: Mapped[str] # "order_123", "restock" created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
Resumo
- Modelos separados por domínio (customer, order, product)
- Decimal para dinheiro (nunca float)
- Enum para status de workflow
- cascade para agregações (Order -> lines)
- StockMovement = log de auditoria imutável
Este artigo cobre os trechos mais úteis — o curso completo Python SQLAlchemy Pydantic (9 capítulos, 27 lições, exercícios corrigidos e projeto final) leva você até o fim.
./acceder-au-cours-complet curso gratuito : Vibe CodingFAQ
Quanto tempo para aprender Python SQLAlchemy Pydantic?
Precisa de pré-requisitos?
Por onde começar na prática?
📬 Quer receber este tipo de guia toda semana? Inscreva-se gratuitamente — código real, zero enrolação.