Simplifique suas configurações do Django com dicas de tipo: um tutorial do Pydantic
Publicados: 2022-07-22Projetos Django costumavam me frustrar porque eu não tinha uma maneira robusta e escalável de adicionar novos ambientes. Ao reunir dicas do tipo pydantic e Python, construí a base poderosa de que precisava.
Conforme descrito no PEP 484, as dicas de tipo oferecem suporte à análise estática, mas essas mesmas anotações também estão disponíveis em tempo de execução. Pacotes de terceiros, como pydantic, oferecem verificação de tipo de tempo de execução que usa esses metadados adicionais. Pydantic usa dicas de tipo Python para ajudar a gerenciar metadados de configurações e realizar validação de dados de tempo de execução.
Este tutorial pydantic mostrará os efeitos positivos e de longo alcance do uso do gerenciamento de configurações pydantic com o Django.
Nossa configuração segue as melhores práticas descritas no site do aplicativo Twelve-Factor:
- Defina configurações não constantes e secretas como variáveis de ambiente.
- Em ambientes de desenvolvimento, defina as variáveis de ambiente em um arquivo
.env
e inclua o.env
em.gitignore
. - Use os mecanismos do provedor de nuvem para definir variáveis de ambiente (secretas) para os ambientes de controle de qualidade, preparação e produção.
- Use um único arquivo
settings.py
que se configura a partir das variáveis de ambiente. - Use pydantic para ler, verificar, validar e converter variáveis de ambiente em variáveis Python que definem as configurações do Django.
Como alternativa, alguns desenvolvedores criam vários arquivos de configurações, como settings_dev.py
e settings_prod.py
. Infelizmente, essa abordagem não escala bem. Isso leva à duplicação de código, confusão, bugs difíceis de encontrar e maiores esforços de manutenção.
Usando as melhores práticas mencionadas, adicionar qualquer número de ambientes é fácil, bem definido e à prova de erros. Embora possamos explorar uma configuração de ambiente mais complicada, vamos nos concentrar em duas para maior clareza: desenvolvimento e produção.
Como é isso na prática?
Gerenciamento de configurações do Pydantic e variáveis de ambiente
Agora nos concentramos em um exemplo em desenvolvimento e produção. Mostramos como cada ambiente configura suas configurações de forma diferente e como o pydantic suporta cada um.
Nosso aplicativo de exemplo requer um banco de dados compatível com Django, então precisamos armazenar a string de conexão do banco de dados. Movemos as informações de configuração de conexão do banco de dados para uma variável de ambiente, DATABASE_URL
, usando o pacote Python dj-database-url. Observe que esta variável é do tipo str
e está formatada da seguinte forma:
postgres://{user}:{password}@{hostname}:{port}/{database-name} mysql://{user}:{password}@{hostname}:{port}/{database-name} oracle://{user}:{password}@{hostname}:{port}/{database-name} sqlite:///PATH
Em nosso ambiente de desenvolvimento, podemos usar uma instância do PostgreSQL contida no Docker para facilitar o uso, enquanto em nosso ambiente de produção, apontaremos para um serviço de banco de dados provisionado.
Outra variável que queremos definir é um booleano, DEBUG
. O sinalizador DEBUG no Django nunca deve ser ativado em uma implantação de produção. Destina-se a obter feedback adicional durante o desenvolvimento. Por exemplo, no modo de depuração, o Django exibirá páginas de erro detalhadas quando ocorrer uma exceção.
Valores diferentes para desenvolvimento e produção podem ser definidos da seguinte forma:
Nome variável | Desenvolvimento | Produção |
---|---|---|
DATABASE_URL | postgres://postgres:mypw@localhost:5432/mydb | postgres://foo1:foo2@foo3:5432/foo4 |
DEBUG | True | False |
Usamos o módulo de gerenciamento de configurações pydantic para gerenciar esses diferentes conjuntos de valores de variáveis de ambiente, dependendo do ambiente.
Etapas Preparatórias
Para colocar isso em prática, começamos a configurar nosso ambiente de desenvolvimento criando nosso único arquivo .env
com este conteúdo:
DATABASE_URL=postgres://postgres:mypw@localhost:5432/mydb DEBUG=True
Em seguida, adicionamos o arquivo .env
ao arquivo .gitignore
do projeto. O arquivo .gitignore
evita salvar informações potencialmente confidenciais no controle de origem.
Enquanto essa abordagem funciona bem em nosso ambiente de desenvolvimento, nossa especificação de ambiente de produção usa um mecanismo diferente. Nossas práticas recomendadas determinam que as variáveis de ambiente de produção usem segredos de ambiente. Por exemplo, no Heroku, esses segredos são chamados de Config Vars e são configurados por meio do Heroku Dashboard. Eles são disponibilizados para o aplicativo implantado como variáveis de ambiente:
Depois disso, precisamos ajustar a configuração do aplicativo para ler esses valores de qualquer ambiente automaticamente.

Configurando o settings.py
do Django
Vamos começar com um novo projeto Django para fornecer a estrutura essencial para o nosso exemplo. Nós montamos um novo projeto Django com o seguinte comando de terminal:
$ django-admin startproject mysite
Agora temos uma estrutura de projeto básica para o projeto mysite
. A estrutura de arquivos do projeto é a seguinte:
mysite/ manage.py mysite/ __init__.py settings.py urls.py asgi.py wsgi.py
O arquivo settings.py
contém código clichê que nos permite gerenciar a configuração do aplicativo. Ele tem muitas configurações padrão predefinidas que devemos ajustar ao ambiente relevante.
Para gerenciar essas configurações do aplicativo usando variáveis de ambiente e pydantic, adicione este código ao topo do arquivo settings.py
:
import os from pathlib import Path from pydantic import ( BaseSettings, PostgresDsn, EmailStr, HttpUrl, ) import dj_database_url # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent class SettingsFromEnvironment(BaseSettings): """Defines environment variables with their types and optional defaults""" DATABASE_URL: PostgresDsn DEBUG: bool = False class Config: """Defines configuration for pydantic environment loading""" env_file = str(BASE_DIR / ".env") case_sensitive = True config = SettingsFromEnvironment() os.environ["DATABASE_URL"] = config.DATABASE_URL DATABASES = { "default": dj_database_url.config(conn_max_age=600, ssl_require=True) } DEBUG = config.DEBUG
Este código faz o seguinte:
- Define uma classe
SettingsFromEnvironment
, herdando da classe BaseSettings doBaseSettings
. - Define
DATABASE_URL
eDEBUG
, definindo seu tipo e padrão opcional usando dicas de tipo Python. - Define uma classe
Config
dizendo ao pydantic para procurar as variáveis em um arquivo.env
se não estiver presente nas variáveis de ambiente do sistema. - Instancia a classe
Config
naconfig
do objeto; as variáveis desejadas ficam disponíveis comoconfig.DATABASE_URL
econfig.DEBUG
. - Define as variáveis regulares do Django
DATABASES
eDEBUG
desses membros deconfig
.
O mesmo código é executado em todos os ambientes e o pydantic cuida do seguinte:
- Ele procura as variáveis de ambiente
DATABASE_URL
eDEBUG
.- Se definido como variáveis de ambiente, como na produção, ele as usará.
- Caso contrário, ele extrai esses valores do arquivo
.env
. - Se não encontrar um valor, ele fará o seguinte:
- Para
DATABASE_URL
, ele gera um erro. - Para
DEBUG
, ele atribui um valor padrão deFalse
.
- Para
- Se encontrar uma variável de ambiente, ele verificará os tipos de campo e dará um erro se algum deles estiver errado:
- Para
DATABASE_URL
, ele verifica se seu tipo de campo é uma URL no estiloPostgresDsn
. - Para
DEBUG
, ele verifica se seu tipo de campo é um booleano pydantic válido e não estrito.
- Para
Observe a configuração explícita da variável de ambiente do sistema operacional do valor de configuração para DATABASE_URL
. Pode parecer redundante definir os.environ["DATABASE_URL"] = config.DATABASE_URL
porque DATABASE_URL
já está definido como uma variável de ambiente externa. No entanto, isso permite que o pydantic analise, verifique e valide essa variável. Se a variável de ambiente DATABASE_URL
estiver ausente ou formatada incorretamente, pydantic fornecerá uma mensagem de erro clara. Essas verificações de erros são inestimáveis à medida que o aplicativo passa do desenvolvimento para os ambientes subsequentes.
Se uma variável não for definida, um padrão será atribuído ou um erro solicitará que ela seja definida. Qualquer prompt gerado também detalha o tipo de variável desejado. Um benefício colateral dessas verificações é que novos membros da equipe e engenheiros de DevOps descobrem mais facilmente quais variáveis precisam ser definidas. Isso evita os problemas difíceis de encontrar que resultam quando o aplicativo é executado sem todas as variáveis definidas.
É isso. O aplicativo agora tem uma implementação de gerenciamento de configurações sustentável usando uma única versão de settings.py
. A beleza dessa abordagem é que ela nos permite especificar as variáveis de ambiente corretas em um arquivo .env
ou qualquer outro meio desejado disponível em nosso ambiente de hospedagem.
O caminho escalável
Eu tenho usado o gerenciamento de configurações do Django com digitação em tempo de execução pydantic em todos os meus projetos Django. Descobri que isso leva a bases de código menores e mais fáceis de manter. Ele também fornece uma abordagem bem estruturada, autodocumentada e escalável para adicionar novos ambientes.
O próximo artigo desta série é um tutorial passo a passo sobre como construir um aplicativo Django do zero, com gerenciamento de configurações pydantic e implantá-lo no Heroku.
O Toptal Engineering Blog agradece a Stephen Davidson por revisar os exemplos de código apresentados neste artigo.
Uma versão anterior deste artigo enfatizou uma versão específica do Python. Como o Python oferece suporte a dicas de tipo há vários anos, generalizamos o texto introdutório removendo esta referência de versão.