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.
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 variável de ambiente (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:
fast →💬 Você pode criar uma env var MY_NAME usandoexport MY_NAME="Wade Wilson" 💬 E utilizá-la em outros programas, comoecho "Hello $MY_NAME" Hello Wade Wilson
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:
importosname=os.getenv("MY_NAME","World")print(f"Hello {name} from Python")
Dica
O segundo parâmetro em os.getenv() é 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:
fast →💬 Aqui ainda não definimos a env varpython main.py 💬 Por isso obtemos o valor padrão Hello World from Python
💬 Mas se definirmos uma variável de ambiente primeiroexport MY_NAME="Wade Wilson" 💬 E executarmos o programa novamentepython main.py 💬 Agora ele pode ler a variável de ambiente Hello Wade Wilson from Python
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:
fast →💬 Criando uma env var MY_NAME na mesma linha da execução do programaMY_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 issopython main.py Hello World from Python
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.
Por sorte, o Pydantic possui uma funcionalidade para lidar com essas configurações vindas de variáveis de ambiente utilizando Pydantic: Settings management.
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.
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().
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).
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:
fast →ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
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.
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.
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.
A classe Config é usada apenas para configuração do Pydantic. Você pode ler mais em Pydantic Model Config.
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.
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:
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:
defget_settings():returnSettings()
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. ✔️
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.
@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.
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 Python Docs sobre @lru_cache.
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.