Modelos Aninhados no Pydantic: Trabalhando com Estruturas Complexas

Cansado de programar?

Conheça a melhor e mais completa formação de Python e Django e sinta-se um programador verdadeiramente competente. Além de Python e Django, você também vai aprender Banco de Dados, SQL, HTML, CSS, Javascript, Bootstrap e muito mais!

Quero aprender Python e Django de Verdade! Quero aprender!
Suporte

Tire suas dúvidas diretamente com o professor

Projetos práticos

Projetos práticos voltados para o mercado de trabalho

Prática profissional

Formação moderna com foco na prática profissional

Download do Artigo

✅ Artigo atualizado em Setembro 2026 Todos os exemplos foram executados no Pydantic 2.13: corrigimos a saída de model_dump_json() (que é JSON compacto, sem espaços) e completamos imports e dependências que faltavam.

Salve salve Pythonista :wave:

No desenvolvimento de aplicações Python, a manipulação de estruturas de dados complexas pode ser desafiadora.

Com o Pydantic v2, a tarefa de validação e modelagem torna-se mais intuitiva.

Este artigo foca nos modelos aninhados, que permitem representar relações entre dados em Python.

Vamos explorar como o Pydantic lida com listas, dicionários e estruturas JSON complexas.

Além disso, veremos como converter modelos aninhados em dicionários e JSON, essencial para transferências de dados.

Vá Direto ao Assunto…

Representando relações entre dados

O Pydantic permite a construção de modelos aninhados, ou seja, modelos que contêm outros modelos como campos.

Esta função é útil para representar estruturas hierárquicas.

Veja como criar um modelo aninhado simples:

1
2
3
4
5
6
7
8
9
10
from pydantic import BaseModel

class Endereco(BaseModel):
    rua: str
    cidade: str

class Usuario(BaseModel):
    nome: str
    email: str
    endereco: Endereco

Neste exemplo, Usuario possui um campo endereco do tipo Endereco.

Isso estabelece uma relação entre Usuario e seu endereço.

Validação de listas e dicionários

O Pydantic também lida bem com coleções de dados.

Podemos validar listas e dicionários de modelos aninhados facilmente.

Veja um exemplo com listas:

1
2
3
4
5
6
7
class Pedido(BaseModel):
    id_pedido: int
    descricao: str

class Cliente(BaseModel):
    nome: str
    pedidos: list[Pedido]

Aqui, Cliente tem uma lista de pedidos, cada um representado pelo modelo Pedido.

Vamos ver como instanciar e validar:

1
2
3
4
pedido1 = Pedido(id_pedido=1, descricao="Pedido 1")
pedido2 = Pedido(id_pedido=2, descricao="Pedido 2")
cliente = Cliente(nome="Alice", pedidos=[pedido1, pedido2])
print(cliente)

E a saída será:

1
nome='Alice' pedidos=[Pedido(id_pedido=1, descricao='Pedido 1'), Pedido(id_pedido=2, descricao='Pedido 2')]

Essa abordagem promove organização e clareza no código, especialmente com estruturas JSON complexas em APIs.

:package: Modelos complexos na prática: Estou usando Pydantic para estruturar dados no Ebookr.ai, uma plataforma que gera ebooks profissionais com IA sobre qualquer tema. Infográficos automáticos, capa gerada por IA e exportação em PDF. Vale conferir!

Validando estruturas JSON complexas

Estruturas JSON complexas são comuns ao lidar com APIs.

O Pydantic facilita a validação e manipulação dessas estruturas.

Veja como usar modelos para validar um JSON complexo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import json

dados_json = '''
{
    "nome": "Carlos",
    "email": "[email protected]",
    "endereco": {
        "rua": "Rua das Flores",
        "cidade": "São Paulo"
    }
}
'''

dados = json.loads(dados_json)
usuario = Usuario(**dados)
print(usuario)

E a saída será:

1
nome='Carlos' email='[email protected]' endereco=Endereco(rua='Rua das Flores', cidade='São Paulo')

Neste exemplo, o JSON é convertido em um modelo Usuario, que é validado automaticamente.

Se o JSON já chega como string, também dá para pular o json.loads e validar direto com Usuario.model_validate_json(dados_json), que produz o mesmo resultado.

Antes de continuar… Está curtindo esse conteúdo? :thumbsup:

Que tal receber 30 dias de conteúdo direto na sua Caixa de Entrada?

Sua assinatura não pôde ser validada.
Você fez sua assinatura com sucesso.

Assine as PyDicas e receba 30 dias do melhor conteúdo Python na sua Caixa de Entrada: direto e sem enrolação!

Conversão de modelos em dicionários e JSON

O Pydantic permite converter modelos em dicionários e JSON facilmente.

Isso é útil para exportar dados de forma estruturada.

Conversão para dicionários

Usando o método model_dump, é possível converter para dicionário:

1
2
usuario_dict = usuario.model_dump()
print(usuario_dict)

A saída será:

1
{'nome': 'Carlos', 'email': '[email protected]', 'endereco': {'rua': 'Rua das Flores', 'cidade': 'São Paulo'}}

Conversão para JSON

Com model_dump_json, transformamos para JSON:

1
2
usuario_json = usuario.model_dump_json()
print(usuario_json)

A saída será:

1
{"nome":"Carlos","email":"[email protected]","endereco":{"rua":"Rua das Flores","cidade":"São Paulo"}}

Repare que model_dump_json() gera JSON compacto, sem espaços, e mantém acentos como São Paulo sem escapá-los. Para uma saída legível, use usuario.model_dump_json(indent=2).

Esses métodos são fundamentais para transferir dados em aplicações web.

Não é raro machucar a mão com nested models e validação assim que o projeto cresce. Ter uma base de Python bem estruturada evita boa parte dessas dores de cabeça - e é isso que a Jornada Python constrói, do zero:

Casos Práticos Reais

1. Sistema de E-commerce Completo

O campo email usa EmailStr, que depende do pacote email-validator (instale com pip install "pydantic[email]").

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
61
62
63
from pydantic import BaseModel, EmailStr, Field
from typing import List, Optional
from decimal import Decimal

class ItemCarrinho(BaseModel):
    produto_id: int
    nome: str
    quantidade: int = Field(gt=0)
    preco_unitario: Decimal

    @property
    def subtotal(self) -> Decimal:
        return self.preco_unitario * self.quantidade

class Endereco(BaseModel):
    rua: str
    numero: str
    complemento: Optional[str] = None
    bairro: str
    cidade: str
    estado: str
    cep: str

class Cliente(BaseModel):
    id: int
    nome: str
    email: EmailStr
    cpf: str
    enderecos: List[Endereco]

class Pedido(BaseModel):
    id: int
    cliente: Cliente
    itens: List[ItemCarrinho]
    endereco_entrega: Endereco

    @property
    def total(self) -> Decimal:
        return sum(item.subtotal for item in self.itens)

# Uso
pedido_data = {
    "id": 1001,
    "cliente": {
        "id": 5,
        "nome": "Maria Silva",
        "email": "[email protected]",
        "cpf": "12345678900",
        "enderecos": [
            {"rua": "Av. Paulista", "numero": "1000", "bairro": "Bela Vista",
             "cidade": "São Paulo", "estado": "SP", "cep": "01310100"}
        ]
    },
    "itens": [
        {"produto_id": 10, "nome": "Notebook", "quantidade": 1, "preco_unitario": "3500.00"},
        {"produto_id": 20, "nome": "Mouse", "quantidade": 2, "preco_unitario": "50.00"}
    ],
    "endereco_entrega": {"rua": "Av. Paulista", "numero": "1000", "bairro": "Bela Vista",
                          "cidade": "São Paulo", "estado": "SP", "cep": "01310100"}
}

pedido = Pedido(**pedido_data)
print(f"Total do pedido: R$ {pedido.total}")  # R$ 3600.00

2. API de Rede Social

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
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel

class Usuario(BaseModel):
    id: int
    username: str
    nome_completo: str
    bio: Optional[str] = None

class Comentario(BaseModel):
    id: int
    autor: Usuario
    texto: str
    created_at: datetime
    likes: int = 0

class Post(BaseModel):
    id: int
    autor: Usuario
    titulo: str
    conteudo: str
    tags: List[str]
    comentarios: List[Comentario]
    created_at: datetime

    @property
    def total_comentarios(self) -> int:
        return len(self.comentarios)

    @property
    def total_likes_comentarios(self) -> int:
        return sum(c.likes for c in self.comentarios)

3. Configuração de Aplicação Multi-Ambiente

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
from pydantic import BaseModel, Field
from typing import Dict, Any

class DatabaseConfig(BaseModel):
    host: str
    port: int = 5432
    database: str
    username: str
    password: str
    pool_size: int = Field(default=10, ge=1, le=100)

class RedisConfig(BaseModel):
    host: str
    port: int = 6379
    db: int = 0

class AppConfig(BaseModel):
    app_name: str
    debug: bool = False
    database: DatabaseConfig
    redis: RedisConfig
    api_keys: Dict[str, str]
    features: Dict[str, bool]

config_data = {
    "app_name": "MyApp",
    "debug": True,
    "database": {
        "host": "localhost",
        "database": "myapp_db",
        "username": "user",
        "password": "secret"
    },
    "redis": {"host": "localhost"},
    "api_keys": {"openai": "sk-xxx", "stripe": "pk-xxx"},
    "features": {"new_ui": True, "beta_feature": False}
}

config = AppConfig(**config_data)

Quando Usar Modelos Complexos

✅ APIs REST com payloads aninhados Perfeito para request/response bodies complexos

✅ Configurações de aplicação Validar configs YAML/JSON com múltiplos níveis

✅ Dados de domínio complexos E-commerce, redes sociais, sistemas financeiros

✅ Integração com ORMs Mapear relacionamentos SQL (1:N, N:N)

✅ Microserviços Contratos de dados entre serviços

Quando NÃO Usar

❌ Dados muito profundamente aninhados (>5 níveis) Dificulta manutenção e debugging

❌ Performance crítica com grandes volumes Validação tem custo - considere dataclasses

❌ Estruturas completamente dinâmicas Se não há schema definido, Dict[str, Any] pode ser melhor

❌ Dados muito simples Overhead desnecessário para estruturas planas

Pydantic vs Outras Abordagens

Critério Pydantic Dataclasses Dict NamedTuple
Validação ✅ Automática ❌ Não ❌ Não ❌ Não
Aninhamento ✅ Excelente ⚠️ Manual ✅ Sim ⚠️ Limitado
JSON ✅ Built-in ⚠️ Manual ✅ Nativo ❌ Não
Performance ⚠️ Média ✅ Rápido ✅ Rápido ✅ Rápido
Type hints ✅ Completo ✅ Completo ❌ Não ✅ Sim
Mutável ✅ Sim ✅ Sim ✅ Sim ❌ Não
Uso ideal APIs/Validação Data classes Flexível Imutáveis

Conclusão

Neste guia sobre Modelos Complexos no Pydantic, você aprendeu:

✅ Modelos aninhados - Models dentro de models
✅ Listas tipadas - List[Model] com validação
✅ Dicionários - Dict com tipos complexos
✅ JSON complexo - Estruturas profundamente aninhadas
✅ Serialização - model_dump() e model_dump_json()

Principais lições:

  • Aninhamento permite estruturas complexas e organizadas
  • Validação recursiva garante consistência em toda estrutura
  • Listas e dicts suportam tipos complexos
  • Perfeito para APIs REST com payloads complexos

Próximos passos:

Quer praticar POO e construção de APIs REST (com Django REST Framework) em projetos guiados, do zero ao avançado? Conheça a Jornada Python, nosso curso de Python completo.

Começe agora sua Jornada na Programação!

Não deixe para amanhã o sucesso que você pode começar a construir hoje!

#newsletter Olá :wave: Curtiu o artigo? Então faça parte da nossa Newsletter! Privacidade Não se preocupe, respeitamos sua privacidade. Você pode se descadastrar a qualquer momento.