Django e settings.py: dicas e boas práticas

Em vez de ficar rasgando elogios ao Django, como de costume, eu tentarei aproveitar o melhor o meu (e o seu) tempo e mostrar algo que é de fato útil. O assunto em questão envolve o módulo settings.py, cuja função é fornecer as configurações necessárias para que o projeto funcione. Apesar deste módulo ser uma peça importante em qualquer projeto Django, pouco se fala sobre ele, ou sobre boas práticas associadas a ele. Por isso, achei que seria interessante mostrar a minha visão pessoal sobre o assunto.

O poder do Python

Quem não está acostumado a trabalhar com Django pode estranhar o fato deste utilizar um script Python para fazer a configuração do projeto: o famoso settings.py. E essa estranheza é justificada, afinal muitos desenvolvedores — principalmente os que vêm de outras linguagens — estão acostumados a trabalhar com ferramentas onde tal tarefa é feita com arquivos XML ou coisa assim.

O fato é que utilizar um script Python para tal é uma excelente idéia, pois você deixa de depender de algum tipo de estrutura estática (e muitas vezes inconveniente) para algo mais flexível e poderoso, que, no caso, é a própria linguagem de programação. Isso faz uma grande diferença em situações onde tal flexibilidade é necessária.

Quer exemplos?

Ambientes de desenvolvimento/produção

Eis como fizemos aqui. Primeiramente, defina a variável de ambiente WORKSPACE. No Linux, isso pode ser feito através do arquivo ~/.bashrc:

export WORKSPACE=1

A idéia é possibilitar a criação de perfis de configuração, cada qual correspondente a um ambiente de execução. A existência ou não da variável de ambiente é o que define o ambiente no qual a aplicação roda.

Continuando com o exemplo, no diretório raíz do seu projeto Django, crie o módulo environment.py, com o seguinte conteúdo:

import os
 
def production_mode():
    return not development_mode()
 
def development_mode():
    return 'WORKSPACE' in os.environ

Finalmente, modifique o módulo settings.py para que este utilize tais métodos onde for necessário definir configurações diferentes para ambientes de execução diferentes:

from environment import production_mode
 
# Configurações padrão
PREPEND_WWW = False
DEBUG = True
 
# Configurações para ambiente de produção
if production_mode():
    PREPEND_WWW = True
    DEBUG = False
 
MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.doc.XViewMiddleware',
    'pages.middleware.PageFallbackMiddleware',
    'django.contrib.redirects.middleware.RedirectFallbackMiddleware',
)
 
# Habilitando caching em modo de produção
if production_mode():
    MIDDLEWARE_CLASSES += (
        'django.middleware.cache.UpdateCacheMiddleware',
        'django.middleware.cache.FetchFromCacheMiddleware',
    )

Não tenha medo de utilizar os recursos do Python onde eles se fizerem necessários, apenas tomando cuidado para não exagerar e acabar criando acidentalmente um Algoritmo Genético para configurar seu projeto!

Informações secretas (IPs, usernames, passwords, etc)

Praticamente não há quem ignore a importância de um bom sistema de controle de revisões (Subversion, Git, etc). O problema é que, muitas vezes, não tomamos o devido cuidado com o que colocamos dentro dos nossos repositórios. Sempre que possível, informações secretas — como endereços de IP, usernames e passwords — devem ser mantidas fora dos repositórios.

Em um projeto Django, as informações de acesso a servidores de e-mail e bancos de dados devem estar disponíveis através do módulo  settings.py. Felizmente, existe um jeito simples de manter essas informações separadas das outras configurações.

Primeiramente, abra o arquivo settings.py do seu projeto e remova todas as senhas e outras informações que você considere secretas. Em seguida, cole o seguinte trecho de código no final do arquivo:

try:
    from private import *
except ImportError:
    pass

Feito isso, crie o módulo private.py no diretório raíz do seu projeto Django. Coloque nele as configurações que você removeu do settings.py no passo anterior. Exemplo:

from environment import production_mode
 
SECRET_KEY = 'secret_key_do_seu_projeto'
 
EMAIL_HOST = 'localhost'
EMAIL_HOST_PASSWORD = 'senha_email_local'
 
DATABASE_HOST = 'localhost'
DATABASE_USER = 'username_db_local'
DATABASE_PASSWORD = 'senha_db_local'
 
if production_mode():
    EMAIL_HOST = 'ip_email_producao'
    EMAIL_HOST_PASSWORD = 'senha_email_producao'
 
    DATABASE_HOST = 'ip_db_producao'
    DATABSE_USER = 'username_db_producao'
    DATABASE_PASSWORD = 'senha_db_producao'

Certifique-se de adicionar o arquivo private.py no ignore (.cvsignore para CVS, .gitignore para Git, etc) correspondente ao sistema de controle de revisões sendo utilizado. Faça o commit e pronto.

Para finalizar, se o seu repositório já contém uma versão “insegura” do módulo settings.py, mude as senhas dos seus servidores.

E você, o que recomenda?

Tem alguma dica útil para compartilhar conosco? Nós adoraríamos ouvir o que você tem a dizer!

8 Responses to “Django e settings.py: dicas e boas práticas”

  1. Muito boas as dicas. Eu particularmente não gosto de o django armazenar as senhas em forma de texo, e possibilitar que elas seja obtidas facilmente. Em meus projetos eu adotei a política de criptografar as senhas (estes dados que você coloca como private). No caso, adoto algo do tipo:

    DATABASE_PASSWORD = descriptografar('senha_criptografada')

    Onde descriptografar é uma função que eu mesmo criei, baseada no base64 pra criptografar.

  2. Excelente idéia Vinicius, com certeza eu a implementarei aqui. Obrigado por compartilhar!

    A minha abordagem mantém as senhas fora do repositório, mas não adianta muita coisa pois elas ainda existem (de forma plain-text, o que é pior) nos computadores de desenvolvimento. Se, por exemplo, o meu notebook for furtado por alguém com o mínimo de conhecimento, essas informações podem ser usadas de forma indevida.

    Obrigado novamente!

  3. A minha abordagem evita que a senha seja lida em texto plano, mas se o usuário fizer:

    $ python
    >>> import settings
    >>> settings.DATABASE_PASSWORD
    ‘minha_senha_em_texto_plano’

    Ou seja, se a pessoa entender de python e django, consegue a senha fácil, fácil… e não adianta nem mandar o arquivo com as senhas em formato .pyc…

    O ideal seria que o django recomendasse que você criptografasse a senha, e então o core faria algum algoritmo pra descompressão na hora de conectar ao servidor… mas ainda assim, não impossibilita que peguem a senha, mas dificulta bastante…

  4. Ah sim! Nesse caso não tem jeito mesmo. Ainda assim, é uma proteção a mais.

  5. o que fazemos nos nossos servidores é permitir acesso ao arquivo de local_settings somente aos usuários que realmente necessitam (não colocamos senhas e hash de segurança no settings padrão), assim a senha fica em um arquivo protegido, já nas máquinas locais, se for ambiente linux dá pra fazer algo semelhante, mas como todas as máquinas tem um bd próprio e não usam o bd principal, a segurança não precisa ser grande.
    outro ponto é que o usuário de acesso ao banco do django (e o acesso à rede do bd) também devem ser restritos.
    Agora, se a pessoa tiver a senha do usuário do linux, souber django, tiver acesso à rede do banco, ou ele é funcionário ou vcs demoraram pra contratar =P

  6. Obrigado pelas informações, Anderson!

  7. Essa parte é algo que gosto mais do rails do que do django. No rails eu tenho, nativamente, arquivo de configurações gerais (environment.rb) e depois tenho mais três arquivos de configurações:

    Ambiente de Desenvolvimento: development.rb
    Ambiente de Produção: production.rb
    Ambiente de Teste: test.rb

    Claro que da para fazer isso com o Django, mas não é nativo. :P

    abraços,

  8. Verdade Willian, concordo plenamente. Além do Rails, são vários os frameworks que possibilitam dividir configurações por tipo de “ambiente”, e isso é muito útil no dia-a-dia.

    Criar algo mais padronizado para o Django é uma idéia que deve ser considerada. Assim que sobrar um tempinho eu vou tentar bolar alguma coisa! (será que sai OUTRO projeto open source?) :D

    Grande abraço!

Leave a Reply