Curso
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:
def calculate_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.15
elif is_premium_member:
base_discount = 0.10
else:
base_discount = 0.05
return purchase_amount * base_discount
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)
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:
def create_user(data):
# Manual validation nightmare
if not isinstance(data.get('age'), int):
raise ValueError("Age must be an integer")
if data['age'] < 0 or data['age'] > 150:
raise ValueError("Age must be between 0 and 150")
if not isinstance(data.get('email'), str) or '@' not in data['email']:
raise ValueError("Invalid email format")
if not isinstance(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'])
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
class User(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": "john@example.com",
"is_active": "true" # String gets converted to bool
}
user = User(**user_data)
print(user.age) # 25 (as integer)
print(user.model_dump()) # Clean dictionary output
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()
class UserCreate(BaseModel):
name: str
email: EmailStr
age: int
@app.post("/users/")
async def create_user(user: UserCreate):
# FastAPI automatically validates the request body
# and generates API docs from your Pydantic model
return {"message": f"Created user {user.name}"}
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
@dataclass
class UserDataclass:
name: str
age: int
# Pydantic: validation, serialization, framework integration
class UserPydantic(BaseModel):
name: str
age: int
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 Windows
Agora, instale o Pydantic com o seguinte comando:
pip install pydantic
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:
pip install "pydantic[email]"
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
class User(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": "alice@example.com",
"age": 28
}
user = User(**clean_data)
print(f"User created: {user.name}, Age: {user.age}")
print(f"Model output: {user.model_dump()}")
Saída:
User created: Alice Johnson, Age: 28
Model output: {'name': 'Alice Johnson', 'email': 'alice@example.com', 'age': 28, 'is_active': True, 'created_at': None}
Vamos detalhar o que está acontecendo nessa definição de modelo:
- Criando um modelo Pydantic: Sua classe
User
herda 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. - Definições de campo: Cada linha da classe define um campo com seu tipo esperado. A sintaxe
name: str
informa ao Pydantic quename
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 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. - 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
paracreated_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": "bob@company.com",
"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'>
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": "bob@company.com",
"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'>
Saída:
Age type: <class 'int'>
Is active type: <class 'bool'>
Como você pode ver, os campos age
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)
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]
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
@dataclass
class ProductDataclass:
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")
Os modelos Pydantic adicionam validação, serialização e integração de estruturas:
from pydantic import BaseModel, Field
class ProductPydantic(BaseModel):
name: str = Field(min_length=1)
price: float = Field(gt=0) # Must be greater than 0
in_stock: bool
# Automatic validation prevents bad data
try:
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
)
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ção Field()
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
class Product(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 messages
try:
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")
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 sintaxe Field(...)
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 coercion
class FlexibleOrder(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 matters
class StrictOrder(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)
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
class Address(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"
class Customer(BaseModel):
name: str = Field(min_length=1)
email: EmailStr
shipping_address: Address
billing_address: Optional[Address] = None
class OrderItem(BaseModel):
product_id: int = Field(gt=0)
quantity: int = Field(gt=0, le=100)
unit_price: Decimal = Field(gt=0)
class Order(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": "john@example.com",
"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")
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 site default_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:
from typing import Optional
class UserCreate(BaseModel):
name: str = Field(min_length=1)
email: EmailStr
age: int = Field(ge=13, le=120)
phone: Optional[str] = Field(None, pattern=r'^\+?1?\d{9,15}$')
class UserUpdate(BaseModel):
name: Optional[str] = Field(None, min_length=1)
email: Optional[EmailStr] = None
age: Optional[int] = Field(None, ge=13, le=120)
phone: Optional[str] = Field(None, pattern=r'^\+?1?\d{9,15}$')
# PATCH request with partial data
update_data = {"name": "Jane Smith", "age": 30}
user_update = UserUpdate(**update_data)
# Serialize only provided fields
patch_data = user_update.model_dump(exclude_none=True)
print(f"Fields to update: {list(patch_data.keys())}")
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
class UserRegistration(BaseModel):
username: str = Field(min_length=3)
email: EmailStr
password: str
subscription_tier: str = Field(pattern=r'^(free|pro|enterprise)$')
@field_validator('password')
@classmethod
def validate_password_complexity(cls, password, info):
tier = info.data.get('subscription_tier', 'free')
if len(password) < 8:
raise ValueError('Password must be at least 8 characters')
if tier == 'enterprise' and not re.search(r'[A-Z]', password):
raise ValueError('Enterprise accounts require uppercase letters')
return password
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
class EventRegistration(BaseModel):
start_date: datetime
end_date: datetime
max_attendees: int = Field(gt=0)
current_attendees: int = Field(ge=0)
@model_validator(mode='after')
def validate_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 self
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()
class UserCreate(BaseModel):
username: str = Field(min_length=3)
email: EmailStr
password: str = Field(min_length=8)
class UserResponse(BaseModel):
id: int
username: str
email: EmailStr
created_at: datetime
@app.post("/users/", response_model=UserResponse)
async def create_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)
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:
class UserUpdate(BaseModel):
username: Optional[str] = Field(None, min_length=3)
email: Optional[EmailStr] = None
@app.patch("/users/{user_id}")
async def update_user(user_id: int, user_update: UserUpdate):
# Only update provided fields
update_data = user_update.model_dump(exclude_unset=True)
# Your database update logic here
return {"message": f"Updated user {user_id}"}
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:
# .env file
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
SECRET_KEY=your-secret-key-here
DEBUG=false
ALLOWED_HOSTS=localhost,127.0.0.1,yourdomain.com
Em seguida, defina seu modelo de configurações:
from pydantic import BaseSettings, Field
from typing import List
class AppSettings(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"])
class Config:
env_file = ".env"
case_sensitive = False
# Load settings automatically from environment and .env file
settings = AppSettings()
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ção case_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:
class DatabaseSettings(BaseSettings):
url: str = Field(env="DATABASE_URL")
max_connections: int = Field(default=5, env="DB_MAX_CONNECTIONS")
class AppSettings(BaseSettings):
secret_key: str
debug: bool = False
database: DatabaseSettings = DatabaseSettings()
class Config:
env_file = ".env"
settings = AppSettings()
# Access nested configuration
db_url = settings.database.url
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çõ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:
- Documentação oficial:
- Documentação do Pydantic- Referência completa com exemplos e práticas recomendadas
- Guia de migração do Pydantic- Leitura essencial para a atualização entre versões
- Documentação FastAPI- Saiba como a Pydantic possibilita a documentação automática da API
- Recursos de aprendizagem relacionados ao DataCamp:
- Validação de modelos em Python- Mergulhe fundo nas técnicas de validação com o scikit-learn
- Introdução à FastAPI- Crie APIs de produção com a integração do Pydantic
- Introdução às APIs em Python- Trabalhar com APIs externas e validação de dados JSON
- Python intermediário- Domine os conceitos básicos do Python que complementam o uso do Pydantic
- Programação orientada a objetos em Python- Crie a base para entender os modelos Pydantic
Perguntas frequentes
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.

Sou um criador de conteúdo de ciência de dados com mais de 2 anos de experiência e um dos maiores seguidores no Medium. Gosto de escrever artigos detalhados sobre IA e ML com um estilo um pouco sarcástico, porque você precisa fazer algo para torná-los um pouco menos monótonos. Produzi mais de 130 artigos e um curso DataCamp, e estou preparando outro. Meu conteúdo foi visto por mais de 5 milhões de pessoas, das quais 20 mil se tornaram seguidores no Medium e no LinkedIn.