committed by
GitHub
1 changed files with 357 additions and 0 deletions
@ -0,0 +1,357 @@ |
|||
# 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. |
|||
|
|||
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *} |
|||
|
|||
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. |
|||
|
|||
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *} |
|||
|
|||
/// note | 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. |
|||
|
|||
/// tip | 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: |
|||
|
|||
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *} |
|||
|
|||
/// info | Informação |
|||
|
|||
Para usar `EmailStr`, primeiro instale <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email-validator`</a>. |
|||
|
|||
Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ative-o e instale-o, por exemplo: |
|||
|
|||
```console |
|||
$ pip install email-validator |
|||
``` |
|||
|
|||
ou com: |
|||
|
|||
```console |
|||
$ pip install "pydantic[email]" |
|||
``` |
|||
|
|||
/// |
|||
|
|||
E estamos usando este modelo para declarar nossa entrada e o mesmo modelo para declarar nossa saída: |
|||
|
|||
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *} |
|||
|
|||
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. |
|||
|
|||
/// danger | 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: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *} |
|||
|
|||
Aqui, embora nossa *função de operação de rota* esteja retornando o mesmo usuário de entrada que contém a senha: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *} |
|||
|
|||
...declaramos o `response_model` como nosso modelo `UserOut`, que não inclui a senha: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *} |
|||
|
|||
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. |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *} |
|||
|
|||
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: |
|||
|
|||
<img src="/img/tutorial/response-model/image01.png"> |
|||
|
|||
E ambos os modelos serão usados para a documentação interativa da API: |
|||
|
|||
<img src="/img/tutorial/response-model/image02.png"> |
|||
|
|||
## 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](../advanced/response-directly.md){.internal-link target=_blank}. |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *} |
|||
|
|||
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: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *} |
|||
|
|||
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 <abbr title='Uma união entre vários tipos significa "qualquer um desses tipos".'>união</abbr> entre tipos diferentes onde um ou mais deles não são tipos Pydantic válidos, por exemplo, isso falharia 💥: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *} |
|||
|
|||
... 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`: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *} |
|||
|
|||
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: |
|||
|
|||
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *} |
|||
|
|||
* `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* : |
|||
|
|||
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *} |
|||
|
|||
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á: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"price": 50.2 |
|||
} |
|||
``` |
|||
|
|||
/// info | 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. |
|||
|
|||
/// |
|||
|
|||
/// info | Informação |
|||
|
|||
O FastAPI usa `.dict()` do modelo Pydantic com <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">seu parâmetro `exclude_unset`</a> para chegar a isso. |
|||
|
|||
/// |
|||
|
|||
/// info | Informação |
|||
|
|||
Você também pode usar: |
|||
|
|||
* `response_model_exclude_defaults=True` |
|||
* `response_model_exclude_none=True` |
|||
|
|||
conforme descrito na <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">documentação do Pydantic</a> 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`: |
|||
|
|||
```Python hl_lines="3 5" |
|||
{ |
|||
"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`: |
|||
|
|||
```Python hl_lines="3 5-6" |
|||
{ |
|||
"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. |
|||
|
|||
/// tip | 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. |
|||
|
|||
/// tip | 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. |
|||
|
|||
/// |
|||
|
|||
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *} |
|||
|
|||
/// tip | 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: |
|||
|
|||
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *} |
|||
|
|||
## 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. |
Loading…
Reference in new issue