7.3 KiB
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"):
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
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:
something()
ou
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:
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:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Observe o método __init__ usado para criar uma instância da classe:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...ele possui os mesmos parâmetros que nosso common_parameters anterior:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Esses parâmetros são utilizados pelo FastAPI para "definir" a dependência.
Em ambos os casos teremos:
- Um parâmetro de consulta
qopcional do tipostr. - Um parâmetro de consulta
skipdo tipoint, com valor padrão0. - Um parâmetro de consulta
limitdo tipoint, com valor padrão100.
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.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
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:
//// tab | Python 3.9+
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Utilize a versão com Annotated se possível.
///
commons: CommonQueryParams = Depends(CommonQueryParams)
////
O último CommonQueryParams, em:
... 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:
//// tab | Python 3.9+
commons: Annotated[CommonQueryParams, ...
////
//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Utilize a versão com Annotated se possível.
///
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:
//// tab | Python 3.9+
commons: Annotated[Any, Depends(CommonQueryParams)]
////
//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Utilize a versão com Annotated se possível.
///
commons = Depends(CommonQueryParams)
////
...como em:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
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, e assim ele pode ajudar com preenchimento automático, verificações de tipo, etc:
Pegando um Atalho
Mas você pode ver que temos uma repetição do código neste exemplo, escrevendo CommonQueryParams duas vezes:
//// tab | Python 3.9+
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Utilize a versão com Annotated se possível.
///
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:
//// tab | Python 3.9+
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Utilize a versão com Annotated se possível.
///
commons: CommonQueryParams = Depends(CommonQueryParams)
////
...escreva:
//// tab | Python 3.9+
commons: Annotated[CommonQueryParams, Depends()]
////
//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Utilize a versão com Annotated se possível.
///
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:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...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.
///