Skip to content

Capítulo 3.3: Modelos de Dados com Pydantic

Tempo de estudo: 50 minutos


1. Pydantic: A "Planta Digital" da nave espacial

Imagine que você está construindo uma nave espacial. Você não pode simplesmente soldar pedaços de metal aleatoriamente. Você precisa de uma planta detalhada que defina:

  • Nome da nave (tipo: string, comprimento máx.: 50 caracteres)
  • Ano de lançamento (tipo: número inteiro)
  • Presença de hipermotor (tipo: sim/não)

Pydantic é uma biblioteca que permite criar essas "plantas digitais" para seus dados em Python. No FastAPI, ela desempenha três funções chave:

  1. Declaração da estrutura: Descreve claramente quais campos seus dados contêm.
  2. Validação de dados: Verifica automaticamente se os dados de entrada correspondem à planta.
  3. Documentação: O FastAPI usa essas plantas para gerar documentação detalhada e interativa.

💡 Analogia espacial: Um modelo Pydantic é a ficha técnica de um objeto. Qualquer "carga" (dados) que chega à estação deve corresponder à especificação na ficha técnica. Caso contrário, o computador de bordo (Pydantic) a rejeitará.


2. Criando a primeira planta: Modelo Spaceship

Vamos criar um modelo que descreverá nossa nave espacial.

Passo 1: Importamos BaseModel do Pydantic Pydantic já está instalado junto com fastapi[all]. Precisamos apenas importar a classe base para nossos modelos.

Adicione no main.py no topo, ao lado de outras importações:

# main.py
from fastapi import FastAPI
from pydantic import BaseModel

Passo 2: Descrevemos o modelo Spaceship Crie uma classe que herda de BaseModel. Dentro da classe, defina os campos e seus tipos, usando as anotações de tipo padrão do Python.

Adicione este código em main.py (pode ser depois das importações):

class Spaceship(BaseModel):
    """
    Ficha técnica (modelo) da nave espacial.
    """
    name: str
    type: str
    launch_year: int
    status: str
É isso! Você acabou de criar a "planta". Pydantic agora sabe que qualquer objeto do tipo Spaceship deve ter quatro campos com os tipos especificados.


3. Aplicando o modelo: Melhoramos nossos endpoints

Agora, vamos usar nosso novo modelo para tornar a API "mais inteligente".

A. Modelo como resposta (Response Model) Podemos informar ao FastAPI que nosso endpoint deve retornar dados que correspondam ao modelo Spaceship. Isso garante que a resposta sempre terá a estrutura correta.

Modifique o endpoint /spaceships/{ship_id} da seguinte forma:

# main.py

# ... código com db_spaceships e o modelo Spaceship ...

# Usamos `response_model` para especificar a "planta" da resposta
@app.get("/spaceships/{ship_id}", response_model=Spaceship)
def get_spaceship(ship_id: int):
    """
    Retorna os dados da nave, correspondentes ao modelo Spaceship.
    """
    ship = db_spaceships.get(ship_id)
    return ship
- response_model=Spaceship: Dizemos ao FastAPI: "A resposta desta função deve corresponder à estrutura Spaceship. Filtre todos os campos extras e certifique-se de que os tipos estão corretos".

O que isso oferece?

  • Filtragem de dados: Se db_spaceships tivesse campos extras (por exemplo, "secret_code"), eles não seriam incluídos no JSON final.
  • Garantia de estrutura: O cliente da API pode ter certeza de que sempre receberá uma resposta no formato esperado.
  • Documentação: Em /docs agora será exibido um exemplo exato da resposta (Example Value).

B. Modelos para coleções E quanto ao endpoint /spaceships, que retorna uma lista de naves? Para isso, precisamos usar list do módulo typing.

Modifique as importações e o endpoint /spaceships:

# main.py no topo
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List  # <-- Importamos List

# ... código ...

# Indicamos que a resposta é uma lista (List) de objetos do tipo Spaceship
@app.get("/spaceships", response_model=List[Spaceship])
def get_spaceships():
    """
    Retorna uma lista de naves. Cada item da lista
    é verificado de acordo com o modelo Spaceship.
    """
    # Pydantic não pode trabalhar com um dicionário cujas chaves são IDs.
    # Convertemos nosso dicionário em uma lista simples.
    return list(db_spaceships.values())

  • response_model=List[Spaceship]: Indicamos que a resposta será uma lista, onde cada elemento é um objeto que corresponde ao modelo Spaceship.
  • return list(db_spaceships.values()): Uma mudança importante! Pydantic espera um objeto iterável (uma lista), e não um dicionário onde as chaves são IDs. Convertemos os valores do nosso "simulador de BD" em uma lista.

4. Verificando a API aprimorada

Certifique-se de que o servidor uvicorn esteja em execução com --reload.

  1. Verifique http://127.0.0.1:8000/spaceships: Agora a resposta é um array JSON, e não um objeto. Esta é uma estrutura mais correta e padrão para coleções.
    [
      { "name": "Voyager-1", "type": "Sonda", ... },
      { "name": "Hubble Space Telescope", ... }
    ]
    
  2. Verifique http://127.0.0.1:8000/spaceships/1: A resposta não mudou, mas agora está garantida para corresponder ao modelo.
  3. Dê uma olhada em /docs: Na seção "Schemas" na parte inferior da página, seu modelo Spaceship apareceu. E nos exemplos de resposta para os endpoints, agora é exibido um esquema de dados bonito e estruturado.

5. Validação avançada: O "computador de bordo" em ação

Pydantic pode fazer muito mais do que apenas verificar tipos.

Vamos adicionar validação ao nosso modelo Spaceship:

from pydantic import BaseModel, Field

class Spaceship(BaseModel):
    name: str = Field(..., min_length=3, max_length=50, description="Nome da nave")
    type: str
    launch_year: int = Field(..., gt=1950, description="O ano de lançamento deve ser depois de 1950")
    status: str

  • Field(...): Usado para adicionar regras de validação adicionais.
  • ... (Ellipsis): Significa que o campo é obrigatório.
  • min_length, max_length: Restrições para strings.
  • gt: "Greater Than" (maior que).

Embora ainda não estejamos criando novas naves (isso será na próxima seção), essas regras já serão refletidas na documentação e entrarão em vigor quando implementarmos as solicitações POST.


Quiz para fixação

1. Pydantic no FastAPI é usado para...

2. Para criar um modelo de dados, você precisa herdar a classe de...

3. O parâmetro `response_model` no decorador `@app.get` é necessário para...

4. Como indicar que um endpoint retorna uma *lista* de objetos do tipo `Item`?

5. `Field(..., gt=0)` em um modelo Pydantic significa que o campo...


🚀 Resumo do Capítulo:

Você projetou os "projetos digitais" para os dados da sua API. Agora ela não apenas funciona, mas funciona de forma previsível e confiável.

  • 📝 Modelo Spaceship criado usando Pydantic.
  • 🛡️ A API agora valida e filtra os dados de saída usando response_model.
  • 📊 A documentação tornou-se muito mais informativa, mostrando esquemas de dados precisos.

Os projetos estão prontos e aprovados! No próximo capítulo, passaremos da leitura de dados para a criação — implementaremos operações CRUD completas para a nossa frota.

📌 Verificação:

  • Certifique-se de que o esquema do modelo Spaceship apareceu em /docs.
  • Verifique se o endpoint /spaceships agora retorna um array JSON ([...]), e não um objeto ({...}).
  • Certifique-se de que não há erros de sintaxe no código após adicionar os modelos.

⚠️ Se houver erros:

  • NameError: name 'BaseModel' is not defined: Verifique se você importou BaseModel de pydantic.
  • NameError: name 'List' is not defined: Verifique se você importou List de typing.
  • A resposta para /spaceships está vazia ([]): Certifique-se de que você alterou return db_spaceships para return list(db_spaceships.values()).