Django: Como gerenciar configurações de desenvolvimento e produção?

128

Estou desenvolvendo um aplicativo básico. Agora, no estágio de implantação, ficou claro que eu preciso de configurações locais e de produção.

Seria ótimo saber o seguinte:

  • Qual a melhor forma de lidar com as configurações de desenvolvimento e produção.
  • Como manter aplicativos como o django-debug-toolbar apenas em um ambiente de desenvolvimento.
  • Quaisquer outras dicas e práticas recomendadas para configurações de desenvolvimento e implantação.
Kristian Roebuck
fonte

Respostas:

108

A DJANGO_SETTINGS_MODULEvariável de ambiente controla quais arquivos de configuração o Django carregará.

Portanto, você cria arquivos de configuração separados para seus respectivos ambientes (observe que eles podem, obviamente, a import *partir de um arquivo separado "de configurações compartilhadas") e use-os DJANGO_SETTINGS_MODULEpara controlar qual deles usar.

Aqui está como:

Conforme observado na documentação do Django:

O valor de DJANGO_SETTINGS_MODULE deve estar na sintaxe do caminho do Python, por exemplo, mysite.settings. Observe que o módulo de configurações deve estar no caminho de pesquisa de importação do Python.

Então, vamos assumir que você criou myapp/production_settings.pye myapp/test_settings.pyno seu repositório de origem.

Nesse caso, você configuraria, respectivamente, DJANGO_SETTINGS_MODULE=myapp.production_settingso primeiro e DJANGO_SETTINGS_MODULE=myapp.test_settingso último.


Daqui em diante, o problema se resume a definir a DJANGO_SETTINGS_MODULEvariável de ambiente.

Configurando DJANGO_SETTINGS_MODULEUsando um Script ou Shell

Então você pode usar um script de inicialização ou um gerente de processo para carregar as configurações corretas (definindo o meio ambiente), ou apenas executá-lo a partir do seu shell antes de iniciar Django: export DJANGO_SETTINGS_MODULE=myapp.production_settings.

Observe que você pode executar essa exportação a qualquer momento a partir de um shell - ele não precisa viver no seu .bashrc ou em nada.

Configurando DJANGO_SETTINGS_MODULEUsando um Process Manager

Se você não gosta de escrever um script de auto-inicialização que define o ambiente (e há boas razões para se sentir assim!), Eu recomendaria o uso de um gerenciador de processos:


Por fim, observe que você pode tirar proveito da PYTHONPATHvariável para armazenar as configurações em um local completamente diferente (por exemplo, em um servidor de produção, armazenando-as /etc/). Isso permite separar a configuração dos arquivos do aplicativo. Você pode ou não querer isso, depende de como seu aplicativo está estruturado.

Thomas Orozco
fonte
7
Para esclarecer, como o settings.pyarquivo é armazenado SiteName/settings.pypor padrão, se você colocar seus arquivos de configuração alternativos no mesmo diretório, a linha adicionada ao bin / enable deve ler DJANGO_SETTINGS_MODULE="SiteName.test_settings"Caso contrário, excelente resposta!
Alexbhandari 2/14
2
por coincidência é que você sabe um tutorial sobre como fazer isso passo a passo, eu sou novo para Django e não sabes onde para definir o DJANGO_SETTINGS_MODULE ou PYTHONPATH
Jesus Almaral - Hackaprende
Esta solução parece não ser verdadeira para um ambiente conda. Não há bin / ativar em um ambiente conda.
Pouya Yousefi
1
@PouyaYousefi: você absolutamente não precisa usar o virtualenv para usar esta resposta. A resposta realmente se resume a duas etapas: a) use arquivos de configurações separados eb) use DJANGO_SETTINGS_MODULEpara escolher o que você deseja usar. Modificar bin/activate é o de fazer o último (TBH, eu não acho mais uma boa ideia, então tirei isso), mas não é o único.
Thomas Orozco
Também é útil Se você estiver usando o Django no pycharm community edition e precisar executar testes de unidade na linha de comando e na comunidade pycharm corretamente. Suponha que você criou apenas um arquivo de configuração simples em myapp / settings.py no seu repositório de origem. Nesse caso, você definiria “DJANGO_SETTINGS_MODULE = myapp.settings” no menu EXECUTAR / Editar variável de configuração / ambiente para usá-lo posteriormente para executar casos de teste.
F.Tamy
57

Por padrão, use as configurações de produção, mas crie um arquivo chamado settings_dev.pyna mesma pasta que o seu settings.pyarquivo. Adicione substituições lá, comoDEBUG=True .

No computador que será usado para o desenvolvimento, adicione isso ao seu ~/.bashrcarquivo:

export DJANGO_DEVELOPMENT=true

Na parte inferior do seu settings.pyarquivo, adicione o seguinte.

# Override production variables if DJANGO_DEVELOPMENT env variable is set
if os.environ.get('DJANGO_DEVELOPMENT'):
    from settings_dev import *  # or specific overrides

(Observe que a importação *geralmente deve ser evitada no Python)

Por padrão, os servidores de produção não substituem nada. Feito!

Comparado com as outras respostas, essa é mais simples porque não requer atualização PYTHONPATH ou configuração DJANGO_SETTINGS_MODULEque permite apenas trabalhar em um projeto de django por vez.

cs01
fonte
8
como essa não é a resposta correta? SO é realmente uma bagunça hoje em dia. Ty CS01
codyc4321
if os.environ.get('DJANGO_DEVELOPMENT', 'true')também funciona. Menciono isso apenas porque o is not truemétodo acima falhou ao importar para mim no Python 3.6.
brt
1
@brt é uma péssima idéia: ele sempre usará suas DEVconfigurações que vazarão dados particulares em um servidor público. Você realmente quer apenas verificar se a DJANGO_DEVELOPMENTvariável de ambiente existe (ou seja is not None).
CS01
Obrigado pela informação, @ cs01. Percebi que fiz algo errado quando explodi meu site com o carregamento incorreto de configurações, mas não sabia ao certo por que settings_dev.pyestava carregando no servidor.
brt
2
@ cs01 Eu chegaria ao ponto de garantir que ela exista e seja verdadeira, apenas deixando o is not Nonecheque. Também os.getenvé a abreviação
Tjorriemorrie 19/02/19
35

Normalmente, tenho um arquivo de configurações por ambiente e um arquivo de configurações compartilhado:

/myproject/
  settings.production.py
  settings.development.py
  shared_settings.py

Cada um dos meus arquivos de ambiente possui:

try:
    from shared_settings import *
except ImportError:
    pass

Isso me permite substituir as configurações compartilhadas, se necessário (adicionando as modificações abaixo dessa estrofe).

Depois, seleciono quais arquivos de configuração usar vinculando-o a settings.py:

ln -s settings.development.py settings.py
Daniel Watkins
fonte
2
Como você lida com a proibição do pep8 import *? Você desabilita essa verificação? Eu envolto esta importação em uma exec(), mas em seguida, eu não posso ter condicionais em variáveis que não estão definidos neste arquivo, nem posso alter INSTALLED_APPSvariável porque é "indefinido"
Mikhail
11
Nós não utilizamos nossos arquivos de configurações, porque eles não são realmente um código, mas uma configuração expressa em Python.
Daniel Watkins
17

É assim que eu faço em 6 etapas fáceis:

  1. Crie uma pasta dentro do diretório do projeto e nomeie-a settings .

    Estrutura do projeto:

    myproject/
           myapp1/
           myapp2/              
           myproject/
                  settings/
  2. Crie quatro arquivos python dentro do settingsdiretório __init__.py, a saber base.py, dev.pyeprod.py

    Arquivos de configurações:

    settings/
         __init__.py
         base.py
         prod.py
         dev.py 
  3. Abra __init__.pye preencha com o seguinte conteúdo:

    init .py:

    from .base import *
    # you need to set "myproject = 'prod'" as an environment variable
    # in your OS (on which your website is hosted)
    if os.environ['myproject'] == 'prod':
       from .prod import *
    else:
       from .dev import *
  4. Abra base.pye preencha-o com todas as configurações comuns (que serão usadas tanto na produção quanto no desenvolvimento.), Por exemplo:

    base.py:

    import os
    ...
    INSTALLED_APPS = [...]
    MIDDLEWARE = [...]
    TEMPLATES = [{...}]
    ...
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
    MEDIA_ROOT = os.path.join(BASE_DIR, '/path/')
    MEDIA_URL = '/path/'
  5. Abra dev.pye inclua os itens específicos do desenvolvimento, por exemplo:

    dev.py:

    DEBUG = True
    ALLOWED_HOSTS = ['localhost']
    ...
  6. Abra prod.pye inclua os itens específicos da produção, por exemplo:

    prod.py:

    DEBUG = False
    ALLOWED_HOSTS = ['www.example.com']
    LOGGING = [...]
    ...
Ahtisham
fonte
10

Crie vários settings*.pyarquivos, extrapolando as variáveis ​​que precisam ser alteradas por ambiente. Depois, no final do seu settings.pyarquivo mestre :

try:
  from settings_dev import *
except ImportError:
  pass

Você mantém os settings_*arquivos separados para cada estágio.

Na parte superior do seu settings_dev.pyarquivo, adicione este:

import sys
globals().update(vars(sys.modules['settings']))

Para importar variáveis ​​que você precisa modificar.

Esta entrada do wiki tem mais ideias sobre como dividir suas configurações.

Burhan Khalid
fonte
Graças à Burham! Ao implantar o aplicativo, seria necessário remover o arquivo settings_dev para ver minhas configurações de implantação em ação?
Kristian Roebuck
Sim, ou substitua a importação porsettings_prod.py
Burhan Khalid
1
A edição do arquivo master settings.py em uma implantação significa que ele entrará em conflito com o controle de versão, portanto, não é necessariamente o melhor caminho a seguir. Eu diria que a opção de Thomas Orozco é melhor - você pode definir o DJANGO_SETTINGS_MODULE em seu script virtualenv postactivate ou em seu gunicorn ou configuração mod_wsgi
Steve Jalim
1
Talvez deva ser mencionado que você nunca adiciona ao controle de origem os arquivos específicos do estágio. Supus que se entendesse que você não enviaria configurações específicas para o estágio de um projeto.
Burhan Khalid
Se você estiver usando o virtualenv, normalmente será padronizado para {{project_name}}. Portanto, 'configurações' não será a chave no sys.modules. Será 'myproject.settings' (ou qualquer que seja o nome do seu projeto). Você pode usar modname = "%s.settings" % ".".join(__name__.split('.')[:-1])para obter o nome completo do módulo e, em seguida globals().update(vars(sys.modules[modname])). Acho que funciona muito bem para mim. É claro que deixar de lado a questão de determinar programaticamente o nome do módulo em favor de uma string provavelmente também funcionaria na maioria dos casos.
Eric
9

Eu uso as incríveis configurações de django e todas as configurações são armazenadas no meu settings.py:

from configurations import Configuration

class Base(Configuration):
    # all the base settings here...
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    ...

class Develop(Base):
    # development settings here...
    DEBUG = True 
    ...

class Production(Base):
    # production settings here...
    DEBUG = False

Para configurar o projeto Django, apenas segui os documentos .

Riccardo Leschiutta
fonte
7

Aqui está a abordagem que usamos:

  • um settingsmódulo para dividir as configurações em vários arquivos para facilitar a leitura;
  • um .env.jsonarquivo para armazenar credenciais e parâmetros que queremos excluir do repositório git ou que são específicos do ambiente;
  • um env.pyarquivo para ler o .env.jsonarquivo

Considerando a seguinte estrutura:

...
.env.json           # the file containing all specific credentials and parameters
.gitignore          # the .gitignore file to exclude `.env.json`
project_name/       # project dir (the one which django-admin.py creates)
  accounts/         # project's apps
    __init__.py
    ...
  ...
  env.py            # the file to load credentials
  settings/
    __init__.py     # main settings file
    database.py     # database conf
    storage.py      # storage conf
    ...
venv                # virtualenv
...

Com .env.jsoncomo:

{
    "debug": false,
    "allowed_hosts": ["mydomain.com"],
    "django_secret_key": "my_very_long_secret_key",
    "db_password": "my_db_password",
    "db_name": "my_db_name",
    "db_user": "my_db_user",
    "db_host": "my_db_host",
}

E project_name/env.py:

<!-- language: lang-python -->
import json
import os


def get_credentials():
    env_file_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    with open(os.path.join(env_file_dir, '.env.json'), 'r') as f:
        creds = json.loads(f.read())
    return creds


credentials = get_credentials()

Podemos ter as seguintes configurações:

<!-- language: lang-py -->
# project_name/settings/__init__.py
from project_name.env import credentials
from project_name.settings.database import *
from project_name.settings.storage import *
...

SECRET_KEY = credentials.get('django_secret_key')

DEBUG = credentials.get('debug')

ALLOWED_HOSTS = credentials.get('allowed_hosts', [])

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    ...
]

if DEBUG:
    INSTALLED_APPS += ['debug_toolbar']

...

# project_name/settings/database.py
from project_name.env import credentials

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': credentials.get('db_name', ''),
        'USER': credentials.get('db_user', ''),
        'HOST': credentials.get('db_host', ''),
        'PASSWORD': credentials.get('db_password', ''),
        'PORT': '5432',
    }
}

Os benefícios desta solução são:

  • credenciais e configurações específicas do usuário para desenvolvimento local sem modificar o repositório git;
  • configuração específica do ambiente , você pode ter, por exemplo, três ambientes diferentes, com três diferentes .env.jsoncomo dev, stagging e produção;
  • credenciais não estão no repositório

Espero que isso ajude, deixe-me saber se você vê alguma ressalva com esta solução.

Charlesthk
fonte
assumindo onde envsubstituir dev, prodetc.? O que se passa no settings.pyarquivo antigo ? O que há storage.pye database.py?
dbinott
Oi @dbinott, você pode facilmente atualizar o env.pyarquivo para que o seu pode escolher, com uma variável de ambiente, que arquivo para carga
Charlesthk
Por exemplo: conf = os.environ.get ('CONF', '') file_ = f ".env. {Conf} .json"
Charlesthk
Por que você json em oposição a um tipo de dados python nativo?
ataque aéreo
4

Eu uso a estrutura de arquivo de acompanhamento:

project/
   ...
   settings/
   settings/common.py
   settings/local.py
   settings/prod.py
   settings/__init__.py -> local.py

Então __init__.pyé um link (ln em UNIX ou mklink no Windows) para local.pyou pode ser para prod.pyque a configuração ainda está no project.settingsmódulo é limpo e organizado, e se você quiser usar uma configuração especial que você pode usar a variável de ambiente DJANGO_SETTINGS_MODULEpara project.settings.prodse precisar para executar um comando para o ambiente de produção.

Nos arquivos prod.pye local.py:

from .shared import *

DATABASE = {
    ...
}

e o shared.pyarquivo é mantido como global sem configurações específicas.

Felipe Buccioni
fonte
3

desenvolvendo a resposta do cs01:

se você estiver tendo problemas com a variável de ambiente, defina seu valor como uma string (por exemplo, eu fiz DJANGO_DEVELOPMENT="true").

Também alterei o fluxo de trabalho do arquivo cs01 da seguinte maneira:

#settings.py
import os
if os.environ.get('DJANGO_DEVELOPMENT') is not None:
    from settings_dev import * 
else:
    from settings_production import *
#settings_dev.py
development settings go here
#settings_production.py
production settings go here

Dessa forma, o Django não precisa ler a totalidade de um arquivo de configurações antes de executar o arquivo de configurações apropriado. Esta solução é útil se o seu arquivo de produção precisar de coisas que estão apenas no seu servidor de produção.

Nota: no Python 3, os arquivos importados precisam ter um .anexo (por exemplo from .settings_dev import *)

Brian Lee
fonte
1

Se você deseja manter 1 arquivo de configurações e seu sistema operacional de desenvolvimento é diferente do sistema operacional de produção, coloque-o na parte inferior de suas configurações.py:

from sys import platform
if platform == "linux" or platform == "linux2":
    # linux
    # some special setting here for when I'm on my prod server
elif platform == "darwin":
    # OS X
    # some special setting here for when I'm developing on my mac
elif platform == "win32":
    # Windows...
    # some special setting here for when I'm developing on my pc

Leia mais: Como verifico o sistema operacional em Python?

Do utilizador
fonte
1

Isso parece ter sido respondido, no entanto, um método que eu uso como combinado com o controle de versão é o seguinte:

Configure um arquivo env.py no mesmo diretório que as configurações no meu ambiente de desenvolvimento local que eu também adiciono ao .gitignore:

env.py:

#!usr/bin/python

DJANGO_ENV = True
ALLOWED_HOSTS = ['127.0.0.1', 'dev.mywebsite.com']

.gitignore:

mywebsite/env.py

settings.py:

if os.path.exists(os.getcwd() + '/env.py'):
    #env.py is excluded using the .gitignore file - when moving to production we can automatically set debug mode to off:
    from env import *
else:
    DJANGO_ENV = False

DEBUG = DJANGO_ENV

Acabei de achar que isso funciona e é muito mais elegante - com o env.py é fácil ver nossas variáveis ​​de ambiente local e podemos lidar com tudo isso sem vários arquivos settings.py ou coisas do tipo. Esse método permite que todos os tipos de variáveis ​​de ambiente local sejam utilizados que não desejamos definir em nosso servidor de produção. Utilizando o .gitignore via controle de versão, também mantemos tudo perfeitamente integrado.


fonte
Solução mais simples. Pode-se também definir tudo em uma Configclasse dentro do env.pyarquivo. Em vez de um import *, o módulo pode ser importado por from env import Config. Dessa forma, você também não precisa usar isso se os.pathverificar, o que torna tudo mais simples.
Siddharth Pant
0

Use settings.pypara produção. No mesmo diretório, crie settings_dev.pypara substituições.

# settings_dev.py

from .settings import * 

DEBUG = False

Em uma máquina de desenvolvimento, execute seu aplicativo Django com:

DJANGO_SETTINGS_MODULE=<your_app_name>.settings_dev python3 manage.py runserver

Em uma máquina de prod, execute como se você tivesse settings.pye nada mais.

VANTAGENS

  1. settings.py (usado para produção) é completamente independente do fato de que outros ambientes existem.
  2. Para ver a diferença entre prod e dev, basta olhar para um único local - settings_dev.py. Não há necessidade de reunir configurações espalhadas por settings_prod.py, settings_dev.pye settings_shared.py.
  3. Se alguém adicionar uma configuração à sua configuração de produção após solucionar um problema de produção, você pode ter certeza de que ela também aparecerá na sua configuração de desenvolvimento (a menos que seja explicitamente substituída). Assim, a divergência entre os diferentes arquivos de configuração será minimizada.
Alex Yursha
fonte
0

Para o problema de configurar arquivos, eu escolho copiar

Project
   |---__init__.py   [ write code to copy setting file from subdir to current dir]
   |---settings.py  (do not commit this file to git)
   |---setting1_dir
   |         |--  settings.py
   |---setting2_dir
   |         |--  settings.py

Quando você executa o django, __init__py será executado. Neste momento, settings.py in setting1_dirirá substituir settings.py in Project.

Como escolher um ambiente diferente?

  • modificar __init__.pydiretamente.
  • faça um arquivo bash para modificar __init__.py.
  • modifique env no linux e então deixe __init__.pyler esta variável.

Por que usar dessa maneira?

Como não gosto de tantos arquivos no mesmo diretório, muitos arquivos confundem outros parceiros e não muito bem para o IDE (o IDE não consegue encontrar o arquivo que usamos)

Se você não deseja ver todos esses detalhes, pode dividir o projeto em duas partes.

  1. faça sua pequena ferramenta como o Spring Initializr, apenas para configurar seu projeto. (faça o sth como copiar arquivo)
  2. o código do seu projeto
kyakya
fonte
0

Estou usando um arquivo app.yaml diferente para alterar a configuração entre ambientes no Google Cloud App Engine.

Você pode usar isso para criar uma conexão proxy no seu comando do terminal:

./cloud_sql_proxy -instances=<INSTANCE_CONNECTION_NAME>=tcp:1433

https://cloud.google.com/sql/docs/sqlserver/connect-admin-proxy#macos-64-bit

Arquivo: app.yaml

# [START django_app]
service: development
runtime: python37

env_variables:
  DJANGO_DB_HOST: '/cloudsql/myproject:myregion:myinstance'
  DJANGO_DEBUG: True

handlers:
# This configures Google App Engine to serve the files in the app's static
# directory.
- url: /static
  static_dir: static/

# This handler routes all requests not caught above to your main app. It is
# required when static routes are defined, but can be omitted (along with
# the entire handlers section) when there are no static files defined.
- url: /.*
  script: auto
# [END django_app]
Rodrigo Grossi
fonte
-1

Esta é a minha solução, com diferentes ambientes para dev, test e prod

import socket

[...]

DEV_PC = 'PC059'
host_name = socket.gethostname()

if host_name == DEV_PC:
   #do something
   pass
elif [...]
Massimo Variolo
fonte