Ir para o conteúdo

Modelo de resposta - Tipo de retorno

Você pode declarar o tipo usado para a resposta anotando o tipo de retorno da função de operação de rota.

Você pode usar anotações de tipo da mesma forma que usaria para dados de entrada em parâmetros de função, você pode usar modelos Pydantic, listas, dicionários, valores escalares como inteiros, booleanos, etc.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: list[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]
from typing import List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: List[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> List[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]

O FastAPI usará este tipo de retorno para:

  • Validar os dados retornados.
    • Se os dados forem inválidos (por exemplo, se estiver faltando um campo), significa que o código do seu aplicativo está quebrado, não retornando o que deveria, e retornará um erro de servidor em vez de retornar dados incorretos. Dessa forma, você e seus clientes podem ter certeza de que receberão os dados e o formato de dados esperados.
  • Adicionar um Esquema JSON para a resposta, na operação de rota do OpenAPI.
    • Isso será usado pela documentação automática.
    • Também será usado por ferramentas de geração automática de código do cliente.

Mas o mais importante:

  • Ele limitará e filtrará os dados de saída para o que está definido no tipo de retorno.
    • Isso é particularmente importante para a segurança, veremos mais sobre isso abaixo.

Parâmetro response_model

Existem alguns casos em que você precisa ou deseja retornar alguns dados que não são exatamente o que o tipo declara.

Por exemplo, você pode querer retornar um dicionário ou um objeto de banco de dados, mas declará-lo como um modelo Pydantic. Dessa forma, o modelo Pydantic faria toda a documentação de dados, validação, etc. para o objeto que você retornou (por exemplo, um dicionário ou objeto de banco de dados).

Se você adicionasse a anotação do tipo de retorno, ferramentas e editores reclamariam com um erro (correto) informando que sua função está retornando um tipo (por exemplo, um dict) diferente do que você declarou (por exemplo, um modelo Pydantic).

Nesses casos, você pode usar o parâmetro response_model do decorador de operação de rota em vez do tipo de retorno.

Você pode usar o parâmetro response_model em qualquer uma das operações de rota:

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.delete()
  • etc.
from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any:
    return item


@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]
🤓 Other versions and variants
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: list[str] = []


@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any:
    return item


@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]
from typing import Any, List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: List[str] = []


@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any:
    return item


@app.get("/items/", response_model=List[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]

Nota

Observe que response_model é um parâmetro do método "decorator" (get, post, etc). Não da sua função de operação de rota, como todos os parâmetros e corpo.

response_model recebe o mesmo tipo que você declararia para um campo de modelo Pydantic, então, pode ser um modelo Pydantic, mas também pode ser, por exemplo, uma lista de modelos Pydantic, como List[Item].

O FastAPI usará este response_model para fazer toda a documentação de dados, validação, etc. e também para converter e filtrar os dados de saída para sua declaração de tipo.

Dica

Se você tiver verificações de tipo rigorosas em seu editor, mypy, etc, você pode declarar o tipo de retorno da função como Any.

Dessa forma, você diz ao editor que está retornando qualquer coisa intencionalmente. Mas o FastAPI ainda fará a documentação de dados, validação, filtragem, etc. com o response_model.

Prioridade response_model

Se você declarar tanto um tipo de retorno quanto um response_model, o response_model terá prioridade e será usado pelo FastAPI.

Dessa forma, você pode adicionar anotações de tipo corretas às suas funções, mesmo quando estiver retornando um tipo diferente do modelo de resposta, para ser usado pelo editor e ferramentas como mypy. E ainda assim você pode fazer com que o FastAPI faça a validação de dados, documentação, etc. usando o response_model.

Você também pode usar response_model=None para desabilitar a criação de um modelo de resposta para essa operação de rota, você pode precisar fazer isso se estiver adicionando anotações de tipo para coisas que não são campos Pydantic válidos, você verá um exemplo disso em uma das seções abaixo.

Retorna os mesmos dados de entrada

Aqui estamos declarando um modelo UserIn, ele conterá uma senha em texto simples:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user

Informação

Para usar EmailStr, primeiro instale email-validator.

Certifique-se de criar um ambiente virtual, ative-o e instale-o, por exemplo:

$ pip install email-validator

ou com:

$ pip install "pydantic[email]"

E estamos usando este modelo para declarar nossa entrada e o mesmo modelo para declarar nossa saída:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user

Agora, sempre que um navegador estiver criando um usuário com uma senha, a API retornará a mesma senha na resposta.

Neste caso, pode não ser um problema, porque é o mesmo usuário enviando a senha.

Mas se usarmos o mesmo modelo para outra operação de rota, poderíamos estar enviando as senhas dos nossos usuários para todos os clientes.

Perigo

Nunca armazene a senha simples de um usuário ou envie-a em uma resposta como esta, a menos que você saiba todas as ressalvas e saiba o que está fazendo.

Adicionar um modelo de saída

Podemos, em vez disso, criar um modelo de entrada com a senha em texto simples e um modelo de saída sem ela:

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user
🤓 Other versions and variants
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

Aqui, embora nossa função de operação de rota esteja retornando o mesmo usuário de entrada que contém a senha:

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user
🤓 Other versions and variants
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

...declaramos o response_model como nosso modelo UserOut, que não inclui a senha:

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user
🤓 Other versions and variants
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

Então, FastAPI cuidará de filtrar todos os dados que não são declarados no modelo de saída (usando Pydantic).

response_model ou Tipo de Retorno

Neste caso, como os dois modelos são diferentes, se anotássemos o tipo de retorno da função como UserOut, o editor e as ferramentas reclamariam que estamos retornando um tipo inválido, pois são classes diferentes.

É por isso que neste exemplo temos que declará-lo no parâmetro response_model.

...mas continue lendo abaixo para ver como superar isso.

Tipo de Retorno e Filtragem de Dados

Vamos continuar do exemplo anterior. Queríamos anotar a função com um tipo, mas queríamos poder retornar da função algo que realmente incluísse mais dados.

Queremos que o FastAPI continue filtrando os dados usando o modelo de resposta. Para que, embora a função retorne mais dados, a resposta inclua apenas os campos declarados no modelo de resposta.

No exemplo anterior, como as classes eram diferentes, tivemos que usar o parâmetro response_model. Mas isso também significa que não temos suporte do editor e das ferramentas verificando o tipo de retorno da função.

Mas na maioria dos casos em que precisamos fazer algo assim, queremos que o modelo apenas filtre/remova alguns dados como neste exemplo.

E nesses casos, podemos usar classes e herança para aproveitar as anotações de tipo de função para obter melhor suporte no editor e nas ferramentas, e ainda obter a filtragem de dados FastAPI.

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class BaseUser(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


class UserIn(BaseUser):
    password: str


@app.post("/user/")
async def create_user(user: UserIn) -> BaseUser:
    return user
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class BaseUser(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserIn(BaseUser):
    password: str


@app.post("/user/")
async def create_user(user: UserIn) -> BaseUser:
    return user

Com isso, temos suporte de ferramentas, de editores e mypy, pois este código está correto em termos de tipos, mas também obtemos a filtragem de dados do FastAPI.

Como isso funciona? Vamos verificar. 🤓

Anotações de tipo e ferramentas

Primeiro, vamos ver como editores, mypy e outras ferramentas veriam isso.

BaseUser tem os campos base. Então UserIn herda de BaseUser e adiciona o campo password, então, ele incluirá todos os campos de ambos os modelos.

Anotamos o tipo de retorno da função como BaseUser, mas na verdade estamos retornando uma instância UserIn.

O editor, mypy e outras ferramentas não reclamarão disso porque, em termos de digitação, UserIn é uma subclasse de BaseUser, o que significa que é um tipo válido quando o que é esperado é qualquer coisa que seja um BaseUser.

Filtragem de dados FastAPI

Agora, para FastAPI, ele verá o tipo de retorno e garantirá que o que você retornar inclua apenas os campos que são declarados no tipo.

O FastAPI faz várias coisas internamente com o Pydantic para garantir que essas mesmas regras de herança de classe não sejam usadas para a filtragem de dados retornados, caso contrário, você pode acabar retornando muito mais dados do que o esperado.

Dessa forma, você pode obter o melhor dos dois mundos: anotações de tipo com suporte a ferramentas e filtragem de dados.

Veja na documentação

Quando você vê a documentação automática, pode verificar se o modelo de entrada e o modelo de saída terão seus próprios esquemas JSON:

E ambos os modelos serão usados ​​para a documentação interativa da API:

Outras anotações de tipo de retorno

Pode haver casos em que você retorna algo que não é um campo Pydantic válido e anota na função, apenas para obter o suporte fornecido pelas ferramentas (o editor, mypy, etc).

Retornar uma resposta diretamente

O caso mais comum seria retornar uma resposta diretamente, conforme explicado posteriormente na documentação avançada.

from fastapi import FastAPI, Response
from fastapi.responses import JSONResponse, RedirectResponse

app = FastAPI()


@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return JSONResponse(content={"message": "Here's your interdimensional portal."})

Este caso simples é tratado automaticamente pelo FastAPI porque a anotação do tipo de retorno é a classe (ou uma subclasse de) Response.

E as ferramentas também ficarão felizes porque RedirectResponse e ​​JSONResponse são subclasses de Response, então a anotação de tipo está correta.

Anotar uma subclasse de resposta

Você também pode usar uma subclasse de Response na anotação de tipo:

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/teleport")
async def get_teleport() -> RedirectResponse:
    return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")

Isso também funcionará porque RedirectResponse é uma subclasse de Response, e o FastAPI tratará automaticamente este caso simples.

Anotações de Tipo de Retorno Inválido

Mas quando você retorna algum outro objeto arbitrário que não é um tipo Pydantic válido (por exemplo, um objeto de banco de dados) e você o anota dessa forma na função, o FastAPI tentará criar um modelo de resposta Pydantic a partir dessa anotação de tipo e falhará.

O mesmo aconteceria se você tivesse algo como uma união entre tipos diferentes onde um ou mais deles não são tipos Pydantic válidos, por exemplo, isso falharia 💥:

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response | dict:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal")
async def get_portal(teleport: bool = False) -> Union[Response, dict]:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}

... isso falha porque a anotação de tipo não é um tipo Pydantic e não é apenas uma única classe ou subclasse Response, é uma união (qualquer uma das duas) entre um Response e ​​um dict.

Desabilitar modelo de resposta

Continuando com o exemplo acima, você pode não querer ter a validação de dados padrão, documentação, filtragem, etc. que é realizada pelo FastAPI.

Mas você pode querer manter a anotação do tipo de retorno na função para obter o suporte de ferramentas como editores e verificadores de tipo (por exemplo, mypy).

Neste caso, você pode desabilitar a geração do modelo de resposta definindo response_model=None:

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Response | dict:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Union[Response, dict]:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}

Isso fará com que o FastAPI pule a geração do modelo de resposta e, dessa forma, você pode ter quaisquer anotações de tipo de retorno que precisar sem afetar seu aplicativo FastAPI. 🤓

Parâmetros de codificação do modelo de resposta

Seu modelo de resposta pode ter valores padrão, como:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
from typing import List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: List[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
  • description: Union[str, None] = None (ou str | None = None no Python 3.10) tem um padrão de None.
  • tax: float = 10.5 tem um padrão de 10.5.
  • tags: List[str] = [] tem um padrão de uma lista vazia: [].

mas você pode querer omiti-los do resultado se eles não foram realmente armazenados.

Por exemplo, se você tem modelos com muitos atributos opcionais em um banco de dados NoSQL, mas não quer enviar respostas JSON muito longas cheias de valores padrão.

Usar o parâmetro response_model_exclude_unset

Você pode definir o parâmetro response_model_exclude_unset=True do decorador de operação de rota :

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
from typing import List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: List[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]

e esses valores padrão não serão incluídos na resposta, apenas os valores realmente definidos.

Então, se você enviar uma solicitação para essa operação de rota para o item com ID foo, a resposta (sem incluir valores padrão) será:

{
"name": "Foo",
"price": 50.2
}

Informação

No Pydantic v1, o método era chamado .dict(), ele foi descontinuado (mas ainda suportado) no Pydantic v2 e renomeado para .model_dump().

Os exemplos aqui usam .dict() para compatibilidade com Pydantic v1, mas você deve usar .model_dump() em vez disso se puder usar Pydantic v2.

Informação

O FastAPI usa .dict() do modelo Pydantic com seu parâmetro exclude_unset para chegar a isso.

Informação

Você também pode usar:

  • response_model_exclude_defaults=True
  • response_model_exclude_none=True

conforme descrito na documentação do Pydantic para exclude_defaults e exclude_none.

Dados com valores para campos com padrões

Mas se seus dados tiverem valores para os campos do modelo com valores padrões, como o item com ID bar:

{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}

eles serão incluídos na resposta.

Dados com os mesmos valores que os padrões

Se os dados tiverem os mesmos valores que os padrões, como o item com ID baz:

{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}

O FastAPI é inteligente o suficiente (na verdade, o Pydantic é inteligente o suficiente) para perceber que, embora description, tax e tags tenham os mesmos valores que os padrões, eles foram definidos explicitamente (em vez de retirados dos padrões).

Portanto, eles serão incluídos na resposta JSON.

Dica

Observe que os valores padrão podem ser qualquer coisa, não apenas None.

Eles podem ser uma lista ([]), um float de 10.5, etc.

response_model_include e response_model_exclude

Você também pode usar os parâmetros response_model_include e response_model_exclude do decorador de operação de rota.

Eles pegam um set de str com o nome dos atributos para incluir (omitindo o resto) ou para excluir (incluindo o resto).

Isso pode ser usado como um atalho rápido se você tiver apenas um modelo Pydantic e quiser remover alguns dados da saída.

Dica

Mas ainda é recomendado usar as ideias acima, usando várias classes, em vez desses parâmetros.

Isso ocorre porque o Schema JSON gerado no OpenAPI do seu aplicativo (e a documentação) ainda será o único para o modelo completo, mesmo que você use response_model_include ou response_model_exclude para omitir alguns atributos.

Isso também se aplica ao response_model_by_alias que funciona de forma semelhante.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
    return items[item_id]
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
    return items[item_id]

Dica

A sintaxe {"nome", "descrição"} cria um conjunto com esses dois valores.

É equivalente a set(["nome", "descrição"]).

Usando lists em vez de sets

Se você esquecer de usar um set e usar uma lista ou tupla em vez disso, o FastAPI ainda o converterá em um set e funcionará corretamente:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
    return items[item_id]
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
    return items[item_id]

Recapitulação

Use o parâmetro response_model do decorador de operação de rota para definir modelos de resposta e, especialmente, para garantir que dados privados sejam filtrados.

Use response_model_exclude_unset para retornar apenas os valores definidos explicitamente.