committed by
GitHub
21 changed files with 1993 additions and 29 deletions
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 18 KiB |
@ -0,0 +1,5 @@ |
|||||
|
# Apprendre |
||||
|
|
||||
|
Voici les sections introductives et les tutoriels pour apprendre **FastAPI**. |
||||
|
|
||||
|
Vous pouvez considérer ceci comme un **manuel**, un **cours**, la **méthode officielle** et recommandée pour appréhender FastAPI. 😎 |
@ -0,0 +1,240 @@ |
|||||
|
# Retornos Adicionais no OpenAPI |
||||
|
|
||||
|
!!! warning "Aviso" |
||||
|
Este é um tema bem avançado. |
||||
|
|
||||
|
Se você está começando com o **FastAPI**, provavelmente você não precisa disso. |
||||
|
|
||||
|
Você pode declarar retornos adicionais, com códigos de status adicionais, media types, descrições, etc. |
||||
|
|
||||
|
Essas respostas adicionais serão incluídas no esquema do OpenAPI, e também aparecerão na documentação da API. |
||||
|
|
||||
|
Porém para as respostas adicionais, você deve garantir que está retornando um `Response` como por exemplo o `JSONResponse` diretamente, junto com o código de status e o conteúdo. |
||||
|
|
||||
|
## Retorno Adicional com `model` |
||||
|
|
||||
|
Você pode fornecer o parâmetro `responses` aos seus *decoradores de caminho*. |
||||
|
|
||||
|
Este parâmetro recebe um `dict`, as chaves são os códigos de status para cada retorno, como por exemplo `200`, e os valores são um outro `dict` com a informação de cada um deles. |
||||
|
|
||||
|
Cada um desses `dict` de retorno pode ter uma chave `model`, contendo um modelo do Pydantic, assim como o `response_model`. |
||||
|
|
||||
|
O **FastAPI** pegará este modelo, gerará o esquema JSON dele e incluirá no local correto do OpenAPI. |
||||
|
|
||||
|
Por exemplo, para declarar um outro retorno com o status code `404` e um modelo do Pydantic chamado `Message`, você pode escrever: |
||||
|
|
||||
|
```Python hl_lines="18 22" |
||||
|
{!../../../docs_src/additional_responses/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! note "Nota" |
||||
|
Lembre-se que você deve retornar o `JSONResponse` diretamente. |
||||
|
|
||||
|
!!! info "Informação" |
||||
|
A chave `model` não é parte do OpenAPI. |
||||
|
|
||||
|
O **FastAPI** pegará o modelo do Pydantic, gerará o `JSON Schema`, e adicionará no local correto. |
||||
|
|
||||
|
O local correto é: |
||||
|
|
||||
|
* Na chave `content`, que tem como valor um outro objeto JSON (`dict`) que contém: |
||||
|
* Uma chave com o media type, como por exemplo `application/json`, que contém como valor um outro objeto JSON, contendo:: |
||||
|
* Uma chave `schema`, que contém como valor o JSON Schema do modelo, sendo este o local correto. |
||||
|
* O **FastAPI** adiciona aqui a referência dos esquemas JSON globais que estão localizados em outro lugar, ao invés de incluí-lo diretamente. Deste modo, outras aplicações e clientes podem utilizar estes esquemas JSON diretamente, fornecer melhores ferramentas de geração de código, etc. |
||||
|
|
||||
|
O retorno gerado no OpenAI para esta *operação de caminho* será: |
||||
|
|
||||
|
```JSON hl_lines="3-12" |
||||
|
{ |
||||
|
"responses": { |
||||
|
"404": { |
||||
|
"description": "Additional Response", |
||||
|
"content": { |
||||
|
"application/json": { |
||||
|
"schema": { |
||||
|
"$ref": "#/components/schemas/Message" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"200": { |
||||
|
"description": "Successful Response", |
||||
|
"content": { |
||||
|
"application/json": { |
||||
|
"schema": { |
||||
|
"$ref": "#/components/schemas/Item" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"422": { |
||||
|
"description": "Validation Error", |
||||
|
"content": { |
||||
|
"application/json": { |
||||
|
"schema": { |
||||
|
"$ref": "#/components/schemas/HTTPValidationError" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Os esquemas são referenciados em outro local dentro do esquema OpenAPI: |
||||
|
|
||||
|
```JSON hl_lines="4-16" |
||||
|
{ |
||||
|
"components": { |
||||
|
"schemas": { |
||||
|
"Message": { |
||||
|
"title": "Message", |
||||
|
"required": [ |
||||
|
"message" |
||||
|
], |
||||
|
"type": "object", |
||||
|
"properties": { |
||||
|
"message": { |
||||
|
"title": "Message", |
||||
|
"type": "string" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"Item": { |
||||
|
"title": "Item", |
||||
|
"required": [ |
||||
|
"id", |
||||
|
"value" |
||||
|
], |
||||
|
"type": "object", |
||||
|
"properties": { |
||||
|
"id": { |
||||
|
"title": "Id", |
||||
|
"type": "string" |
||||
|
}, |
||||
|
"value": { |
||||
|
"title": "Value", |
||||
|
"type": "string" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"ValidationError": { |
||||
|
"title": "ValidationError", |
||||
|
"required": [ |
||||
|
"loc", |
||||
|
"msg", |
||||
|
"type" |
||||
|
], |
||||
|
"type": "object", |
||||
|
"properties": { |
||||
|
"loc": { |
||||
|
"title": "Location", |
||||
|
"type": "array", |
||||
|
"items": { |
||||
|
"type": "string" |
||||
|
} |
||||
|
}, |
||||
|
"msg": { |
||||
|
"title": "Message", |
||||
|
"type": "string" |
||||
|
}, |
||||
|
"type": { |
||||
|
"title": "Error Type", |
||||
|
"type": "string" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"HTTPValidationError": { |
||||
|
"title": "HTTPValidationError", |
||||
|
"type": "object", |
||||
|
"properties": { |
||||
|
"detail": { |
||||
|
"title": "Detail", |
||||
|
"type": "array", |
||||
|
"items": { |
||||
|
"$ref": "#/components/schemas/ValidationError" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Media types adicionais para o retorno principal |
||||
|
|
||||
|
Você pode utilizar o mesmo parâmetro `responses` para adicionar diferentes media types para o mesmo retorno principal. |
||||
|
|
||||
|
Por exemplo, você pode adicionar um media type adicional de `image/png`, declarando que a sua *operação de caminho* pode retornar um objeto JSON (com o media type `application/json`) ou uma imagem PNG: |
||||
|
|
||||
|
```Python hl_lines="19-24 28" |
||||
|
{!../../../docs_src/additional_responses/tutorial002.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! note "Nota" |
||||
|
Note que você deve retornar a imagem utilizando um `FileResponse` diretamente. |
||||
|
|
||||
|
!!! info "Informação" |
||||
|
A menos que você especifique um media type diferente explicitamente em seu parâmetro `responses`, o FastAPI assumirá que o retorno possui o mesmo media type contido na classe principal de retorno (padrão `application/json`). |
||||
|
|
||||
|
Porém se você especificou uma classe de retorno com o valor `None` como media type, o FastAPI utilizará `application/json` para qualquer retorno adicional que possui um modelo associado. |
||||
|
|
||||
|
## Combinando informações |
||||
|
|
||||
|
Você também pode combinar informações de diferentes lugares, incluindo os parâmetros `response_model`, `status_code`, e `responses`. |
||||
|
|
||||
|
Você pode declarar um `response_model`, utilizando o código de status padrão `200` (ou um customizado caso você precise), e depois adicionar informações adicionais para esse mesmo retorno em `responses`, diretamente no esquema OpenAPI. |
||||
|
|
||||
|
O **FastAPI** manterá as informações adicionais do `responses`, e combinará com o esquema JSON do seu modelo. |
||||
|
|
||||
|
Por exemplo, você pode declarar um retorno com o código de status `404` que utiliza um modelo do Pydantic que possui um `description` customizado. |
||||
|
|
||||
|
E um retorno com o código de status `200` que utiliza o seu `response_model`, porém inclui um `example` customizado: |
||||
|
|
||||
|
```Python hl_lines="20-31" |
||||
|
{!../../../docs_src/additional_responses/tutorial003.py!} |
||||
|
``` |
||||
|
|
||||
|
Isso será combinado e incluído em seu OpenAPI, e disponibilizado na documentação da sua API: |
||||
|
|
||||
|
<img src="/img/tutorial/additional-responses/image01.png"> |
||||
|
|
||||
|
## Combinar retornos predefinidos e personalizados |
||||
|
|
||||
|
Você pode querer possuir alguns retornos predefinidos que são aplicados para diversas *operações de caminho*, porém você deseja combinar com retornos personalizados que são necessários para cada *operação de caminho*. |
||||
|
|
||||
|
Para estes casos, você pode utilizar a técnica do Python de "desempacotamento" de um `dict` utilizando `**dict_to_unpack`: |
||||
|
|
||||
|
```Python |
||||
|
old_dict = { |
||||
|
"old key": "old value", |
||||
|
"second old key": "second old value", |
||||
|
} |
||||
|
new_dict = {**old_dict, "new key": "new value"} |
||||
|
``` |
||||
|
|
||||
|
Aqui, o `new_dict` terá todos os pares de chave-valor do `old_dict` mais o novo par de chave-valor: |
||||
|
|
||||
|
```Python |
||||
|
{ |
||||
|
"old key": "old value", |
||||
|
"second old key": "second old value", |
||||
|
"new key": "new value", |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Você pode utilizar essa técnica para reutilizar alguns retornos predefinidos nas suas *operações de caminho* e combiná-las com personalizações adicionais. |
||||
|
|
||||
|
Por exemplo: |
||||
|
|
||||
|
```Python hl_lines="13-17 26" |
||||
|
{!../../../docs_src/additional_responses/tutorial004.py!} |
||||
|
``` |
||||
|
|
||||
|
## Mais informações sobre retornos OpenAPI |
||||
|
|
||||
|
Para verificar exatamente o que você pode incluir nos retornos, você pode conferir estas seções na especificação do OpenAPI: |
||||
|
|
||||
|
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responsesObject" class="external-link" target="_blank">Objeto de Retorno OpenAPI</a>, inclui o `Response Object`. |
||||
|
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responseObject" class="external-link" target="_blank">Objeto de Retorno OpenAPI</a>, você pode incluir qualquer coisa dele diretamente em cada retorno dentro do seu parâmetro `responses`. Incluindo `description`, `headers`, `content` (dentro dele que você declara diferentes media types e esquemas JSON), e `links`. |
@ -0,0 +1,69 @@ |
|||||
|
# Códigos de status adicionais |
||||
|
|
||||
|
Por padrão, o **FastAPI** retornará as respostas utilizando o `JSONResponse`, adicionando o conteúdo do retorno da sua *operação de caminho* dentro do `JSONResponse`. |
||||
|
|
||||
|
Ele usará o código de status padrão ou o que você definir na sua *operação de caminho*. |
||||
|
|
||||
|
## Códigos de status adicionais |
||||
|
|
||||
|
Caso você queira retornar códigos de status adicionais além do código principal, você pode fazer isso retornando um `Response` diretamente, como por exemplo um `JSONResponse`, e definir os códigos de status adicionais diretamente. |
||||
|
|
||||
|
Por exemplo, vamos dizer que você deseja ter uma *operação de caminho* que permita atualizar itens, e retornar um código de status HTTP 200 "OK" quando for bem sucedido. |
||||
|
|
||||
|
Mas você também deseja aceitar novos itens. E quando os itens não existiam, ele os cria, e retorna o código de status HTTP 201 "Created. |
||||
|
|
||||
|
Para conseguir isso, importe `JSONResponse` e retorne o seu conteúdo diretamente, definindo o `status_code` que você deseja: |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="4 25" |
||||
|
{!> ../../../docs_src/additional_status_codes/tutorial001_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="4 25" |
||||
|
{!> ../../../docs_src/additional_status_codes/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="4 26" |
||||
|
{!> ../../../docs_src/additional_status_codes/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Faça uso da versão `Annotated` quando possível. |
||||
|
|
||||
|
```Python hl_lines="2 23" |
||||
|
{!> ../../../docs_src/additional_status_codes/tutorial001_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Faça uso da versão `Annotated` quando possível. |
||||
|
|
||||
|
```Python hl_lines="4 25" |
||||
|
{!> ../../../docs_src/additional_status_codes/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! warning "Aviso" |
||||
|
Quando você retorna um `Response` diretamente, como no exemplo acima, ele será retornado diretamente. |
||||
|
|
||||
|
Ele não será serializado com um modelo, etc. |
||||
|
|
||||
|
Garanta que ele tenha toda informação que você deseja, e que os valores sejam um JSON válido (caso você esteja usando `JSONResponse`). |
||||
|
|
||||
|
!!! note "Detalhes técnicos" |
||||
|
Você também pode utilizar `from starlette.responses import JSONResponse`. |
||||
|
|
||||
|
O **FastAPI** disponibiliza o `starlette.responses` como `fastapi.responses` apenas por conveniência para você, o programador. Porém a maioria dos retornos disponíveis vem diretamente do Starlette. O mesmo com `status`. |
||||
|
|
||||
|
## OpenAPI e documentação da API |
||||
|
|
||||
|
Se você retorna códigos de status adicionais e retornos diretamente, eles não serão incluídos no esquema do OpenAPI (a documentação da API), porque o FastAPI não tem como saber de antemão o que será retornado. |
||||
|
|
||||
|
Mas você pode documentar isso no seu código, utilizando: [Retornos Adicionais](additional-responses.md){.internal-link target=_blank}. |
@ -0,0 +1,138 @@ |
|||||
|
# Dependências avançadas |
||||
|
|
||||
|
## Dependências parametrizadas |
||||
|
|
||||
|
Todas as dependências que vimos até agora são funções ou classes fixas. |
||||
|
|
||||
|
Mas podem ocorrer casos onde você deseja ser capaz de definir parâmetros na dependência, sem ter a necessidade de declarar diversas funções ou classes. |
||||
|
|
||||
|
Vamos imaginar que queremos ter uma dependência que verifica se o parâmetro de consulta `q` possui um valor fixo. |
||||
|
|
||||
|
Porém nós queremos poder parametrizar o conteúdo fixo. |
||||
|
|
||||
|
## Uma instância "chamável" |
||||
|
|
||||
|
Em Python existe uma maneira de fazer com que uma instância de uma classe seja um "chamável". |
||||
|
|
||||
|
Não propriamente a classe (que já é um chamável), mas a instância desta classe. |
||||
|
|
||||
|
Para fazer isso, nós declaramos o método `__call__`: |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="12" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="11" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Prefira utilizar a versão `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="10" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011.py!} |
||||
|
``` |
||||
|
|
||||
|
Neste caso, o `__call__` é o que o **FastAPI** utilizará para verificar parâmetros adicionais e sub dependências, e isso é o que será chamado para passar o valor ao parâmetro na sua *função de operação de rota* posteriormente. |
||||
|
|
||||
|
## Parametrizar a instância |
||||
|
|
||||
|
E agora, nós podemos utilizar o `__init__` para declarar os parâmetros da instância que podemos utilizar para "parametrizar" a dependência: |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="9" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="8" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Prefira utilizar a versão `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="7" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011.py!} |
||||
|
``` |
||||
|
|
||||
|
Neste caso, o **FastAPI** nunca tocará ou se importará com o `__init__`, nós vamos utilizar diretamente em nosso código. |
||||
|
|
||||
|
## Crie uma instância |
||||
|
|
||||
|
Nós poderíamos criar uma instância desta classe com: |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="18" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="17" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Prefira utilizar a versão `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="16" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011.py!} |
||||
|
``` |
||||
|
|
||||
|
E deste modo nós podemos "parametrizar" a nossa dependência, que agora possui `"bar"` dentro dele, como o atributo `checker.fixed_content`. |
||||
|
|
||||
|
## Utilize a instância como dependência |
||||
|
|
||||
|
Então, nós podemos utilizar este `checker` em um `Depends(checker)`, no lugar de `Depends(FixedContentQueryChecker)`, porque a dependência é a instância, `checker`, e não a própria classe. |
||||
|
|
||||
|
E quando a dependência for resolvida, o **FastAPI** chamará este `checker` como: |
||||
|
|
||||
|
```Python |
||||
|
checker(q="somequery") |
||||
|
``` |
||||
|
|
||||
|
...e passar o que quer que isso retorne como valor da dependência em nossa *função de operação de rota* como o parâmetro `fixed_content_included`: |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="22" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="21" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Prefira utilizar a versão `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="20" |
||||
|
{!> ../../../docs_src/dependencies/tutorial011.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Tudo isso parece não ser natural. E pode não estar muito claro ou aparentar ser útil ainda. |
||||
|
|
||||
|
Estes exemplos são intencionalmente simples, porém mostram como tudo funciona. |
||||
|
|
||||
|
Nos capítulos sobre segurança, existem funções utilitárias que são implementadas desta maneira. |
||||
|
|
||||
|
Se você entendeu tudo isso, você já sabe como essas funções utilitárias para segurança funcionam por debaixo dos panos. |
@ -0,0 +1,485 @@ |
|||||
|
# Configurações e Variáveis de Ambiente |
||||
|
|
||||
|
Em muitos casos a sua aplicação pode precisar de configurações externas, como chaves secretas, credenciais de banco de dados, credenciais para serviços de email, etc. |
||||
|
|
||||
|
A maioria dessas configurações é variável (podem mudar), como URLs de bancos de dados. E muitas delas podem conter dados sensíveis, como tokens secretos. |
||||
|
|
||||
|
Por isso é comum prover essas configurações como variáveis de ambiente que são utilizidas pela aplicação. |
||||
|
|
||||
|
## Variáveis de Ambiente |
||||
|
|
||||
|
!!! dica |
||||
|
Se você já sabe o que são variáveis de ambiente e como utilizá-las, sinta-se livre para avançar para o próximo tópico. |
||||
|
|
||||
|
Uma <a href="https://pt.wikipedia.org/wiki/Variável_de_ambiente" class="external-link" target="_blank">variável de ambiente</a> (abreviada em inglês para "env var") é uma variável definida fora do código Python, no sistema operacional, e pode ser lida pelo seu código Python (ou por outros programas). |
||||
|
|
||||
|
Você pode criar e utilizar variáveis de ambiente no terminal, sem precisar utilizar Python: |
||||
|
|
||||
|
=== "Linux, macOS, Windows Bash" |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
// Você pode criar uma env var MY_NAME usando |
||||
|
$ export MY_NAME="Wade Wilson" |
||||
|
|
||||
|
// E utilizá-la em outros programas, como |
||||
|
$ echo "Hello $MY_NAME" |
||||
|
|
||||
|
Hello Wade Wilson |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
=== "Windows PowerShell" |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
// Criando env var MY_NAME |
||||
|
$ $Env:MY_NAME = "Wade Wilson" |
||||
|
|
||||
|
// Usando em outros programas, como |
||||
|
$ echo "Hello $Env:MY_NAME" |
||||
|
|
||||
|
Hello Wade Wilson |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
### Lendo variáveis de ambiente com Python |
||||
|
|
||||
|
Você também pode criar variáveis de ambiente fora do Python, no terminal (ou com qualquer outro método), e realizar a leitura delas no Python. |
||||
|
|
||||
|
Por exemplo, você pode definir um arquivo `main.py` com o seguinte código: |
||||
|
|
||||
|
```Python hl_lines="3" |
||||
|
import os |
||||
|
|
||||
|
name = os.getenv("MY_NAME", "World") |
||||
|
print(f"Hello {name} from Python") |
||||
|
``` |
||||
|
|
||||
|
!!! dica |
||||
|
O segundo parâmetro em <a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> é o valor padrão para o retorno. |
||||
|
|
||||
|
Se nenhum valor for informado, `None` é utilizado por padrão, aqui definimos `"World"` como o valor padrão a ser utilizado. |
||||
|
|
||||
|
E depois você pode executar esse arquivo: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
// Aqui ainda não definimos a env var |
||||
|
$ python main.py |
||||
|
|
||||
|
// Por isso obtemos o valor padrão |
||||
|
|
||||
|
Hello World from Python |
||||
|
|
||||
|
// Mas se definirmos uma variável de ambiente primeiro |
||||
|
$ export MY_NAME="Wade Wilson" |
||||
|
|
||||
|
// E executarmos o programa novamente |
||||
|
$ python main.py |
||||
|
|
||||
|
// Agora ele pode ler a variável de ambiente |
||||
|
|
||||
|
Hello Wade Wilson from Python |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
Como variáveis de ambiente podem ser definidas fora do código da aplicação, mas acessadas pela aplicação, e não precisam ser armazenadas (versionadas com `git`) junto dos outros arquivos, é comum utilizá-las para guardar configurações. |
||||
|
|
||||
|
Você também pode criar uma variável de ambiente específica para uma invocação de um programa, que é acessível somente para esse programa, e somente enquanto ele estiver executando. |
||||
|
|
||||
|
Para fazer isso, crie a variável imediatamente antes de iniciar o programa, na mesma linha: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
// Criando uma env var MY_NAME na mesma linha da execução do programa |
||||
|
$ MY_NAME="Wade Wilson" python main.py |
||||
|
|
||||
|
// Agora a aplicação consegue ler a variável de ambiente |
||||
|
|
||||
|
Hello Wade Wilson from Python |
||||
|
|
||||
|
// E a variável deixa de existir após isso |
||||
|
$ python main.py |
||||
|
|
||||
|
Hello World from Python |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
!!! dica |
||||
|
Você pode ler mais sobre isso em: <a href="https://12factor.net/pt_br/config" class="external-link" target="_blank">The Twelve-Factor App: Configurações</a>. |
||||
|
|
||||
|
### Tipagem e Validação |
||||
|
|
||||
|
Essas variáveis de ambiente suportam apenas strings, por serem externas ao Python e por que precisam ser compatíveis com outros programas e o resto do sistema (e até mesmo com outros sistemas operacionais, como Linux, Windows e macOS). |
||||
|
|
||||
|
Isso significa que qualquer valor obtido de uma variável de ambiente em Python terá o tipo `str`, e qualquer conversão para um tipo diferente ou validação deve ser realizada no código. |
||||
|
|
||||
|
## Pydantic `Settings` |
||||
|
|
||||
|
Por sorte, o Pydantic possui uma funcionalidade para lidar com essas configurações vindas de variáveis de ambiente utilizando <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" class="external-link" target="_blank">Pydantic: Settings management</a>. |
||||
|
|
||||
|
### Instalando `pydantic-settings` |
||||
|
|
||||
|
Primeiro, instale o pacote `pydantic-settings`: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
$ pip install pydantic-settings |
||||
|
---> 100% |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
Ele também está incluído no fastapi quando você instala com a opção `all`: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
$ pip install "fastapi[all]" |
||||
|
---> 100% |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
!!! info |
||||
|
Na v1 do Pydantic ele estava incluído no pacote principal. Agora ele está distribuido como um pacote independente para que você possa optar por instalar ou não caso você não precise dessa funcionalidade. |
||||
|
|
||||
|
### Criando o objeto `Settings` |
||||
|
|
||||
|
Importe a classe `BaseSettings` do Pydantic e crie uma nova subclasse, de forma parecida com um modelo do Pydantic. |
||||
|
|
||||
|
Os atributos da classe são declarados com anotações de tipo, e possíveis valores padrão, da mesma maneira que os modelos do Pydantic. |
||||
|
|
||||
|
Você pode utilizar todas as ferramentas e funcionalidades de validação que são utilizadas nos modelos do Pydantic, como tipos de dados diferentes e validações adicionei com `Field()`. |
||||
|
|
||||
|
=== "Pydantic v2" |
||||
|
|
||||
|
```Python hl_lines="2 5-8 11" |
||||
|
{!> ../../../docs_src/settings/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Pydantic v1" |
||||
|
|
||||
|
!!! Info |
||||
|
Na versão 1 do Pydantic você importaria `BaseSettings` diretamente do módulo `pydantic` em vez do módulo `pydantic_settings`. |
||||
|
|
||||
|
```Python hl_lines="2 5-8 11" |
||||
|
{!> ../../../docs_src/settings/tutorial001_pv1.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! dica |
||||
|
Se você quiser algo pronto para copiar e colar na sua aplicação, não use esse exemplo, mas sim o exemplo abaixo. |
||||
|
|
||||
|
Portanto, quando você cria uma instância da classe `Settings` (nesse caso, o objeto `settings`), o Pydantic lê as variáveis de ambiente sem diferenciar maiúsculas e minúsculas, por isso, uma variável maiúscula `APP_NAME` será usada para o atributo `app_name`. |
||||
|
|
||||
|
Depois ele irá converter e validar os dados. Assim, quando você utilizar aquele objeto `settings`, os dados terão o tipo que você declarou (e.g. `items_per_user` será do tipo `int`). |
||||
|
|
||||
|
### Usando o objeto `settings` |
||||
|
|
||||
|
Depois, Você pode utilizar o novo objeto `settings` na sua aplicação: |
||||
|
|
||||
|
```Python hl_lines="18-20" |
||||
|
{!../../../docs_src/settings/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
### Executando o servidor |
||||
|
|
||||
|
No próximo passo, você pode inicializar o servidor passando as configurações em forma de variáveis de ambiente, por exemplo, você poderia definir `ADMIN_EMAIL` e `APP_NAME` da seguinte forma: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
$ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" fastapi run main.py |
||||
|
|
||||
|
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
!!! dica |
||||
|
Para definir múltiplas variáveis de ambiente para um único comando basta separá-las utilizando espaços, e incluir todas elas antes do comando. |
||||
|
|
||||
|
Assim, o atributo `admin_email` seria definido como `"[email protected]"`. |
||||
|
|
||||
|
`app_name` seria `"ChimichangApp"`. |
||||
|
|
||||
|
E `items_per_user` manteria o valor padrão de `50`. |
||||
|
|
||||
|
## Configurações em um módulo separado |
||||
|
|
||||
|
Você também pode incluir essas configurações em um arquivo de um módulo separado como visto em [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=\_blank}. |
||||
|
|
||||
|
Por exemplo, você pode adicionar um arquivo `config.py` com: |
||||
|
|
||||
|
```Python |
||||
|
{!../../../docs_src/settings/app01/config.py!} |
||||
|
``` |
||||
|
|
||||
|
E utilizar essa configuração em `main.py`: |
||||
|
|
||||
|
```Python hl_lines="3 11-13" |
||||
|
{!../../../docs_src/settings/app01/main.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! dica |
||||
|
Você também precisa incluir um arquivo `__init__.py` como visto em [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=\_blank}. |
||||
|
|
||||
|
## Configurações em uma dependência |
||||
|
|
||||
|
Em certas ocasiões, pode ser útil fornecer essas configurações a partir de uma dependência, em vez de definir um objeto global `settings` que é utilizado em toda a aplicação. |
||||
|
|
||||
|
Isso é especialmente útil durante os testes, já que é bastante simples sobrescrever uma dependência com suas configurações personalizadas. |
||||
|
|
||||
|
### O arquivo de configuração |
||||
|
|
||||
|
Baseando-se no exemplo anterior, seu arquivo `config.py` seria parecido com isso: |
||||
|
|
||||
|
```Python hl_lines="10" |
||||
|
{!../../../docs_src/settings/app02/config.py!} |
||||
|
``` |
||||
|
|
||||
|
Perceba que dessa vez não criamos uma instância padrão `settings = Settings()`. |
||||
|
|
||||
|
### O arquivo principal da aplicação |
||||
|
|
||||
|
Agora criamos a dependência que retorna um novo objeto `config.Settings()`. |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="6 12-13" |
||||
|
{!> ../../../docs_src/settings/app02_an_py39/main.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="6 12-13" |
||||
|
{!> ../../../docs_src/settings/app02_an/main.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! dica |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="5 11-12" |
||||
|
{!> ../../../docs_src/settings/app02/main.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! dica |
||||
|
Vamos discutir sobre `@lru_cache` logo mais. |
||||
|
|
||||
|
Por enquanto, você pode considerar `get_settings()` como uma função normal. |
||||
|
|
||||
|
E então podemos declarar essas configurações como uma dependência na função de operação da rota e utilizar onde for necessário. |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="17 19-21" |
||||
|
{!> ../../../docs_src/settings/app02_an_py39/main.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="17 19-21" |
||||
|
{!> ../../../docs_src/settings/app02_an/main.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! dica |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="16 18-20" |
||||
|
{!> ../../../docs_src/settings/app02/main.py!} |
||||
|
``` |
||||
|
|
||||
|
### Configurações e testes |
||||
|
|
||||
|
Então seria muito fácil fornecer uma configuração diferente durante a execução dos testes sobrescrevendo a dependência de `get_settings`: |
||||
|
|
||||
|
```Python hl_lines="9-10 13 21" |
||||
|
{!../../../docs_src/settings/app02/test_main.py!} |
||||
|
``` |
||||
|
|
||||
|
Na sobrescrita da dependência, definimos um novo valor para `admin_email` quando instanciamos um novo objeto `Settings`, e então retornamos esse novo objeto. |
||||
|
|
||||
|
Após isso, podemos testar se o valor está sendo utilizado. |
||||
|
|
||||
|
## Lendo um arquivo `.env` |
||||
|
|
||||
|
Se você tiver muitas configurações que variem bastante, talvez em ambientes distintos, pode ser útil colocá-las em um arquivo e depois lê-las como se fossem variáveis de ambiente. |
||||
|
|
||||
|
Essa prática é tão comum que possui um nome, essas variáveis de ambiente normalmente são colocadas em um arquivo `.env`, e esse arquivo é chamado de "dotenv". |
||||
|
|
||||
|
!!! dica |
||||
|
Um arquivo iniciando com um ponto final (`.`) é um arquivo oculto em sistemas baseados em Unix, como Linux e MacOS. |
||||
|
|
||||
|
Mas um arquivo dotenv não precisa ter esse nome exato. |
||||
|
|
||||
|
Pydantic suporta a leitura desses tipos de arquivos utilizando uma biblioteca externa. Você pode ler mais em <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support" class="external-link" target="_blank">Pydantic Settings: Dotenv (.env) support</a>. |
||||
|
|
||||
|
!!! dica |
||||
|
Para que isso funcione você precisa executar `pip install python-dotenv`. |
||||
|
|
||||
|
### O arquivo `.env` |
||||
|
|
||||
|
Você pode definir um arquivo `.env` com o seguinte conteúdo: |
||||
|
|
||||
|
```bash |
||||
|
ADMIN_EMAIL="[email protected]" |
||||
|
APP_NAME="ChimichangApp" |
||||
|
``` |
||||
|
|
||||
|
### Obtendo configurações do `.env` |
||||
|
|
||||
|
E então adicionar o seguinte código em `config.py`: |
||||
|
|
||||
|
=== "Pydantic v2" |
||||
|
|
||||
|
```Python hl_lines="9" |
||||
|
{!> ../../../docs_src/settings/app03_an/config.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! dica |
||||
|
O atributo `model_config` é usado apenas para configuração do Pydantic. Você pode ler mais em <a href="https://docs.pydantic.dev/latest/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>. |
||||
|
|
||||
|
=== "Pydantic v1" |
||||
|
|
||||
|
```Python hl_lines="9-10" |
||||
|
{!> ../../../docs_src/settings/app03_an/config_pv1.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! dica |
||||
|
A classe `Config` é usada apenas para configuração do Pydantic. Você pode ler mais em <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>. |
||||
|
|
||||
|
!!! info |
||||
|
Na versão 1 do Pydantic a configuração é realizada por uma classe interna `Config`, na versão 2 do Pydantic isso é feito com o atributo `model_config`. Esse atributo recebe um `dict`, para utilizar o autocomplete e checagem de erros do seu editor de texto você pode importar e utilizar `SettingsConfigDict` para definir esse `dict`. |
||||
|
|
||||
|
Aqui definimos a configuração `env_file` dentro da classe `Settings` do Pydantic, e definimos o valor como o nome do arquivo dotenv que queremos utilizar. |
||||
|
|
||||
|
### Declarando `Settings` apenas uma vez com `lru_cache` |
||||
|
|
||||
|
Ler o conteúdo de um arquivo em disco normalmente é uma operação custosa (lenta), então você provavelmente quer fazer isso apenas um vez e reutilizar o mesmo objeto settings depois, em vez de ler os valores a cada requisição. |
||||
|
|
||||
|
Mas cada vez que fazemos: |
||||
|
|
||||
|
```Python |
||||
|
Settings() |
||||
|
``` |
||||
|
|
||||
|
um novo objeto `Settings` é instanciado, e durante a instanciação, o arquivo `.env` é lido novamente. |
||||
|
|
||||
|
Se a função da dependência fosse apenas: |
||||
|
|
||||
|
```Python |
||||
|
def get_settings(): |
||||
|
return Settings() |
||||
|
``` |
||||
|
|
||||
|
Iriamos criar um novo objeto a cada requisição, e estaríamos lendo o arquivo `.env` a cada requisição. ⚠️ |
||||
|
|
||||
|
Mas como estamos utilizando o decorador `@lru_cache` acima, o objeto `Settings` é criado apenas uma vez, na primeira vez que a função é chamada. ✔️ |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="1 11" |
||||
|
{!> ../../../docs_src/settings/app03_an_py39/main.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="1 11" |
||||
|
{!> ../../../docs_src/settings/app03_an/main.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! dica |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="1 10" |
||||
|
{!> ../../../docs_src/settings/app03/main.py!} |
||||
|
``` |
||||
|
|
||||
|
Dessa forma, todas as chamadas da função `get_settings()` nas dependências das próximas requisições, em vez de executar o código interno de `get_settings()` e instanciar um novo objeto `Settings`, irão retornar o mesmo objeto que foi retornado na primeira chamada, de novo e de novo. |
||||
|
|
||||
|
#### Detalhes Técnicos de `lru_cache` |
||||
|
|
||||
|
`@lru_cache` modifica a função decorada para retornar o mesmo valor que foi retornado na primeira vez, em vez de calculá-lo novamente, executando o código da função toda vez. |
||||
|
|
||||
|
Assim, a função abaixo do decorador é executada uma única vez para cada combinação dos argumentos passados. E os valores retornados para cada combinação de argumentos são sempre reutilizados para cada nova chamada da função com a mesma combinação de argumentos. |
||||
|
|
||||
|
Por exemplo, se você definir uma função: |
||||
|
|
||||
|
```Python |
||||
|
@lru_cache |
||||
|
def say_hi(name: str, salutation: str = "Ms."): |
||||
|
return f"Hello {salutation} {name}" |
||||
|
``` |
||||
|
|
||||
|
Seu programa poderia executar dessa forma: |
||||
|
|
||||
|
```mermaid |
||||
|
sequenceDiagram |
||||
|
|
||||
|
participant code as Código |
||||
|
participant function as say_hi() |
||||
|
participant execute as Executar Função |
||||
|
|
||||
|
rect rgba(0, 255, 0, .1) |
||||
|
code ->> function: say_hi(name="Camila") |
||||
|
function ->> execute: executar código da função |
||||
|
execute ->> code: retornar o resultado |
||||
|
end |
||||
|
|
||||
|
rect rgba(0, 255, 255, .1) |
||||
|
code ->> function: say_hi(name="Camila") |
||||
|
function ->> code: retornar resultado armazenado |
||||
|
end |
||||
|
|
||||
|
rect rgba(0, 255, 0, .1) |
||||
|
code ->> function: say_hi(name="Rick") |
||||
|
function ->> execute: executar código da função |
||||
|
execute ->> code: retornar o resultado |
||||
|
end |
||||
|
|
||||
|
rect rgba(0, 255, 0, .1) |
||||
|
code ->> function: say_hi(name="Rick", salutation="Mr.") |
||||
|
function ->> execute: executar código da função |
||||
|
execute ->> code: retornar o resultado |
||||
|
end |
||||
|
|
||||
|
rect rgba(0, 255, 255, .1) |
||||
|
code ->> function: say_hi(name="Rick") |
||||
|
function ->> code: retornar resultado armazenado |
||||
|
end |
||||
|
|
||||
|
rect rgba(0, 255, 255, .1) |
||||
|
code ->> function: say_hi(name="Camila") |
||||
|
function ->> code: retornar resultado armazenado |
||||
|
end |
||||
|
``` |
||||
|
|
||||
|
No caso da nossa dependência `get_settings()`, a função não recebe nenhum argumento, então ela sempre retorna o mesmo valor. |
||||
|
|
||||
|
Dessa forma, ela se comporta praticamente como uma variável global, mas ao ser utilizada como uma função de uma dependência, pode facilmente ser sobrescrita durante os testes. |
||||
|
|
||||
|
`@lru_cache` é definido no módulo `functools` que faz parte da biblioteca padrão do Python, você pode ler mais sobre esse decorador no link <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">Python Docs sobre `@lru_cache`</a>. |
||||
|
|
||||
|
## Recapitulando |
||||
|
|
||||
|
Você pode usar o módulo Pydantic Settings para gerenciar as configurações de sua aplicação, utilizando todo o poder dos modelos Pydantic. |
||||
|
|
||||
|
- Utilizar dependências simplifica os testes. |
||||
|
- Você pode utilizar arquivos .env junto das configurações do Pydantic. |
||||
|
- Utilizar o decorador `@lru_cache` evita que o arquivo .env seja lido de novo e de novo para cada requisição, enquanto permite que você sobrescreva durante os testes. |
@ -0,0 +1,11 @@ |
|||||
|
# Como Fazer - Exemplos Práticos |
||||
|
|
||||
|
Aqui você encontrará diferentes exemplos práticos ou tutoriais de "como fazer" para vários tópicos. |
||||
|
|
||||
|
A maioria dessas ideias será mais ou menos **independente**, e na maioria dos casos você só precisará estudá-las se elas se aplicarem diretamente ao **seu projeto**. |
||||
|
|
||||
|
Se algo parecer interessante e útil para o seu projeto, vá em frente e dê uma olhada. Caso contrário, você pode simplesmente ignorá-lo. |
||||
|
|
||||
|
!!! tip |
||||
|
|
||||
|
Se você deseja **aprender FastAPI** de forma estruturada (recomendado), leia capítulo por capítulo [Tutorial - Guia de Usuário](../tutorial/index.md){.internal-link target=_blank} em vez disso. |
@ -0,0 +1,497 @@ |
|||||
|
# Classes como Dependências |
||||
|
|
||||
|
Antes de nos aprofundarmos no sistema de **Injeção de Dependência**, vamos melhorar o exemplo anterior. |
||||
|
|
||||
|
## `dict` do exemplo anterior |
||||
|
|
||||
|
No exemplo anterior, nós retornávamos um `dict` da nossa dependência ("injetável"): |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="9" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="11" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="12" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="7" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="11" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
Mas assim obtemos um `dict` como valor do parâmetro `commons` na *função de operação de rota*. |
||||
|
|
||||
|
E sabemos que editores de texto não têm como oferecer muitas funcionalidades (como sugestões automáticas) para objetos do tipo `dict`, por que não há como eles saberem o tipo das chaves e dos valores. |
||||
|
|
||||
|
Podemos fazer melhor... |
||||
|
|
||||
|
## O que caracteriza uma dependência |
||||
|
|
||||
|
Até agora você apenas viu dependências declaradas como funções. |
||||
|
|
||||
|
Mas essa não é a única forma de declarar dependências (mesmo que provavelmente seja a mais comum). |
||||
|
|
||||
|
O fator principal para uma dependência é que ela deve ser "chamável" |
||||
|
|
||||
|
Um objeto "chamável" em Python é qualquer coisa que o Python possa "chamar" como uma função |
||||
|
|
||||
|
Então se você tiver um objeto `alguma_coisa` (que pode *não* ser uma função) que você possa "chamar" (executá-lo) dessa maneira: |
||||
|
|
||||
|
```Python |
||||
|
something() |
||||
|
``` |
||||
|
|
||||
|
ou |
||||
|
|
||||
|
```Python |
||||
|
something(some_argument, some_keyword_argument="foo") |
||||
|
``` |
||||
|
|
||||
|
Então esse objeto é um "chamável". |
||||
|
|
||||
|
## Classes como dependências |
||||
|
|
||||
|
Você deve ter percebido que para criar um instância de uma classe em Python, a mesma sintaxe é utilizada. |
||||
|
|
||||
|
Por exemplo: |
||||
|
|
||||
|
```Python |
||||
|
class Cat: |
||||
|
def __init__(self, name: str): |
||||
|
self.name = name |
||||
|
|
||||
|
|
||||
|
fluffy = Cat(name="Mr Fluffy") |
||||
|
``` |
||||
|
|
||||
|
Nesse caso, `fluffy` é uma instância da classe `Cat`. |
||||
|
|
||||
|
E para criar `fluffy`, você está "chamando" `Cat`. |
||||
|
|
||||
|
Então, uma classe Python também é "chamável". |
||||
|
|
||||
|
Então, no **FastAPI**, você pode utilizar uma classe Python como uma dependência. |
||||
|
|
||||
|
O que o FastAPI realmente verifica, é se a dependência é algo chamável (função, classe, ou outra coisa) e os parâmetros que foram definidos. |
||||
|
|
||||
|
Se você passar algo "chamável" como uma dependência do **FastAPI**, o framework irá analisar os parâmetros desse "chamável" e processá-los da mesma forma que os parâmetros de uma *função de operação de rota*. Incluindo as sub-dependências. |
||||
|
|
||||
|
Isso também se aplica a objetos chamáveis que não recebem nenhum parâmetro. Da mesma forma que uma *função de operação de rota* sem parâmetros. |
||||
|
|
||||
|
Então, podemos mudar o "injetável" na dependência `common_parameters` acima para a classe `CommonQueryParams`: |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="11-15" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="11-15" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="12-16" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="9-13" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="11-15" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002.py!} |
||||
|
``` |
||||
|
|
||||
|
Observe o método `__init__` usado para criar uma instância da classe: |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="12" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="12" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="13" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="10" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="12" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002.py!} |
||||
|
``` |
||||
|
|
||||
|
...ele possui os mesmos parâmetros que nosso `common_parameters` anterior: |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="8" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="9" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="10" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="6" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="9" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
Esses parâmetros são utilizados pelo **FastAPI** para "definir" a dependência. |
||||
|
|
||||
|
Em ambos os casos teremos: |
||||
|
|
||||
|
* Um parâmetro de consulta `q` opcional do tipo `str`. |
||||
|
* Um parâmetro de consulta `skip` do tipo `int`, com valor padrão `0`. |
||||
|
* Um parâmetro de consulta `limit` do tipo `int`, com valor padrão `100`. |
||||
|
|
||||
|
Os dados serão convertidos, validados, documentados no esquema da OpenAPI e etc nos dois casos. |
||||
|
|
||||
|
## Utilizando |
||||
|
|
||||
|
Agora você pode declarar sua dependência utilizando essa classe. |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="19" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="19" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="20" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="17" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="19" |
||||
|
{!> ../../../docs_src/dependencies/tutorial002.py!} |
||||
|
``` |
||||
|
|
||||
|
O **FastAPI** chama a classe `CommonQueryParams`. Isso cria uma "instância" dessa classe e é a instância que será passada para o parâmetro `commons` na sua função. |
||||
|
|
||||
|
## Anotações de Tipo vs `Depends` |
||||
|
|
||||
|
Perceba como escrevemos `CommonQueryParams` duas vezes no código abaixo: |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python |
||||
|
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python |
||||
|
commons: CommonQueryParams = Depends(CommonQueryParams) |
||||
|
``` |
||||
|
|
||||
|
O último `CommonQueryParams`, em: |
||||
|
|
||||
|
```Python |
||||
|
... Depends(CommonQueryParams) |
||||
|
``` |
||||
|
|
||||
|
...é o que o **FastAPI** irá realmente usar para saber qual é a dependência. |
||||
|
|
||||
|
É a partir dele que o FastAPI irá extrair os parâmetros passados e será o que o FastAPI irá realmente chamar. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
Nesse caso, o primeiro `CommonQueryParams`, em: |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python |
||||
|
commons: Annotated[CommonQueryParams, ... |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python |
||||
|
commons: CommonQueryParams ... |
||||
|
``` |
||||
|
|
||||
|
...não tem nenhum signficado especial para o **FastAPI**. O FastAPI não irá utilizá-lo para conversão dos dados, validação, etc (já que ele utiliza `Depends(CommonQueryParams)` para isso). |
||||
|
|
||||
|
Na verdade você poderia escrever apenas: |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python |
||||
|
commons: Annotated[Any, Depends(CommonQueryParams)] |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python |
||||
|
commons = Depends(CommonQueryParams) |
||||
|
``` |
||||
|
|
||||
|
...como em: |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="19" |
||||
|
{!> ../../../docs_src/dependencies/tutorial003_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="19" |
||||
|
{!> ../../../docs_src/dependencies/tutorial003_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="20" |
||||
|
{!> ../../../docs_src/dependencies/tutorial003_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="17" |
||||
|
{!> ../../../docs_src/dependencies/tutorial003_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="19" |
||||
|
{!> ../../../docs_src/dependencies/tutorial003.py!} |
||||
|
``` |
||||
|
|
||||
|
Mas declarar o tipo é encorajado por que é a forma que o seu editor de texto sabe o que será passado como valor do parâmetro `commons`. |
||||
|
|
||||
|
<img src="/img/tutorial/dependencies/image02.png"> |
||||
|
|
||||
|
## Pegando um Atalho |
||||
|
|
||||
|
Mas você pode ver que temos uma repetição do código neste exemplo, escrevendo `CommonQueryParams` duas vezes: |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python |
||||
|
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python |
||||
|
commons: CommonQueryParams = Depends(CommonQueryParams) |
||||
|
``` |
||||
|
|
||||
|
O **FastAPI** nos fornece um atalho para esses casos, onde a dependência é *especificamente* uma classe que o **FastAPI** irá "chamar" para criar uma instância da própria classe. |
||||
|
|
||||
|
Para esses casos específicos, você pode fazer o seguinte: |
||||
|
|
||||
|
Em vez de escrever: |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python |
||||
|
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python |
||||
|
commons: CommonQueryParams = Depends(CommonQueryParams) |
||||
|
``` |
||||
|
|
||||
|
...escreva: |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python |
||||
|
commons: Annotated[CommonQueryParams, Depends()] |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8 non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python |
||||
|
commons: CommonQueryParams = Depends() |
||||
|
``` |
||||
|
|
||||
|
Você declara a dependência como o tipo do parâmetro, e utiliza `Depends()` sem nenhum parâmetro, em vez de ter que escrever a classe *novamente* dentro de `Depends(CommonQueryParams)`. |
||||
|
|
||||
|
O mesmo exemplo ficaria então dessa forma: |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="19" |
||||
|
{!> ../../../docs_src/dependencies/tutorial004_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="19" |
||||
|
{!> ../../../docs_src/dependencies/tutorial004_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="20" |
||||
|
{!> ../../../docs_src/dependencies/tutorial004_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="17" |
||||
|
{!> ../../../docs_src/dependencies/tutorial004_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="19" |
||||
|
{!> ../../../docs_src/dependencies/tutorial004.py!} |
||||
|
``` |
||||
|
|
||||
|
...e o **FastAPI** saberá o que fazer. |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Se isso parece mais confuso do que útil, não utilize, você não *precisa* disso. |
||||
|
|
||||
|
É apenas um atalho. Por que o **FastAPI** se preocupa em ajudar a minimizar a repetição de código. |
@ -0,0 +1,353 @@ |
|||||
|
# Dependências |
||||
|
|
||||
|
O **FastAPI** possui um poderoso, mas intuitivo sistema de **<abbr title="também conhecidos como, recursos, provedores, serviços, injetáveis">Injeção de Dependência</abbr>**. |
||||
|
|
||||
|
Esse sistema foi pensado para ser fácil de usar, e permitir que qualquer desenvolvedor possa integrar facilmente outros componentes ao **FastAPI**. |
||||
|
|
||||
|
## O que é "Injeção de Dependência" |
||||
|
|
||||
|
**"Injeção de Dependência"** no mundo da programação significa, que existe uma maneira de declarar no seu código (nesse caso, suas *funções de operação de rota*) para declarar as coisas que ele precisa para funcionar e que serão utilizadas: "dependências". |
||||
|
|
||||
|
Então, esse sistema (nesse caso o **FastAPI**) se encarrega de fazer o que for preciso para fornecer essas dependências para o código ("injetando" as dependências). |
||||
|
|
||||
|
Isso é bastante útil quando você precisa: |
||||
|
|
||||
|
* Definir uma lógica compartilhada (mesmo formato de código repetidamente). |
||||
|
* Compartilhar conexões com banco de dados. |
||||
|
* Aplicar regras de segurança, autenticação, papéis de usuários, etc. |
||||
|
* E muitas outras coisas... |
||||
|
|
||||
|
Tudo isso, enquanto minimizamos a repetição de código. |
||||
|
|
||||
|
## Primeiros passos |
||||
|
|
||||
|
Vamos ver um exemplo simples. Tão simples que não será muito útil, por enquanto. |
||||
|
|
||||
|
Mas dessa forma podemos focar em como o sistema de **Injeção de Dependência** funciona. |
||||
|
|
||||
|
### Criando uma dependência, ou "injetável" |
||||
|
|
||||
|
Primeiro vamos focar na dependência. |
||||
|
|
||||
|
Ela é apenas uma função que pode receber os mesmos parâmetros de uma *função de operação de rota*: |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="8-9" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="8-11" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="9-12" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="6-7" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="8-11" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
E pronto. |
||||
|
|
||||
|
**2 linhas**. |
||||
|
|
||||
|
E com a mesma forma e estrutura de todas as suas *funções de operação de rota*. |
||||
|
|
||||
|
Você pode pensar nela como uma *função de operação de rota* sem o "decorador" (sem a linha `@app.get("/some-path")`). |
||||
|
|
||||
|
E com qualquer retorno que você desejar. |
||||
|
|
||||
|
Neste caso, a dependência espera por: |
||||
|
|
||||
|
* Um parâmetro de consulta opcional `q` do tipo `str`. |
||||
|
* Um parâmetro de consulta opcional `skip` do tipo `int`, e igual a `0` por padrão. |
||||
|
* Um parâmetro de consulta opcional `limit` do tipo `int`, e igual a `100` por padrão. |
||||
|
|
||||
|
E então retorna um `dict` contendo esses valores. |
||||
|
|
||||
|
!!! info "Informação" |
||||
|
FastAPI passou a suportar a notação `Annotated` (e começou a recomendá-la) na versão 0.95.0. |
||||
|
|
||||
|
Se você utiliza uma versão anterior, ocorrerão erros ao tentar utilizar `Annotated`. |
||||
|
|
||||
|
Certifique-se de [Atualizar a versão do FastAPI](../../deployment/versions.md#atualizando-as-versoes-do-fastapi){.internal-link target=_blank} para pelo menos 0.95.1 antes de usar `Annotated`. |
||||
|
|
||||
|
### Importando `Depends` |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="3" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="3" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="3" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="1" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="3" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
### Declarando a dependência, no "dependente" |
||||
|
|
||||
|
Da mesma forma que você utiliza `Body`, `Query`, etc. Como parâmetros de sua *função de operação de rota*, utilize `Depends` com um novo parâmetro: |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="13 18" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="15 20" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="16 21" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.10+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="11 16" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ non-Annotated" |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Utilize a versão com `Annotated` se possível. |
||||
|
|
||||
|
```Python hl_lines="15 20" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
Ainda que `Depends` seja utilizado nos parâmetros da função da mesma forma que `Body`, `Query`, etc, `Depends` funciona de uma forma um pouco diferente. |
||||
|
|
||||
|
Você fornece um único parâmetro para `Depends`. |
||||
|
|
||||
|
Esse parâmetro deve ser algo como uma função. |
||||
|
|
||||
|
Você **não chama a função** diretamente (não adicione os parênteses no final), apenas a passe como parâmetro de `Depends()`. |
||||
|
|
||||
|
E essa função vai receber os parâmetros da mesma forma que uma *função de operação de rota*. |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Você verá quais outras "coisas", além de funções, podem ser usadas como dependências no próximo capítulo. |
||||
|
|
||||
|
Sempre que uma nova requisição for realizada, o **FastAPI** se encarrega de: |
||||
|
|
||||
|
* Chamar sua dependência ("injetável") com os parâmetros corretos. |
||||
|
* Obter o resultado da função. |
||||
|
* Atribuir esse resultado para o parâmetro em sua *função de operação de rota*. |
||||
|
|
||||
|
```mermaid |
||||
|
graph TB |
||||
|
|
||||
|
common_parameters(["common_parameters"]) |
||||
|
read_items["/items/"] |
||||
|
read_users["/users/"] |
||||
|
|
||||
|
common_parameters --> read_items |
||||
|
common_parameters --> read_users |
||||
|
``` |
||||
|
|
||||
|
Assim, você escreve um código compartilhado apenas uma vez e o **FastAPI** se encarrega de chamá-lo em suas *operações de rota*. |
||||
|
|
||||
|
!!! check "Checando" |
||||
|
Perceba que você não precisa criar uma classe especial e enviar a dependência para algum outro lugar em que o **FastAPI** a "registre" ou realize qualquer operação similar. |
||||
|
|
||||
|
Você apenas envia para `Depends` e o **FastAPI** sabe como fazer o resto. |
||||
|
|
||||
|
## Compartilhando dependências `Annotated` |
||||
|
|
||||
|
Nos exemplos acima, você pode ver que existe uma pequena **duplicação de código**. |
||||
|
|
||||
|
Quando você precisa utilizar a dependência `common_parameters()`, você precisa escrever o parâmetro inteiro com uma anotação de tipo e `Depends()`: |
||||
|
|
||||
|
```Python |
||||
|
commons: Annotated[dict, Depends(common_parameters)] |
||||
|
``` |
||||
|
|
||||
|
Mas como estamos utilizando `Annotated`, podemos guardar esse valor `Annotated` em uma variável e utilizá-la em múltiplos locais: |
||||
|
|
||||
|
=== "Python 3.10+" |
||||
|
|
||||
|
```Python hl_lines="12 16 21" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_02_an_py310.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="14 18 23" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_02_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="15 19 24" |
||||
|
{!> ../../../docs_src/dependencies/tutorial001_02_an.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! tip "Dica" |
||||
|
Isso é apenas Python padrão, essa funcionalidade é chamada de "type alias", e na verdade não é específica ao **FastAPI**. |
||||
|
|
||||
|
Mas como o **FastAPI** se baseia em convenções do Python, incluindo `Annotated`, você pode incluir esse truque no seu código. 😎 |
||||
|
|
||||
|
As dependências continuarão funcionando como esperado, e a **melhor parte** é que a **informação sobre o tipo é preservada**, o que signfica que seu editor de texto ainda irá incluir **preenchimento automático**, **visualização de erros**, etc. O mesmo vale para ferramentas como `mypy`. |
||||
|
|
||||
|
Isso é especialmente útil para uma **base de código grande** onde **as mesmas dependências** são utilizadas repetidamente em **muitas *operações de rota***. |
||||
|
|
||||
|
## `Async` ou não, eis a questão |
||||
|
|
||||
|
Como as dependências também serão chamadas pelo **FastAPI** (da mesma forma que *funções de operação de rota*), as mesmas regras se aplicam ao definir suas funções. |
||||
|
|
||||
|
Você pode utilizar `async def` ou apenas `def`. |
||||
|
|
||||
|
E você pode declarar dependências utilizando `async def` dentro de *funções de operação de rota* definidas com `def`, ou declarar dependências com `def` e utilizar dentro de *funções de operação de rota* definidas com `async def`, etc. |
||||
|
|
||||
|
Não faz diferença. O **FastAPI** sabe o que fazer. |
||||
|
|
||||
|
!!! note "Nota" |
||||
|
Caso você não conheça, veja em [Async: *"Com Pressa?"*](../../async.md#com-pressa){.internal-link target=_blank} a sessão acerca de `async` e `await` na documentação. |
||||
|
|
||||
|
## Integrando com OpenAPI |
||||
|
|
||||
|
Todas as declarações de requisições, validações e requisitos para suas dependências (e sub-dependências) serão integradas em um mesmo esquema OpenAPI. |
||||
|
|
||||
|
Então, a documentação interativa também terá toda a informação sobre essas dependências: |
||||
|
|
||||
|
<img src="/img/tutorial/dependencies/image01.png"> |
||||
|
|
||||
|
## Caso de Uso Simples |
||||
|
|
||||
|
Se você parar para ver, *funções de operação de rota* são declaradas para serem usadas sempre que uma *rota* e uma *operação* se encaixam, e então o **FastAPI** se encarrega de chamar a função correspondente com os argumentos corretos, extraindo os dados da requisição. |
||||
|
|
||||
|
Na verdade, todos (ou a maioria) dos frameworks web funcionam da mesma forma. |
||||
|
|
||||
|
Você nunca chama essas funções diretamente. Elas são chamadas pelo framework utilizado (nesse caso, **FastAPI**). |
||||
|
|
||||
|
Com o Sistema de Injeção de Dependência, você também pode informar ao **FastAPI** que sua *função de operação de rota* também "depende" em algo a mais que deve ser executado antes de sua *função de operação de rota*, e o **FastAPI** se encarrega de executar e "injetar" os resultados. |
||||
|
|
||||
|
Outros termos comuns para essa mesma ideia de "injeção de dependência" são: |
||||
|
|
||||
|
* recursos |
||||
|
* provedores |
||||
|
* serviços |
||||
|
* injetáveis |
||||
|
* componentes |
||||
|
|
||||
|
## Plug-ins em **FastAPI** |
||||
|
|
||||
|
Integrações e "plug-ins" podem ser construídos com o sistema de **Injeção de Dependência**. Mas na verdade, **não há necessidade de criar "plug-ins"**, já que utilizando dependências é possível declarar um número infinito de integrações e interações que se tornam disponíveis para as suas *funções de operação de rota*. |
||||
|
|
||||
|
E as dependências pode ser criadas de uma forma bastante simples e intuitiva que permite que você importe apenas os pacotes Python que forem necessários, e integrá-los com as funções de sua API em algumas linhas de código, *literalmente*. |
||||
|
|
||||
|
Você verá exemplos disso nos próximos capítulos, acerca de bancos de dados relacionais e NoSQL, segurança, etc. |
||||
|
|
||||
|
## Compatibilidade do **FastAPI** |
||||
|
|
||||
|
A simplicidade do sistema de injeção de dependência do **FastAPI** faz ele compatível com: |
||||
|
|
||||
|
* todos os bancos de dados relacionais |
||||
|
* bancos de dados NoSQL |
||||
|
* pacotes externos |
||||
|
* APIs externas |
||||
|
* sistemas de autenticação e autorização |
||||
|
* istemas de monitoramento de uso para APIs |
||||
|
* sistemas de injeção de dados de resposta |
||||
|
* etc. |
||||
|
|
||||
|
## Simples e Poderoso |
||||
|
|
||||
|
Mesmo que o sistema hierárquico de injeção de dependência seja simples de definir e utilizar, ele ainda é bastante poderoso. |
||||
|
|
||||
|
Você pode definir dependências que por sua vez definem suas próprias dependências. |
||||
|
|
||||
|
No fim, uma árvore hierárquica de dependências é criadas, e o sistema de **Injeção de Dependência** toma conta de resolver todas essas dependências (e as sub-dependências delas) para você, e provê (injeta) os resultados em cada passo. |
||||
|
|
||||
|
Por exemplo, vamos supor que você possua 4 endpoints na sua API (*operações de rota*): |
||||
|
|
||||
|
* `/items/public/` |
||||
|
* `/items/private/` |
||||
|
* `/users/{user_id}/activate` |
||||
|
* `/items/pro/` |
||||
|
|
||||
|
Você poderia adicionar diferentes requisitos de permissão para cada um deles utilizando apenas dependências e sub-dependências: |
||||
|
|
||||
|
```mermaid |
||||
|
graph TB |
||||
|
|
||||
|
current_user(["current_user"]) |
||||
|
active_user(["active_user"]) |
||||
|
admin_user(["admin_user"]) |
||||
|
paying_user(["paying_user"]) |
||||
|
|
||||
|
public["/items/public/"] |
||||
|
private["/items/private/"] |
||||
|
activate_user["/users/{user_id}/activate"] |
||||
|
pro_items["/items/pro/"] |
||||
|
|
||||
|
current_user --> active_user |
||||
|
active_user --> admin_user |
||||
|
active_user --> paying_user |
||||
|
|
||||
|
current_user --> public |
||||
|
active_user --> private |
||||
|
admin_user --> activate_user |
||||
|
paying_user --> pro_items |
||||
|
``` |
||||
|
|
||||
|
## Integração com **OpenAPI** |
||||
|
|
||||
|
Todas essas dependências, ao declarar os requisitos para suas *operações de rota*, também adicionam parâmetros, validações, etc. |
||||
|
|
||||
|
O **FastAPI** se encarrega de adicionar tudo isso ao esquema OpenAPI, para que seja mostrado nos sistemas de documentação interativa. |
Loading…
Reference in new issue