✅ Atualizado para Pydantic V2 (Fevereiro 2025)
Tipos avançados: Decimal, UUID, DateTime, validators customizados, Field metadata.
Salve salve Pythonista ![]()
A validação de dados é essencial no desenvolvimento de aplicações robustas em Python.
Com o Pydantic, é possível personalizar a validação de dados utilizando tipos avançados e mecanismos poderosos.
Neste artigo, abordaremos como trabalhar com tipos complexos como Decimal, UUID e DateTime, utilizar campos opcionais e valores padrão, implementar validações personalizadas com validator, e introduzir o uso de Field para configurar validações e metadados.
Dominar esses tópicos é crucial para garantir a integridade e a consistência dos dados em suas aplicações Python.
Vá Direto ao Assunto…
- Trabalhando com Tipos Complexos: Decimal, UUID e DateTime
- Utilizando Campos Opcionais (Optional) e Valores Padrão
- Validação Personalizada com Validator
- Introdução ao Field para Configurar Validações e Metadados
- Casos Práticos com Tipos Avançados
- Tipos Avançados: Comparação
- Validators: Níveis de Validação
- Quando Usar Validators Customizados
- Quando NÃO Usar
Trabalhando com Tipos Complexos: Decimal, UUID e DateTime
O Pydantic oferece suporte a diversos tipos complexos que facilitam a manipulação de dados específicos.
Vamos explorar como utilizar Decimal, UUID e DateTime em seus modelos.
Utilizando Decimal
O tipo Decimal é útil para representar números com precisão decimal, evitando problemas de arredondamento comuns com floats.
1
2
3
4
5
6
from decimal import Decimal
from pydantic import BaseModel
class Produto(BaseModel):
nome: str
preco: Decimal
Explicação do código:
-
Importação: Importamos
Decimaldo módulodecimaleBaseModeldo Pydantic. -
Definição da Classe: Criamos a classe
Produtoherdando deBaseModel. -
Atributos: Definimos
nomecomo string eprecocomoDecimal.
Falando em Pydantic: O DevBook usa Pydantic extensivamente para validar e estruturar conteúdo gerado por IA. É uma ferramenta que criei para gerar ebooks técnicos profissionais — com syntax highlighting, infográficos e exportação em PDF. Conhece lá!
Utilizando UUID
Para identificar objetos de forma única, o UUID (Universally Unique Identifier) é uma excelente escolha.
1
2
3
4
5
6
import uuid
from pydantic import BaseModel
class Usuario(BaseModel):
id: uuid.UUID
nome: str
Explicação do código:
-
Importação: Importamos
uuideBaseModel. -
Definição da Classe: Criamos a classe
Usuariocomiddo tipoUUIDenomecomo string.
Antes de continuar… Está curtindo esse conteúdo? ![]()
Que tal receber 30 dias de conteúdo direto na sua Caixa de Entrada?
Utilizando DateTime
O tipo DateTime permite trabalhar com data e hora de forma eficiente.
1
2
3
4
5
6
from datetime import datetime
from pydantic import BaseModel
class Evento(BaseModel):
nome: str
data_hora: datetime
Explicação do código:
-
Importação: Importamos
datetimedo módulodatetimeeBaseModel. -
Definição da Classe: Criamos a classe
Eventocomnomeedata_horacomoDateTime.
Utilizando Campos Opcionais (Optional) e Valores Padrão
Nem todos os campos dos modelos precisam ser obrigatórios. O Pydantic facilita a definição de campos opcionais e a atribuição de valores padrão.
Campos Opcionais
Usamos Optional para indicar que um campo pode ser ignorado ou ter valor None.
1
2
3
4
5
6
from typing import Optional
from pydantic import BaseModel
class Cliente(BaseModel):
nome: str
email: Optional[str] = None
Explicação do código:
-
Importação: Importamos
OptionaldetypingeBaseModel. -
Definição da Classe: Criamos
Clientecomnomeobrigatório eemailopcional, com valor padrãoNone.
Valores Padrão
Atribuímos valores padrão diretamente nos campos para garantir que tenham um valor caso não sejam fornecidos.
1
2
3
4
5
from pydantic import BaseModel
class Configuracao(BaseModel):
ativo: bool = True
nivel_acesso: int = 1
Explicação do código:
-
Definição da Classe: Criamos
ConfiguracaocomativocomoTrueenivel_acessocomo1por padrão.
Validação Personalizada com Validator
Embora o Pydantic valide automaticamente muitos tipos, às vezes é necessário implementar validações específicas. O decorador @validator facilita essa customização.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pydantic import BaseModel, validator
from decimal import Decimal
class Pedido(BaseModel):
quantidade: int
preco_unitario: Decimal
@validator('quantidade')
def quantidade_positiva(cls, v):
if v <= 0:
raise ValueError('A quantidade deve ser positiva')
return v
@validator('preco_unitario')
def preco_valido(cls, v):
if v <= Decimal('0.00'):
raise ValueError('O preço unitário deve ser maior que zero')
return v
Explicação do código:
-
Definição da Classe: Criamos
Pedidocomquantidadeepreco_unitario. -
Validador de Quantidade:
- O método
quantidade_positivaverifica sequantidadeé maior que zero. - Se não, lança um
ValueError.
- O método
-
Validador de Preço Unitário:
- O método
preco_validoassegura quepreco_unitarioseja maior que zero. - Se não, lança um
ValueError.
- O método
Esses validadores garantem que os dados atendam às regras de negócio definidas.
Introdução ao Field para Configurar Validações e Metadados
O Field do Pydantic permite configurar validações adicionais e adicionar metadados aos campos dos modelos, como descrições, títulos e exemplos.
1
2
3
4
5
6
7
from pydantic import BaseModel, Field
from uuid import UUID
class Produto(BaseModel):
id: UUID = Field(..., title="ID do Produto", description="Identificador único do produto")
nome: str = Field(..., min_length=3, max_length=50, example="Caneca")
preco: Decimal = Field(..., gt=0, description="Preço do produto em reais")
Explicação do código:
-
Importação: Importamos
Fielddepydantic, além de outros tipos necessários. -
Definição da Classe: Criamos
Produtocomid,nomeepreco. -
Uso de Field:
-
id: Define título e descrição para documentação. -
nome: Define comprimento mínimo e máximo, além de um exemplo. -
preco: Define que o valor deve ser maior que zero e adiciona uma descrição.
-
O Field é especialmente útil para gerar documentação automática e garantir que os campos atendam a critérios específicos além da validação básica de tipos.
Casos Práticos com Tipos Avançados
1. Sistema de Pagamentos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from pydantic import BaseModel, Field, validator
from decimal import Decimal
from datetime import datetime
from uuid import UUID, uuid4
from enum import Enum
class StatusPagamento(str, Enum):
PENDENTE = "pendente"
PROCESSANDO = "processando"
APROVADO = "aprovado"
REJEITADO = "rejeitado"
class Pagamento(BaseModel):
id: UUID = Field(default_factory=uuid4)
valor: Decimal = Field(..., gt=0, decimal_places=2)
moeda: str = Field(default="BRL", regex="^[A-Z]{3}$")
status: StatusPagamento = StatusPagamento.PENDENTE
created_at: datetime = Field(default_factory=datetime.now)
processado_em: Optional[datetime] = None
@validator('valor')
def validar_valor_maximo(cls, v):
if v > Decimal('999999.99'):
raise ValueError('Valor excede limite de R$ 999.999,99')
return v
class Config:
use_enum_values = True
# Uso
pagamento = Pagamento(valor=Decimal('150.50'))
print(pagamento.model_dump_json(indent=2))
2. Agendamento de Eventos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from pydantic import BaseModel, Field, validator, root_validator
from datetime import datetime, timedelta
from typing import Optional
from zoneinfo import ZoneInfo
class Evento(BaseModel):
titulo: str = Field(..., min_length=5, max_length=100)
inicio: datetime
fim: datetime
timezone: str = Field(default="America/Sao_Paulo")
participantes_min: int = Field(default=1, ge=1)
participantes_max: int = Field(..., ge=1, le=1000)
@validator('fim')
def fim_depois_inicio(cls, v, values):
if 'inicio' in values and v <= values['inicio']:
raise ValueError('Fim deve ser após início')
return v
@root_validator
def validar_duracao(cls, values):
inicio = values.get('inicio')
fim = values.get('fim')
if inicio and fim:
duracao = fim - inicio
if duracao > timedelta(hours=24):
raise ValueError('Evento não pode durar mais de 24h')
return values
@validator('participantes_max')
def max_maior_que_min(cls, v, values):
if 'participantes_min' in values and v < values['participantes_min']:
raise ValueError('Máximo deve ser >= mínimo')
return v
3. Documentação de API com Field
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from pydantic import BaseModel, Field, EmailStr, HttpUrl
from typing import List, Optional
from datetime import date
class UsuarioAPI(BaseModel):
"""Schema para criação de usuário via API"""
nome: str = Field(
...,
min_length=2,
max_length=100,
title="Nome Completo",
description="Nome completo do usuário",
example="João Silva"
)
email: EmailStr = Field(
...,
title="E-mail",
description="E-mail válido do usuário",
example="[email protected]"
)
idade: int = Field(
...,
ge=18,
le=120,
title="Idade",
description="Idade do usuário (18-120)",
example=25
)
site: Optional[HttpUrl] = Field(
None,
title="Website",
description="URL do site pessoal",
example="https://joao.com"
)
tags: List[str] = Field(
default_factory=list,
max_items=10,
title="Tags",
description="Lista de tags (máx 10)",
example=["python", "dev"]
)
class Config:
schema_extra = {
"example": {
"nome": "João Silva",
"email": "[email protected]",
"idade": 25,
"site": "https://joao.com",
"tags": ["python", "dev"]
}
}
# Gerar schema JSON para documentação OpenAPI
print(UsuarioAPI.schema_json(indent=2))
Tipos Avançados: Comparação
| Tipo | Quando Usar | Validação | Exemplo |
|---|---|---|---|
| Decimal | Valores monetários, precisão | Precisão decimal exata | Preços, salários |
| UUID | IDs únicos, chaves primárias | Formato UUID válido | IDs de banco |
| DateTime | Timestamps, agendamentos | Formato ISO 8601 | created_at |
| EmailStr | E-mails | Formato de e-mail | Cadastros |
| HttpUrl | URLs | URL válida | Links externos |
| IPvAnyAddress | Endereços IP | IPv4/IPv6 | Logs, segurança |
| Json | JSON em string | JSON válido | Configs dinâmicas |
| SecretStr | Senhas, tokens | - | Credenciais |
Validators: Níveis de Validação
Field-level validator
1
2
3
4
@validator('campo')
def validar_campo(cls, v):
# Valida um campo específico
return v
Root validator (validar múltiplos campos)
1
2
3
4
@root_validator
def validar_modelo_completo(cls, values):
# Valida relações entre campos
return values
Validator com pre=True (antes da validação de tipo)
1
2
3
4
@validator('campo', pre=True)
def processar_antes(cls, v):
# Processa antes da conversão de tipo
return v
Quando Usar Validators Customizados
✅ Regras de negócio complexas
Ex: CEP deve ser válido para o estado
✅ Validações com dependências
Ex: data_fim > data_inicio
✅ Normalização de dados
Ex: converter telefone para formato padrão
✅ Integração com APIs externas
Ex: validar CPF consultando serviço
Quando NÃO Usar
❌ Validações simples de tipo
Type hints já resolvem
❌ Performance crítica
Validators adicionam overhead
❌ Lógica de negócio pesada
Melhor em camada de serviço
Conclusão
Neste artigo, exploramos como o Pydantic permite personalizar a validação de dados utilizando tipos avançados como Decimal, UUID e DateTime.
Aprendemos a definir campos opcionais e atribuir valores padrão, implementar validações personalizadas com o decorador @validator, e utilizar o Field para configurar validações adicionais e metadados.
Essas ferramentas tornam o gerenciamento de dados em Python mais eficiente e seguro, fortalecendo a integridade das suas aplicações.
Com esses conhecimentos, você está pronto para criar modelos robustos e confiáveis em seus projetos Python.
"Porque o Senhor dá a sabedoria, e da sua boca vem a inteligência e o entendimento" Pv 2:6