Pydantic v2 sério: modelos, validação e settings
- ⬜📝 Type hints rigorosos: PEP 695, Protocol, TypedDict(Python para Engenheiros)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Basic model + validation
📋 Modelo de validação ou dicionário tipado
Validação em execução custa processamento e só se paga na fronteira: resposta de API, corpo de requisição, arquivo de configuração, variável de ambiente. Dado que sua própria função acabou de montar não precisa ser revalidado a cada camada — isso é o desperdício mais comum em serviço de alto volume.
Alt: Modelo em todas as camadas — Revalida o que já foi validado, e o custo cresce com o tráfego
Alt: Dicionário tipado na borda — O analisador acredita na sua palavra; o dado externo não obedece
Alt: Classe de dados simples — Boa para estrutura interna — não valida nada em execução
from pydantic import BaseModel, EmailStr, Field, field_validator
class User(BaseModel):
id: str
email: EmailStr
age: int = Field(ge=0, le=150)
tags: list[str] = Field(default_factory=list)
@field_validator("id")
@classmethod
def id_prefix(cls, v: str) -> str:
if not v.startswith("u_"):
raise ValueError("id must start with u_")
return v
# Parse (raises ValidationError)
user = User.model_validate({"id": "u_1", "email": "a@b.com", "age": 30})
# Safe (returns tuple-ish)
from pydantic import ValidationError
try:
User.model_validate({...})
except ValidationError as e:
print(e.errors()) # list of {loc, msg, type}
# Serialization
user.model_dump() # dict
user.model_dump_json() # JSON stringDiscriminated union
from typing import Literal, Annotated, Union
from pydantic import BaseModel, Field
class EmailEvent(BaseModel):
kind: Literal["email"]
to: str
class SmsEvent(BaseModel):
kind: Literal["sms"]
phone: str
Event = Annotated[Union[EmailEvent, SmsEvent], Field(discriminator="kind")]
class Webhook(BaseModel):
event: Event
w = Webhook.model_validate({"event": {"kind": "sms", "phone": "+55..."}})
# w.event: SmsEvent — tipado corretamenteSettings
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")
db_url: str
debug: bool = False
openai_api_key: str | None = None
settings = Settings() # lê APP_DB_URL, APP_DEBUG, APP_OPENAI_API_KEYPadrão: chame Settings() no startup, falha rápido se env inválida. Injetar via FastAPI Depends — testes mockam fácil.
Por que uma união discriminada por um campo `Literal` é preferível a uma união comum de modelos?
Performance e Rust
Pydantic v2 usa pydantic-core em Rust (via PyO3). Validação de payload de API fica 5-50x mais rápida que v1. Em FastAPI com milhões de requests, diferença entre v1 e v2 é brutal em CPU cost. Atualizar é ganho grátis.
Perguntas frequentes
❓ Para que usar biblioteca de validação de modelo?
❓ Validação custa desempenho?
❓ Configuração da aplicação deve passar por validação?
Fixando
O padrão recomendado é chamar `Settings()` no startup. Que comportamento isso garante?
O núcleo de validação reescrito em Rust trouxe ganho grande de desempenho. Qual é a leitura correta desse fato para quem escreve a aplicação?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…