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
(oustr | None = None
no Python 3.10) tem um padrão deNone
.tax: float = 10.5
tem um padrão de10.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 list
s em vez de set
s¶
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.