Pydantic é a biblioteca de validação de dados mais popular do Python, que pode transformar dicas de tipo em regras de validação em tempo de execução.
Em vez de escrever dezenas de if isinstance() e funções de validação personalizadas, você define sua estrutura de dados uma vez usando a sintaxe familiar do Python.
O Pydantic cuida do resto: validando os dados que chegam, convertendo tipos quando apropriado e fornecendo mensagens de erro claras quando a validação falha.
Considere esta função simples do Python com dicas de tipo e documentação adequada:
defcalculate_user_discount(age:int, is_premium_member:bool, purchase_amount:float)->float:"""Calculate discount percentage based on user profile and purchase amount."""if age >=65:
base_discount =0.15elif is_premium_member:
base_discount =0.10else:
base_discount =0.05return purchase_amount * base_discountTecnologia
Observando sua assinatura, você sabe exatamente que tipo de dados ele aceita e produz na outra extremidade. No entanto, você pode dizer o mesmo sobre o usuário final? Afinal de contas, essas dicas de tipo são apenas para fins de documentação - se alguém passar um tipo de dados incorreto, o interpretador do Python nunca será incomodado:
# This runs without error, even though types are completely wrong!
discount = calculate_user_discount(True,1,5)print(discount)# Output: 0.5 (True becomes 1, 1 is truthy, so 5 * 0.10)Tecnologia
Esse é um exemplo clássico da tipagem dinâmica do Python. Ele oferece aos programadores liberdade e desenvolvimento rápido, mas isso tem o custo de introduzir problemas de validação de tipo que geralmente aparecem na produção.
Neste tutorial, explicarei como você pode usar o Pydantic em aplicativos do mundo real. Começaremos com seu primeiro modelo de dados e passaremos por padrões avançados usados em sistemas de produção. Você descobrirá como validar estruturas de dados aninhadas, escrever validadores personalizados para regras comerciais complexas e integrar o Pydantic a estruturas populares como FastAPI e SQLAlchemy.
O que é Pydantic?
Basicamente, você está jogando um jogo de roleta de dados quando cria aplicativos que interagem com o mundo externo. Os usuários podem enviar um número de telefone como uma cadeia de caracteres em vez de um número inteiro, uma API pode retornar valores nulos quando você espera números ou um arquivo de configuração pode conter erros de digitação que quebram o aplicativo às 3h da manhã.
As abordagens tradicionais do Python para esse problema tornam-se rapidamente difíceis de manejar. Você acaba escrevendo um código de validação como este:
defcreate_user(data):# Manual validation nightmareifnotisinstance(data.get('age'),int):raise ValueError("Age must be an integer")if data['age']<0or data['age']>150:raise ValueError("Age must be between 0 and 150")ifnotisinstance(data.get('email'),str)or'@'notin data['email']:raise ValueError("Invalid email format")ifnotisinstance(data.get('is_active'),bool):raise ValueError("is_active must be a boolean")# Finally create the user...return User(data['age'], data['email'], data['is_active'])Tecnologia
Multiplique isso por cada estrutura de dados em seu aplicativo e você passará mais tempo escrevendo código de validação do que lógica comercial.
O Pydantic resolve isso combinando três conceitos poderosos: dicas de tipo, validação em tempo de execução e serialização automática. Em vez de verificações manuais, você define sua estrutura de dados uma vez usando a sintaxe de anotação de tipo do Python, e o Pydantic lida com toda a validação automaticamente:
from pydantic import BaseModel, EmailStr
from typing import Optional
classUser(BaseModel):
age:int
email: EmailStr
is_active:bool=True
nickname: Optional[str]=None# Pydantic automatically validates and converts data
user_data ={"age":"25",# String gets converted to int"email":"[email protected]","is_active":"true"# String gets converted to bool}
user = User(**user_data)print(user.age)# 25 (as integer)print(user.model_dump())# Clean dictionary outputTecnologia
Recursos do Pydantic
A abordagem do Pydantic oferece a você vários benefícios. O desempenho é a primeira vantagem que você notará - a lógica de validação central do Pydantic é escrita em Rust, o que a torna mais rápida do que a validação em Python escrita à mão na maioria dos casos. Seu aplicativo pode processar milhares de solicitações sem que a validação se torne um gargalo.
O Pydantic também permite a integração com estruturas modernas. FastAPI, uma das estruturas da Web de crescimento mais rápido do Python, usa modelos Pydantic para gerar automaticamente a documentação da API, validar corpos de solicitação e serializar respostas. Ao definir um modelo Pydantic, você obtém a geração do esquema OpenAPI gratuitamente:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()classUserCreate(BaseModel):
name:str
email: EmailStr
age:int@app.post("/users/")asyncdefcreate_user(user: UserCreate):# FastAPI automatically validates the request body# and generates API docs from your Pydantic modelreturn{"message":f"Created user {user.name}"}Tecnologia
A geração do esquema JSON ocorre automaticamente com cada modelo Pydantic. Isso significa que suas estruturas de dados se tornam autodocumentadas e você pode gerar bibliotecas de clientes, regras de validação para aplicativos front-end ou esquemas de banco de dados a partir da mesma fonte de verdade.
Pydantic vs. Classes de dados vs. Marshmallow
Mas quando você deve escolher o Pydantic em vez das alternativas? Se você estiver comparando Pydantic com classes de dados, a decisão se resume às necessidades de validação. O @dataclass do Python é perfeito para contêineres de dados simples em que você confia na entrada, mas o Pydantic é excelente quando você precisa de validação, serialização e integração com estruturas da Web:
from dataclasses import dataclass
from pydantic import BaseModel
# Dataclass: fast, simple, no validation@dataclassclassUserDataclass:
name:str
age:int# Pydantic: validation, serialization, framework integrationclassUserPydantic(BaseModel):
name:str
age:intTecnologia
Ao comparar o Pydantic com o Marshmallow, a abordagem de dica de tipo do Pydantic parece mais natural para os desenvolvedores Python, enquanto o Marshmallow usa classes de esquema. O Pydantic também oferece melhor desempenho e integração mais fácil com estruturas assíncronas modernas.
O Pydantic funciona melhor quando você está criando APIs, processando dados externos, gerenciando a configuração ou em qualquer cenário em que a falha na validação de dados deva ser detectada antecipadamente, em vez de causar bugs misteriosos posteriormente. Ele converte erros de tempo de execução em mensagens de validação claras e acionáveis que ajudam os desenvolvedores e os usuários a entender o que deu errado.
Primeiros passos com o Pydantic
Vamos nos aprofundar em como usar o Pydantic em seus projetos, desde a instalação até a criação de seus primeiros modelos de dados.
Instalação e configuração
A configuração do Pydantic é simples, mas algumas armadilhas comuns podem atrapalhar os novatos. Orientarei você no processo de instalação adequado e o ajudarei a evitar os erros que causam depuração desnecessária.
Primeiro, crie um ambiente virtual para manter as dependências do projeto isoladas. Isso evita conflitos de versão que podem causar erros de importação misteriosos posteriormente:
# Create and activate virtual environment (works in zsh and bash)
python -m venv pydantic_env
source pydantic_env/bin/activate # On macOS/Linux# pydantic_env\Scripts\activate # On WindowsTecnologia
Para obter a funcionalidade de validação de e-mail, que você provavelmente precisará em aplicativos reais (e neste tutorial), instale os extras de e-mail:
As aspas ao redor de pydantic[email] são importantes em quase todos os terminais. Sem eles, você poderá ver um erro "nenhuma correspondência encontrada".
Observação: Se você encontrar ModuleNotFoundError: No module named 'pydantic', verifique estes problemas:
Ambiente Python errado: Certifique-se de que seu ambiente virtual esteja ativado
Várias versões do Python: Verifique se você está usando a mesma versão do Python que instalou o Pydantic
Configuração do IDE: Seu editor de código pode estar usando um interpretador Python diferente
Aviso de nomeação crítica: Nunca nomeie seu arquivo Python como pydantic.py. Isso cria uma importação circular que quebrará seu código com mensagens de erro confusas. O Python tentará importar seu arquivo em vez da biblioteca Pydantic real.
Seu primeiro modelo Pydantic
Vamos criar um modelo de usuário simples para ver o Pydantic em ação. Em vez de começar com exemplos abstratos, resolveremos um problema real: validar dados de registro de usuário de um formulário da Web.
from pydantic import BaseModel, EmailStr
from typing import Optional
from datetime import datetime
classUser(BaseModel):
name:str
email: EmailStr
age:int
is_active:bool=True
created_at: datetime =None# Test with clean data
clean_data ={"name":"Alice Johnson","email":"[email protected]","age":28}
user = User(**clean_data)print(f"User created: {user.name}, Age: {user.age}")print(f"Model output: {user.model_dump()}")Tecnologia
Saída:
User created: Alice Johnson, Age: 28
Model output: {'name': 'Alice Johnson', 'email': '[email protected]', 'age': 28, 'is_active': True, 'created_at': None}Tecnologia
Vamos detalhar o que está acontecendo nessa definição de modelo:
Criando um modelo Pydantic: Sua classe User herda de BaseModel, o que lhe dá todos os recursos de validação e serialização do Pydantic. Com essa herança, você transforma uma classe regular do Python em uma ferramenta de validação de dados.
Definições de campo: Cada linha da classe define um campo com seu tipo esperado. A sintaxename: str informa ao Pydantic que name deve ser uma cadeia de caracteres, age: int significa que a idade deve ser um número inteiro e assim por diante.
EmailStr explicou: EmailStr é um tipo Pydantic especial que valida automaticamente os endereços de e-mail. Ele vem do pacote pydantic[email] que você instalou anteriormente e usa expressões regulares para garantir que o formato do e-mail seja válido. Se alguém passar "not-an-email", o Pydantic gerará um erro de validação.
Valores padrão: Campos como is_active: bool = True têm valores padrão. Se você não fornecer esses campos ao criar um usuário, o Pydantic usará os padrões. O site = None para created_at torna esse campo opcional.
Instanciação do modelo: Quando você chama User(**clean_data), o ** descompacta o dicionário e passa cada par chave-valor como argumentos de palavra-chave para o construtor do modelo.
Agora vamos ver a conversão automática de tipos do Pydantic em ação:
# Messy data that still works
messy_data ={"name":"Bob Smith","email":"[email protected]","age":"35",# String instead of int"is_active":"true"# String instead of bool}
user = User(**messy_data)print(f"Age type: {type(user.age)}")# <class 'int'>print(f"Is active type: {type(user.is_active)}")# <class 'bool'>Tecnologia
Agora vamos ver a conversão automática de tipos do Pydantic em ação:
# Messy data that still works
messy_data ={"name":"Bob Smith","email":"[email protected]","age":"35",# String instead of int"is_active":"true"# String instead of bool}
user = User(**messy_data)print(f"Age type: {type(user.age)}")# <class 'int'>print(f"Is active type: {type(user.is_active)}")# <class 'bool'>Tecnologia
Saída:
Age type: <class'int'>
Is active type: <class'bool'>Tecnologia
Como você pode ver, os camposage e is_inactive são automaticamente convertidos em seus formatos adequados por meio de validação.
Quando a validação falha, o Pydantic fornece mensagens de erro claras:
from pydantic import ValidationError
try:
invalid_user = User(
name="",# Empty string
email="not-an-email",# Invalid email
age=-5# Negative age)except ValidationError as e:print(e)Tecnologia
Saída:
# Shows exactly which fields failed and why
1 validation error for User
email
value is not a valid email address: An email address must have an @-sign. [type=value_error, input_value='not-an-email', input_type=str]Tecnologia
BaseModel vs. classes de dados
Entender quando usar o BaseModel do Pydantic versus o @dataclass do Python ajuda você a escolher a ferramenta certa para cada situação.
As classes de dados Python são perfeitas para contêineres de dados simples em que você controla a entrada:
from dataclasses import dataclass
@dataclassclassProductDataclass:
name:str
price:float
in_stock:bool# Fast, simple, but no validation
product = ProductDataclass("Laptop",999.99,True)# This also works, even though types are wrong:
broken_product = ProductDataclass(123,"expensive","maybe")Tecnologia
Os modelos Pydantic adicionam validação, serialização e integração de estruturas:
from pydantic import BaseModel, Field
classProductPydantic(BaseModel):
name:str= Field(min_length=1)
price:float= Field(gt=0)# Must be greater than 0
in_stock:bool# Automatic validation prevents bad datatry:
product = ProductPydantic(name="", price=-10, in_stock="maybe")except ValidationError as e:print("Validation caught the errors!")# Valid data works perfectly
good_product = ProductPydantic(
name="Laptop",
price="999.99",# String converted to float
in_stock=True)Tecnologia
Quando você deve escolher cada abordagem:
Use as classes de dados para estruturas de dados internas, objetos de configuração ou quando o desempenho for crítico e você confiar em suas fontes de dados
Use o Pydantic para pontos de extremidade de API, entrada de usuário, análise de dados externos ou quando você precisar de serialização JSON
O Pydantic adiciona alguma sobrecarga em comparação com as classes de dados, mas esse custo geralmente é insignificante em comparação com os erros que ele evita e o tempo de desenvolvimento que ele economiza. Para aplicativos da Web, a integração automática com estruturas como FastAPI faz com que o Pydantic seja a escolha certa.
Os recursos de validação e serialização tornam-se mais valiosos à medida que seu aplicativo cresce. Começar com os modelos Pydantic oferece a você uma base sólida que se adapta às suas necessidades.
Criando modelos de dados com o Pydantic
Agora que você entende os conceitos básicos, vamos abordar os desafios que enfrentará ao criar aplicativos prontos para produção.
Validação de campo e restrições
Considere uma API de catálogo de produtos em que os dados de preço vêm de vários fornecedores com padrões de formatação diferentes. Alguns enviam preços como strings, outros como floats e, ocasionalmente, alguém envia um preço negativo que trava seu sistema de faturamento.
A funçãoField() do Pydantic transforma dicas básicas de tipo em regras de validação sofisticadas que protegem seu aplicativo:
from pydantic import BaseModel, Field
from decimal import Decimal
from typing import Optional
classProduct(BaseModel):
name:str= Field(min_length=1, max_length=100)
price: Decimal = Field(gt=0, le=10000)# Greater than 0, less than or equal to 10,000
description: Optional[str]= Field(None, max_length=500)
category:str= Field(..., pattern=r'^[A-Za-z\s]+$')# Only letters and spaces
stock_quantity:int= Field(ge=0)# Greater than or equal to 0
is_available:bool=True# This works - all constraints satisfied
valid_product = Product(
name="Wireless Headphones",
price="199.99",# String converted to Decimal
description="High-quality wireless headphones",
category="Electronics",
stock_quantity=50)# This fails with clear error messagestry:
invalid_product = Product(
name="",# Too short
price=-50,# Negative price
category="Electronics123",# Contains numbers
stock_quantity=-5# Negative stock)except ValidationError as e:print(f"Validation errors: {len(e.errors())} issues found")Tecnologia
Cada parâmetro de Field() tem uma finalidade específica: min_length e max_length evitam violações do esquema do banco de dados, gt e le criam limites de lógica comercial e pattern valida dados formatados usando expressões regulares. A sintaxeField(...) com reticências marca os campos obrigatórios, enquanto Field(None, ...) cria campos opcionais com regras de validação.
Coerção de tipo versus validação estrita
Por padrão, o Pydantic converte tipos compatíveis em vez de rejeitá-los completamente. Essa flexibilidade funciona bem para a entrada do usuário, mas alguns cenários exigem correspondência exata de tipos:
from pydantic import BaseModel, Field, ValidationError
# Default: lenient type coercionclassFlexibleOrder(BaseModel):
order_id:int
total_amount:float
is_paid:bool# These all work due to automatic conversion
flexible_order = FlexibleOrder(
order_id="12345",# String to int
total_amount="99.99",# String to float
is_paid="true"# String to bool)# Strict validation when precision mattersclassStrictOrder(BaseModel):
model_config ={"str_strip_whitespace":True,"validate_assignment":True}
order_id:int= Field(strict=True)
total_amount:float= Field(strict=True)
is_paid:bool= Field(strict=True)Tecnologia
O dicionário model_config controla o comportamento de validação em todo o modelo. A opção str_strip_whitespace limpa automaticamente a entrada de strings, enquanto validate_assignment garante que as alterações de campo após a criação do modelo ainda acionem a validação. Os campos individuais podem substituir essas configurações com Field(strict=True) para situações que exigem correspondência exata de tipos, como cálculos financeiros ou dados científicos.
Modelos aninhados e dados complexos
Os aplicativos reais lidam com estruturas de dados complexas e interconectadas. Um pedido de comércio eletrônico contém informações do cliente, endereços de entrega e vários itens de produtos, cada um exigindo sua própria validação:
from typing import List
from datetime import datetime
classAddress(BaseModel):
street:str= Field(min_length=5)
city:str= Field(min_length=2)
postal_code:str= Field(pattern=r'^\d{5}(-\d{4})?$')
country:str="USA"classCustomer(BaseModel):
name:str= Field(min_length=1)
email: EmailStr
shipping_address: Address
billing_address: Optional[Address]=NoneclassOrderItem(BaseModel):
product_id:int= Field(gt=0)
quantity:int= Field(gt=0, le=100)
unit_price: Decimal = Field(gt=0)classOrder(BaseModel):
order_id:str= Field(pattern=r'^ORD-\d{6}$')
customer: Customer
items: List[OrderItem]= Field(min_items=1)
order_date: datetime = Field(default_factory=datetime.now)# Complex nested data validation
order_data ={"order_id":"ORD-123456","customer":{"name":"John Doe","email":"[email protected]","shipping_address":{"street":"123 Main Street","city":"Anytown","postal_code":"12345"}},"items":[{"product_id":1,"quantity":2,"unit_price":"29.99"},{"product_id":2,"quantity":1,"unit_price":"149.99"}]}
order = Order(**order_data)print(f"Order validated with {len(order.items)} items")Tecnologia
O Pydantic valida estruturas aninhadas recursivamente: o campo Customer torna-se um objeto Customer completo, que valida seu próprio campo Address. A sintaxe List[OrderItem] valida cada elemento da lista como um OrderItem, enquanto Field(min_items=1) impede que pedidos vazios cheguem ao seu sistema de estoque. O uso do sitedefault_factory=datetime.now cria registros de data e hora exclusivos para cada instância de pedido.
Campos opcionais e tratamento de Nenhum
Operações diferentes precisam de requisitos de dados diferentes. A criação do usuário exige informações completas, enquanto as atualizações devem aceitar alterações parciais:
Serialização converte objetos Pydantic em dicionários ou cadeias de caracteres JSON para armazenamento ou transmissão. O método model_dump() lida com essa conversão, com exclude_none=True removendo os campos não fornecidos. Esse padrão funciona perfeitamente para solicitações PATCH em que os clientes enviam apenas os campos que desejam alterar, evitando a substituição acidental de dados em seu banco de dados.
Essa base prepara você para o próximo desafio: implementar uma lógica de validação personalizada que capture as regras comerciais exclusivas do seu aplicativo.
Validação personalizada e integração com o mundo real
Criar aplicativos de produção significa lidar com dados que não se encaixam nos padrões de verificação de tipo padrão.
Considere um formulário de registro de usuário em que os requisitos de senha variam de acordo com os planos de assinatura, ou uma API que recebe dados de endereço de vários países com diferentes formatos de código postal. Esses cenários exigem uma lógica de validação personalizada que capture suas regras comerciais específicas e, ao mesmo tempo, se integre perfeitamente a estruturas da Web e sistemas de configuração.
Esta seção mostra a você como implementar padrões práticos de validação personalizados, integrar modelos Pydantic com FastAPI para documentação automática da API e gerenciar as configurações do aplicativo por meio de variáveis de ambiente usando a abordagem de arquivo.env da qual a maioria dos aplicativos de produção depende.
Validadores de campo e validação de modelo
Quando a lógica comercial determina a validade dos dados, o decorador @field_validator do Pydantic transforma suas funções de validação em parte do próprio modelo. Considere um sistema de registro de usuários em que diferentes níveis de assinatura têm diferentes requisitos de senha:
from pydantic import BaseModel, field_validator, Field
import re
classUserRegistration(BaseModel):
username:str= Field(min_length=3)
email: EmailStr
password:str
subscription_tier:str= Field(pattern=r'^(free|pro|enterprise)$')@field_validator('password')@classmethoddefvalidate_password_complexity(cls, password, info):
tier = info.data.get('subscription_tier','free')iflen(password)<8:raise ValueError('Password must be at least 8 characters')if tier =='enterprise'andnot re.search(r'[A-Z]', password):raise ValueError('Enterprise accounts require uppercase letters')return passwordTecnologia
O decorador @field_validator dá a você acesso a outros valores de campo por meio do parâmetro info.data, permitindo regras de validação que dependem de vários campos. O validador é executado depois que a verificação básica de tipo é aprovada, portanto, você pode presumir com segurança que subscription_tier é um dos valores permitidos.
Para a validação que abrange vários campos, o decorador@model_validator é executado depois que todos os campos individuais são validados:
from datetime import datetime
from pydantic import model_validator
classEventRegistration(BaseModel):
start_date: datetime
end_date: datetime
max_attendees:int= Field(gt=0)
current_attendees:int= Field(ge=0)@model_validator(mode='after')defvalidate_event_constraints(self):if self.end_date <= self.start_date:raise ValueError('Event end date must be after start date')if self.current_attendees > self.max_attendees:raise ValueError('Current attendees cannot exceed maximum')return selfTecnologia
O parâmetro mode='after' garante que o validador receba uma instância de modelo totalmente construída, o que o torna perfeito para a lógica comercial que exige acesso a todos os campos validados. O validador deve retornar self para indicar que a validação foi bem-sucedida.
Integração com FastAPI
A integração do FastAPI com o Pydantic cria validação automática de solicitações e documentação de API. O padrão principal envolve a criação de modelos separados para operações diferentes, dando a você controle sobre quais dados fluem em cada direção:
from fastapi import FastAPI
from typing import Optional
from datetime import datetime
app = FastAPI()classUserCreate(BaseModel):
username:str= Field(min_length=3)
email: EmailStr
password:str= Field(min_length=8)classUserResponse(BaseModel):id:int
username:str
email: EmailStr
created_at: datetime
@app.post("/users/", response_model=UserResponse)asyncdefcreate_user(user: UserCreate):# FastAPI automatically validates the request body
new_user ={"id":1,"username": user.username,"email": user.email,"created_at": datetime.now()}return UserResponse(**new_user)Tecnologia
A separação entre os modelos de entrada e saída oferece vários benefícios. Os modelos de entrada podem incluir regras de validação e campos obrigatórios, enquanto os modelos de saída controlam exatamente quais dados são enviados aos clientes. O FastAPI gera automaticamente documentação OpenAPI a partir de seus modelos Pydantic, criando documentos de API interativos que os desenvolvedores podem usar para testar pontos de extremidade.
Para operações de atualização, você pode criar modelos em que todos os campos são opcionais:
classUserUpdate(BaseModel):
username: Optional[str]= Field(None, min_length=3)
email: Optional[EmailStr]=None@app.patch("/users/{user_id}")asyncdefupdate_user(user_id:int, user_update: UserUpdate):# Only update provided fields
update_data = user_update.model_dump(exclude_unset=True)# Your database update logic herereturn{"message":f"Updated user {user_id}"}Tecnologia
O parâmetro exclude_unset=True nas operações PATCH garante que você atualize apenas os campos que foram explicitamente fornecidos, evitando substituições acidentais. Esse padrão funciona perfeitamente para APIs REST em que os clientes enviam atualizações parciais.
Gerenciamento de configuração com variáveis de ambiente
Os aplicativos de produção precisam de um gerenciamento de configuração seguro e de fácil implementação. O site BaseSettings do Pydantic, combinado com os arquivos.env, fornece uma configuração segura de tipo que funciona em ambientes de desenvolvimento, preparação e produção.
Primeiro, crie um arquivo.env na raiz do seu projeto:
from pydantic import BaseSettings, Field
from typing import List
classAppSettings(BaseSettings):
database_url:str= Field(description="Database connection URL")
secret_key:str= Field(description="Secret key for JWT tokens")
debug:bool= Field(default=False)
allowed_hosts: List[str]= Field(default=["localhost"])classConfig:
env_file =".env"
case_sensitive =False# Load settings automatically from environment and .env file
settings = AppSettings()Tecnologia
A classe BaseSettings lê automaticamente as variáveis de ambiente, os arquivos .env e os argumentos da linha de comando. As variáveis de ambiente têm precedência sobre os valores do arquivo .env, o que facilita a substituição de configurações em diferentes ambientes de implementação. A configuraçãocase_sensitive = False permite a nomeação flexível de variáveis de ambiente.
Para aplicativos complexos, você pode organizar as configurações em grupos lógicos:
Essa abordagem aninhada mantém as configurações relacionadas juntas e, ao mesmo tempo, mantém uma separação clara entre os diferentes componentes do seu aplicativo. Cada grupo de configurações pode ter seu próprio prefixo de variável de ambiente e regras de validação.
A abordagem do arquivo .env funciona com plataformas de implantação como Heroku, AWS e Docker, em que as variáveis de ambiente são a maneira padrão de configurar aplicativos. Seu aplicativo obtém segurança e validação de tipo enquanto segue os padrões de configuração nativos da nuvem que as equipes de operações esperam.
Esses padrões formam a base para a criação de aplicativos de fácil manutenção que lidam com a complexidade do mundo real. O sistema de validação da Pydantic se adapta aos seus requisitos específicos, fornecendo mensagens de erro claras e documentação automática que ajudam toda a sua equipe a entender as estruturas de dados que o aplicativo espera.
Conclusão
Agora você já viu como o Pydantic pode evitar o trabalho tedioso de escrever código de validação manualmente. Em vez de sobrecarregar suas funções com verificaçõesisinstance() e tratamento de erros personalizado, você pode definir sua estrutura de dados uma vez e deixar que o Pydantic cuide do resto.
Não deixe de ler a seção de perguntas frequentes abaixo para perguntas mais específicas sobre o Pydantic. Para obter uma documentação abrangente e padrões avançados, aqui estão alguns recursos que você pode usar:
A validação Pydantic torna meu aplicativo mais lento?⌄
O Pydantic V2 foi desenvolvido com o Rust como base, o que o torna muito rápido. Para a maioria dos aplicativos, a sobrecarga de validação é mínima em comparação com os erros que ela evita. Você pode usar o site model_construct() para ignorar a validação de fontes de dados confiáveis quando o desempenho for crítico.
Quando devo usar o Pydantic em vez das classes de dados do Python?⌄
Use o Pydantic quando você precisar de validação, trabalhar com fontes de dados externas ou criar APIs. Use as classes de dados para estruturas de dados internas simples em que você confia na entrada. O Pydantic é excelente quando os dados são provenientes de usuários, APIs ou arquivos de configuração.
Como faço para lidar com os erros de validação do Pydantic em aplicativos de produção?⌄
Capture exceções em ValidationError e extraia a lista .errors() para obter informações de erro estruturadas. Cada erro contém os campos loc, msg e type que ajudam você a fornecer mensagens de erro fáceis de usar ou registrar informações detalhadas de depuração.
Posso adicionar o Pydantic ao código existente sem quebrar tudo?⌄
Sim! Comece envolvendo pontos de entrada de dados externos (solicitações de API, análise de arquivos) com modelos Pydantic. Você pode converter gradualmente as estruturas de dados internas. O Pydantic funciona junto com o código existente e não exige que você altere toda a sua base de código de uma só vez.
Qual é o erro mais comum ao começar com Pydantic?⌄
Não entender a coerção de tipo. O Pydantic converte tipos compatíveis (como a string "123" para o inteiro 123) por padrão. Se você precisar de verificação rigorosa de tipos, use Field(strict=True) ou defina o modo rigoroso na configuração do modelo para evitar conversões automáticas.
Aprenda a trabalhar com JSON em Python, incluindo serialização, deserialização, formatação, otimização de desempenho, manipulação de APIs e compreensão das limitações e alternativas do JSON.
Aprenda sobre classes abstratas em Python, sua finalidade e como usar o módulo `abc` para garantir interfaces consistentes. Inclui exemplos práticos e melhores práticas para uma implementação eficaz.
Pydantic é a biblioteca de validação de dados mais popular do Python, que pode transformar dicas de tipo em regras de validação em tempo de execução.
Em vez de escrever dezenas de if
isinstance()e funções de validação personalizadas, você define sua estrutura de dados uma vez usando a sintaxe familiar do Python.O Pydantic cuida do resto: validando os dados que chegam, convertendo tipos quando apropriado e fornecendo mensagens de erro claras quando a validação falha.
Considere esta função simples do Python com dicas de tipo e documentação adequada:
Observando sua assinatura, você sabe exatamente que tipo de dados ele aceita e produz na outra extremidade. No entanto, você pode dizer o mesmo sobre o usuário final? Afinal de contas, essas dicas de tipo são apenas para fins de documentação - se alguém passar um tipo de dados incorreto, o interpretador do Python nunca será incomodado:
Esse é um exemplo clássico da tipagem dinâmica do Python. Ele oferece aos programadores liberdade e desenvolvimento rápido, mas isso tem o custo de introduzir problemas de validação de tipo que geralmente aparecem na produção.
Neste tutorial, explicarei como você pode usar o Pydantic em aplicativos do mundo real. Começaremos com seu primeiro modelo de dados e passaremos por padrões avançados usados em sistemas de produção. Você descobrirá como validar estruturas de dados aninhadas, escrever validadores personalizados para regras comerciais complexas e integrar o Pydantic a estruturas populares como FastAPI e SQLAlchemy.
O que é Pydantic?
Basicamente, você está jogando um jogo de roleta de dados quando cria aplicativos que interagem com o mundo externo. Os usuários podem enviar um número de telefone como uma cadeia de caracteres em vez de um número inteiro, uma API pode retornar valores nulos quando você espera números ou um arquivo de configuração pode conter erros de digitação que quebram o aplicativo às 3h da manhã.
As abordagens tradicionais do Python para esse problema tornam-se rapidamente difíceis de manejar. Você acaba escrevendo um código de validação como este:
Multiplique isso por cada estrutura de dados em seu aplicativo e você passará mais tempo escrevendo código de validação do que lógica comercial.
O Pydantic resolve isso combinando três conceitos poderosos: dicas de tipo, validação em tempo de execução e serialização automática. Em vez de verificações manuais, você define sua estrutura de dados uma vez usando a sintaxe de anotação de tipo do Python, e o Pydantic lida com toda a validação automaticamente:
Recursos do Pydantic
A abordagem do Pydantic oferece a você vários benefícios. O desempenho é a primeira vantagem que você notará - a lógica de validação central do Pydantic é escrita em Rust, o que a torna mais rápida do que a validação em Python escrita à mão na maioria dos casos. Seu aplicativo pode processar milhares de solicitações sem que a validação se torne um gargalo.
O Pydantic também permite a integração com estruturas modernas. FastAPI, uma das estruturas da Web de crescimento mais rápido do Python, usa modelos Pydantic para gerar automaticamente a documentação da API, validar corpos de solicitação e serializar respostas. Ao definir um modelo Pydantic, você obtém a geração do esquema OpenAPI gratuitamente:
A geração do esquema JSON ocorre automaticamente com cada modelo Pydantic. Isso significa que suas estruturas de dados se tornam autodocumentadas e você pode gerar bibliotecas de clientes, regras de validação para aplicativos front-end ou esquemas de banco de dados a partir da mesma fonte de verdade.
Pydantic vs. Classes de dados vs. Marshmallow
Mas quando você deve escolher o Pydantic em vez das alternativas? Se você estiver comparando Pydantic com classes de dados, a decisão se resume às necessidades de validação. O
@dataclassdo Python é perfeito para contêineres de dados simples em que você confia na entrada, mas o Pydantic é excelente quando você precisa de validação, serialização e integração com estruturas da Web:Ao comparar o Pydantic com o Marshmallow, a abordagem de dica de tipo do Pydantic parece mais natural para os desenvolvedores Python, enquanto o Marshmallow usa classes de esquema. O Pydantic também oferece melhor desempenho e integração mais fácil com estruturas assíncronas modernas.
O Pydantic funciona melhor quando você está criando APIs, processando dados externos, gerenciando a configuração ou em qualquer cenário em que a falha na validação de dados deva ser detectada antecipadamente, em vez de causar bugs misteriosos posteriormente. Ele converte erros de tempo de execução em mensagens de validação claras e acionáveis que ajudam os desenvolvedores e os usuários a entender o que deu errado.
Primeiros passos com o Pydantic
Vamos nos aprofundar em como usar o Pydantic em seus projetos, desde a instalação até a criação de seus primeiros modelos de dados.
Instalação e configuração
A configuração do Pydantic é simples, mas algumas armadilhas comuns podem atrapalhar os novatos. Orientarei você no processo de instalação adequado e o ajudarei a evitar os erros que causam depuração desnecessária.
Primeiro, crie um ambiente virtual para manter as dependências do projeto isoladas. Isso evita conflitos de versão que podem causar erros de importação misteriosos posteriormente:
Agora, instale o Pydantic com o seguinte comando:
Para obter a funcionalidade de validação de e-mail, que você provavelmente precisará em aplicativos reais (e neste tutorial), instale os extras de e-mail:
As aspas ao redor de
pydantic[email]são importantes em quase todos os terminais. Sem eles, você poderá ver um erro "nenhuma correspondência encontrada".Observação: Se você encontrar
ModuleNotFoundError: No module named 'pydantic', verifique estes problemas:Aviso de nomeação crítica: Nunca nomeie seu arquivo Python como
pydantic.py. Isso cria uma importação circular que quebrará seu código com mensagens de erro confusas. O Python tentará importar seu arquivo em vez da biblioteca Pydantic real.Seu primeiro modelo Pydantic
Vamos criar um modelo de usuário simples para ver o Pydantic em ação. Em vez de começar com exemplos abstratos, resolveremos um problema real: validar dados de registro de usuário de um formulário da Web.
Saída:
Vamos detalhar o que está acontecendo nessa definição de modelo:
Userherda deBaseModel, o que lhe dá todos os recursos de validação e serialização do Pydantic. Com essa herança, você transforma uma classe regular do Python em uma ferramenta de validação de dados.name: strinforma ao Pydantic quenamedeve ser uma cadeia de caracteres,age: intsignifica que a idade deve ser um número inteiro e assim por diante.EmailStré um tipo Pydantic especial que valida automaticamente os endereços de e-mail. Ele vem do pacotepydantic[email]que você instalou anteriormente e usa expressões regulares para garantir que o formato do e-mail seja válido. Se alguém passar "not-an-email", o Pydantic gerará um erro de validação.is_active: bool = Truetêm valores padrão. Se você não fornecer esses campos ao criar um usuário, o Pydantic usará os padrões. O site= Noneparacreated_attorna esse campo opcional.User(**clean_data), o**descompacta o dicionário e passa cada par chave-valor como argumentos de palavra-chave para o construtor do modelo.Agora vamos ver a conversão automática de tipos do Pydantic em ação:
Agora vamos ver a conversão automática de tipos do Pydantic em ação:
Saída:
Como você pode ver, os campos
ageeis_inactivesão automaticamente convertidos em seus formatos adequados por meio de validação.Quando a validação falha, o Pydantic fornece mensagens de erro claras:
Saída:
BaseModel vs. classes de dados
Entender quando usar o
BaseModeldo Pydantic versus o@dataclass do Python ajuda você a escolher a ferramenta certa para cada situação.As classes de dados Python são perfeitas para contêineres de dados simples em que você controla a entrada:
Os modelos Pydantic adicionam validação, serialização e integração de estruturas:
Quando você deve escolher cada abordagem:
O Pydantic adiciona alguma sobrecarga em comparação com as classes de dados, mas esse custo geralmente é insignificante em comparação com os erros que ele evita e o tempo de desenvolvimento que ele economiza. Para aplicativos da Web, a integração automática com estruturas como FastAPI faz com que o Pydantic seja a escolha certa.
Os recursos de validação e serialização tornam-se mais valiosos à medida que seu aplicativo cresce. Começar com os modelos Pydantic oferece a você uma base sólida que se adapta às suas necessidades.
Criando modelos de dados com o Pydantic
Agora que você entende os conceitos básicos, vamos abordar os desafios que enfrentará ao criar aplicativos prontos para produção.
Validação de campo e restrições
Considere uma API de catálogo de produtos em que os dados de preço vêm de vários fornecedores com padrões de formatação diferentes. Alguns enviam preços como strings, outros como floats e, ocasionalmente, alguém envia um preço negativo que trava seu sistema de faturamento.
A função
Field()do Pydantic transforma dicas básicas de tipo em regras de validação sofisticadas que protegem seu aplicativo:Cada parâmetro de
Field()tem uma finalidade específica:min_lengthemax_lengthevitam violações do esquema do banco de dados,gtelecriam limites de lógica comercial epatternvalida dados formatados usando expressões regulares. A sintaxeField(...)com reticências marca os campos obrigatórios, enquantoField(None, ...)cria campos opcionais com regras de validação.Coerção de tipo versus validação estrita
Por padrão, o Pydantic converte tipos compatíveis em vez de rejeitá-los completamente. Essa flexibilidade funciona bem para a entrada do usuário, mas alguns cenários exigem correspondência exata de tipos:
O dicionário
model_configcontrola o comportamento de validação em todo o modelo. A opçãostr_strip_whitespacelimpa automaticamente a entrada de strings, enquantovalidate_assignmentgarante que as alterações de campo após a criação do modelo ainda acionem a validação. Os campos individuais podem substituir essas configurações comField(strict=True)para situações que exigem correspondência exata de tipos, como cálculos financeiros ou dados científicos.Modelos aninhados e dados complexos
Os aplicativos reais lidam com estruturas de dados complexas e interconectadas. Um pedido de comércio eletrônico contém informações do cliente, endereços de entrega e vários itens de produtos, cada um exigindo sua própria validação:
O Pydantic valida estruturas aninhadas recursivamente: o campo
Customertorna-se um objetoCustomercompleto, que valida seu próprio campoAddress. A sintaxeList[OrderItem]valida cada elemento da lista como umOrderItem, enquantoField(min_items=1)impede que pedidos vazios cheguem ao seu sistema de estoque. O uso do sitedefault_factory=datetime.nowcria registros de data e hora exclusivos para cada instância de pedido.Campos opcionais e tratamento de Nenhum
Operações diferentes precisam de requisitos de dados diferentes. A criação do usuário exige informações completas, enquanto as atualizações devem aceitar alterações parciais:
Serialização converte objetos Pydantic em dicionários ou cadeias de caracteres JSON para armazenamento ou transmissão. O método
model_dump()lida com essa conversão, comexclude_none=Trueremovendo os campos não fornecidos. Esse padrão funciona perfeitamente para solicitações PATCH em que os clientes enviam apenas os campos que desejam alterar, evitando a substituição acidental de dados em seu banco de dados.Essa base prepara você para o próximo desafio: implementar uma lógica de validação personalizada que capture as regras comerciais exclusivas do seu aplicativo.
Validação personalizada e integração com o mundo real
Criar aplicativos de produção significa lidar com dados que não se encaixam nos padrões de verificação de tipo padrão.
Considere um formulário de registro de usuário em que os requisitos de senha variam de acordo com os planos de assinatura, ou uma API que recebe dados de endereço de vários países com diferentes formatos de código postal. Esses cenários exigem uma lógica de validação personalizada que capture suas regras comerciais específicas e, ao mesmo tempo, se integre perfeitamente a estruturas da Web e sistemas de configuração.
Esta seção mostra a você como implementar padrões práticos de validação personalizados, integrar modelos Pydantic com FastAPI para documentação automática da API e gerenciar as configurações do aplicativo por meio de variáveis de ambiente usando a abordagem de arquivo
.envda qual a maioria dos aplicativos de produção depende.Validadores de campo e validação de modelo
Quando a lógica comercial determina a validade dos dados, o decorador
@field_validatordo Pydantic transforma suas funções de validação em parte do próprio modelo. Considere um sistema de registro de usuários em que diferentes níveis de assinatura têm diferentes requisitos de senha:O decorador
@field_validatordá a você acesso a outros valores de campo por meio do parâmetroinfo.data, permitindo regras de validação que dependem de vários campos. O validador é executado depois que a verificação básica de tipo é aprovada, portanto, você pode presumir com segurança quesubscription_tieré um dos valores permitidos.Para a validação que abrange vários campos, o decorador
@model_validatoré executado depois que todos os campos individuais são validados:O parâmetro
mode='after'garante que o validador receba uma instância de modelo totalmente construída, o que o torna perfeito para a lógica comercial que exige acesso a todos os campos validados. O validador deve retornarselfpara indicar que a validação foi bem-sucedida.Integração com FastAPI
A integração do FastAPI com o Pydantic cria validação automática de solicitações e documentação de API. O padrão principal envolve a criação de modelos separados para operações diferentes, dando a você controle sobre quais dados fluem em cada direção:
A separação entre os modelos de entrada e saída oferece vários benefícios. Os modelos de entrada podem incluir regras de validação e campos obrigatórios, enquanto os modelos de saída controlam exatamente quais dados são enviados aos clientes. O FastAPI gera automaticamente documentação OpenAPI a partir de seus modelos Pydantic, criando documentos de API interativos que os desenvolvedores podem usar para testar pontos de extremidade.
Para operações de atualização, você pode criar modelos em que todos os campos são opcionais:
O parâmetro
exclude_unset=Truenas operações PATCH garante que você atualize apenas os campos que foram explicitamente fornecidos, evitando substituições acidentais. Esse padrão funciona perfeitamente para APIs REST em que os clientes enviam atualizações parciais.Gerenciamento de configuração com variáveis de ambiente
Os aplicativos de produção precisam de um gerenciamento de configuração seguro e de fácil implementação. O site
BaseSettingsdo Pydantic, combinado com os arquivos.env, fornece uma configuração segura de tipo que funciona em ambientes de desenvolvimento, preparação e produção.Primeiro, crie um arquivo
.envna raiz do seu projeto:Em seguida, defina seu modelo de configurações:
A classe
BaseSettingslê automaticamente as variáveis de ambiente, os arquivos.enve os argumentos da linha de comando. As variáveis de ambiente têm precedência sobre os valores do arquivo.env, o que facilita a substituição de configurações em diferentes ambientes de implementação. A configuraçãocase_sensitive = Falsepermite a nomeação flexível de variáveis de ambiente.Para aplicativos complexos, você pode organizar as configurações em grupos lógicos:
Essa abordagem aninhada mantém as configurações relacionadas juntas e, ao mesmo tempo, mantém uma separação clara entre os diferentes componentes do seu aplicativo. Cada grupo de configurações pode ter seu próprio prefixo de variável de ambiente e regras de validação.
A abordagem do arquivo
.envfunciona com plataformas de implantação como Heroku, AWS e Docker, em que as variáveis de ambiente são a maneira padrão de configurar aplicativos. Seu aplicativo obtém segurança e validação de tipo enquanto segue os padrões de configuração nativos da nuvem que as equipes de operações esperam.Esses padrões formam a base para a criação de aplicativos de fácil manutenção que lidam com a complexidade do mundo real. O sistema de validação da Pydantic se adapta aos seus requisitos específicos, fornecendo mensagens de erro claras e documentação automática que ajudam toda a sua equipe a entender as estruturas de dados que o aplicativo espera.
Conclusão
Agora você já viu como o Pydantic pode evitar o trabalho tedioso de escrever código de validação manualmente. Em vez de sobrecarregar suas funções com verificações
isinstance()e tratamento de erros personalizado, você pode definir sua estrutura de dados uma vez e deixar que o Pydantic cuide do resto.Não deixe de ler a seção de perguntas frequentes abaixo para perguntas mais específicas sobre o Pydantic. Para obter uma documentação abrangente e padrões avançados, aqui estão alguns recursos que você pode usar: