Como gerenciar configurações locais vs de produção no Django?

298

Qual é a maneira recomendada de lidar com as configurações do desenvolvimento local e do servidor de produção? Alguns deles (como constantes, etc.) podem ser alterados / acessados ​​em ambos, mas alguns deles (como caminhos para arquivos estáticos) precisam permanecer diferentes e, portanto, não devem ser substituídos toda vez que o novo código for implantado.

Atualmente, estou adicionando todas as constantes a settings.py. Mas sempre que altero alguma constante localmente, tenho que copiá-la para o servidor de produção e editar o arquivo para alterações específicas de produção ... :(

Edit: parece que não há resposta padrão para esta pergunta, eu aceitei o método mais popular.

akv
fonte
Por favor, dê uma olhada nas configurações de django .
JJD
2
O método aceito não é mais o mais popular.
Daniel
2
O Django-Split-Settings é muito fácil de usar. Não é necessário reescrever nenhuma configuração padrão.
Sobolevn 02/11/2015
você deve usar o arquivo base.py e em seu local.py "de .base import *", o mesmo em seu production.py "de .base import *", você precisa executar seu projeto com: python manage.py runserver - settings = project_name.settings.local
Roberth Solís

Respostas:

127

Em settings.py:

try:
    from local_settings import *
except ImportError as e:
    pass

Você pode substituir o que é necessário local_settings.py; deve ficar fora do seu controle de versão. Mas desde que você mencionou a cópia, acho que você não usa nenhuma;)

ohnoes
fonte
3
Para facilitar o rastreamento / implantação de novas configurações, use um "local_settings.py" nas máquinas de produção / teste e nenhum no desenvolvimento.
31910 John Mee
8
Essa é a maneira que eu faço - adicionando essas linhas no final do settings.py para que eles possam substituir as configurações padrão
daonb
61
Essa abordagem significa que você tem código não versionado em execução no desenvolvimento e produção. E todo desenvolvedor tem uma base de código diferente. Eu chamo anti-padrão aqui.
pydanny
8
@pydanny O problema é que o Django armazena sua configuração no arquivo .py. Você não pode esperar que todos os desenvolvedores e servidores de produção usem as mesmas configurações; portanto, é necessário alterar esse arquivo .py ou implementar alguma solução alternativa (arquivos .ini, ambiente etc.).
Tupteq
3
Prefiro chamar o módulo settings_localem vez local_settingsde agrupá-lo settings.pynas listagens de pastas alfabéticas. Mantenha settings_local.pyfora do controle de versão, .gitignorepois as credenciais não pertencem ao Git. Imagine abrir fontes por acidente. Eu mantenho no git um arquivo de modelo chamado settings_local.py.txt.
Fmalina
297

Duas dicas do Django: Práticas recomendadas para o Django 1.5 sugerem o uso de controle de versão para seus arquivos de configurações e o armazenamento em um diretório separado:

project/
    app1/
    app2/
    project/
        __init__.py
        settings/
            __init__.py
            base.py
            local.py
            production.py
    manage.py

O base.pyarquivo contém as definições comuns (como MEDIA_ROOT ou ADMIN), enquanto local.pye production.pytem configurações específicas do local:

No arquivo base settings/base.py:

INSTALLED_APPS = (
    # common apps...
)

No arquivo de configurações de desenvolvimento local settings/local.py:

from project.settings.base import *

DEBUG = True
INSTALLED_APPS += (
    'debug_toolbar', # and other apps for local development
)

No arquivo de configurações de produção de arquivo settings/production.py:

from project.settings.base import *

DEBUG = False
INSTALLED_APPS += (
    # other apps for production site
)

Então, quando você executa o django, você adiciona a --settingsopção:

# Running django for local development
$ ./manage.py runserver 0:8000 --settings=project.settings.local

# Running django shell on the production site
$ ./manage.py shell --settings=project.settings.production

Os autores do livro também colocaram um modelo de layout de projeto de amostra no Github.

gene_wood
fonte
62
Observe que, em vez de usar --settingssempre, você pode definir o DJANGO_SETTINGS_MODULEenvvar. Isso funciona bem com, por exemplo, o Heroku: defina-o globalmente para produção e substitua-o pelo dev no seu arquivo .env.
Simon Weber
9
Usar DJANGO_SETTINGS_MODULEenv var é a melhor ideia aqui, obrigado Simon.
Kibibu
20
Pode ser necessário alterar BASE_DIRas configurações paraos.path.dirname(os.path.realpath(os.path.dirname(__file__) + "/.."))
Petr Peller
5
@rsp, de acordo com os documentos do django, você importa from django.conf import settingsum objeto que abstrai a interface e desacopla o código da localização das configurações, docs.djangoproject.com/en/dev/topics/settings/…
3
Se eu definir o DJANGO_SETTINGS_MODULE através de uma variável ambiental, ainda preciso do os.environ.setdefault ("DJANGO_SETTINGS_MODULE", "projectname.settings.production") no meu arquivo wsgi.py? Além disso, configurei a var ambiental usando: export DJANGO_SETTINGS_MODULE = projectname.settings.local, mas ela será perdida quando fecho o terminal. O que posso fazer para garantir que ele seja salvo? Devo adicionar essa linha ao arquivo bashrc?
Kritz 28/10
71

Em vez de settings.py, use este layout:

.
└── settings/
    ├── __init__.py  <= not versioned
    ├── common.py
    ├── dev.py
    └── prod.py

common.py é onde vive a maior parte de sua configuração.

prod.py importa tudo de comum e substitui o que for necessário para substituir:

from __future__ import absolute_import # optional, but I like it
from .common import *

# Production overrides
DEBUG = False
#...

Da mesma forma, dev.pyimporta tudo common.pye substitui o que for necessário para substituir.

Por fim, __init__.pyé onde você decide quais configurações carregar e também onde armazena segredos (portanto, esse arquivo não deve ser versionado):

from __future__ import absolute_import
from .prod import *  # or .dev if you want dev

##### DJANGO SECRETS
SECRET_KEY = '(3gd6shenud@&57...'
DATABASES['default']['PASSWORD'] = 'f9kGH...'

##### OTHER SECRETS
AWS_SECRET_ACCESS_KEY = "h50fH..."

O que eu gosto nesta solução é:

  1. Tudo está no seu sistema de versões, exceto segredos
  2. A maioria de configuração está em um lugar: common.py.
  3. Coisas específicas do Prod entram prod.py, coisas específicas do Dev entram dev.py. É simples.
  4. Você pode substituir itens de common.pydentro de prod.pyou ou dev.pysubstituir qualquer item __init__.py.
  5. É python simples. Não há reimportação de hacks.
MiniQuark
fonte
2
Ainda estou tentando descobrir o que definir nos meus arquivos project.wsgi e manage.py para o arquivo de configurações. Você vai lançar alguma luz sobre isso? Especificamente, no meu arquivo manage.py, o os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings")foobar é uma pasta com um __init__.pyarquivo e as configurações são uma pasta com um __init__.pyarquivo que contém meus segredos e importa dev.py, que importa o common.py. EDIT Não importa, eu não tinha um módulo instalado necessário. Foi mal! Isso funciona muito bem !!
Julio
5
Duas coisas: 1) é melhor definir Debug = True no seu dev.py em vez de = False no seu prod.py. 2) Em vez de alternar no init .py, alterne usando o ambiente DJANGO_SETTINGS_MODULE var. Isso ajudará nas implantações do PAAS (por exemplo, Heroku).
Rob Grant
Quando eu uso essa configuração no django 1.8.4 e tento o servidor de execução, recebo "django.core.exceptions.ImproperlyConfigured: A configuração SECRET_KEY não deve estar vazia.", Mesmo que eu tenha SECRET_KEY no meu arquivo .py de inicialização . Estou esquecendo de algo?
polarcare
não é o uso de algo como AWS_SECRET_ACCESS_KEY = os.getenv ("AWS_SECRET_ACCESS_KEY") mais seguro? Pergunta honesta - Eu sei por que você não deseja que ele seja versionado, mas a outra alternativa é obtê-lo do ambiente. O que implora a questão de definir a variável de ambiente, é claro, mas isso pode ser deixado para o seu mecanismo de implantação, não?
JL Peyret #
20

Eu uso uma versão ligeiramente modificada do estilo "if Debug" das configurações que Harper Shelby postou. Obviamente, dependendo do ambiente (win / linux / etc.), O código pode precisar ser ajustado um pouco.

Eu estava usando o "if DEBUG" no passado, mas descobri que ocasionalmente precisava fazer testes com o DEUBG definido como False. O que eu realmente queria distinguir se o ambiente fosse produção ou desenvolvimento, o que me deu a liberdade de escolher o nível DEBUG.

PRODUCTION_SERVERS = ['WEBSERVER1','WEBSERVER2',]
if os.environ['COMPUTERNAME'] in PRODUCTION_SERVERS:
    PRODUCTION = True
else:
    PRODUCTION = False

DEBUG = not PRODUCTION
TEMPLATE_DEBUG = DEBUG

# ...

if PRODUCTION:
    DATABASE_HOST = '192.168.1.1'
else:
    DATABASE_HOST = 'localhost'

Eu ainda consideraria esse modo de configuração um trabalho em andamento. Eu não vi nenhuma maneira de lidar com as configurações do Django que cobriam todas as bases e, ao mesmo tempo, não era um aborrecimento total para configurar (eu não conheço os métodos dos arquivos de configurações de 5x).

T. Stone
fonte
Esse é o tipo de coisa que as configurações do Django, sendo um arquivo de código real, permitem, e eu estava sugerindo. Eu mesmo não fiz nada assim, mas é definitivamente o tipo de solução que pode ser uma resposta geral melhor do que a minha.
Harper Shelby
3
Acabei de encontrar isso pela primeira vez e escolhi (com sucesso!) Usar sua solução, com uma pequena diferença: usei uuid.getnode () para encontrar o uuid do meu sistema. Então, estou testando se uuid.getnode () == 12345678901 (na verdade, um número diferente) em vez do teste os.environ que você usou. Não consegui encontrar documentação para me convencer de que os.environ ['COMPUTERNAME'] é único por computador.
Joe Golton
o os.environ ['COMPUTERNAME'] não funciona no Amazon AWS Ubuntu. Eu recebo um KeyError.
nu everest
Ao usar o UUID, esta solução provou ser a melhor e mais simples para mim. Não requer muita colcha de retalhos complicada e supermodularizada. Em um ambiente de produção, você ainda precisa colocar as senhas do banco de dados e SECRET_KEY em um arquivo separado que reside fora do controle de versão.
nu everest
os.environ['COMPUTERNAME']infelizmente não funciona no PythonAnywhere. Você recebe um KeyError.
nbeuchat
14

Eu uso um settings_local.py e um settings_production.py. Depois de tentar várias opções, descobri que é fácil perder tempo com soluções complexas quando simplesmente ter dois arquivos de configuração é fácil e rápido.

Quando você usa mod_python / mod_wsgi para o seu projeto Django, você precisa apontar para o seu arquivo de configurações. Se você apontar para app / settings_local.py no servidor local e app / settings_production.py no servidor de produção, a vida se tornará fácil. Apenas edite o arquivo de configurações apropriado e reinicie o servidor (o servidor de desenvolvimento do Django será reiniciado automaticamente).

Kai
fonte
2
E o servidor de desenvolvimento local? existe uma maneira de dizer ao servidor web django (execute usando python manage.py runserver), qual arquivo de configurações usar?
AKV
2
@akv se você adicionar --settings = [nome do módulo] (sem extensão .py) ao final do comando runserver, poderá especificar qual arquivo de configurações usar. Se você fizer isso, faça um favor a si mesmo e crie um script de shell / arquivo em lote com as configurações de desenvolvimento definidas. Confie em mim, seus dedos vão agradecer.
T. Pedra
esta é a solução que eu uso. cortar um arquivo de configurações para ser utilizado tanto para a produção ou desenvolvimento é confuso
George Godik
4
Eu acho que é melhor usar o settings.py no desenvolvimento, pois você não precisa especificá-lo o tempo todo.
Andre Bossard 08/07/10
Estou correto ao assumir que esse método requer a importação do módulo de configurações por meio do proxy, django.conf.settings? Caso contrário, você precisará editar as declarações de importação para apontar para o arquivo de configurações correto ao pressionar ao vivo.
Groady 12/01
8

TL; DR: O truque é modificar os.environmentantes de importar settings/base.pyem qualquer settings/<purpose>.py, isso vai simplificar muito as coisas.


Só de pensar em todos esses arquivos entrelaçados me dá dor de cabeça. Combinando, importando (às vezes condicionalmente), substituindo, corrigindo o que já foi definido, caso a DEBUGconfiguração seja alterada posteriormente. Que pesadelo!

Ao longo dos anos, passei por todas as soluções diferentes. Todos eles funcionam um pouco , mas são tão difíceis de gerenciar. WTF! Realmente precisamos de todo esse aborrecimento? Começamos com apenas um settings.pyarquivo. Agora precisamos de uma documentação apenas para combinar corretamente tudo isso em uma ordem correta!

Espero finalmente chegar ao (meu) ponto ideal com a solução abaixo.

Vamos recapitular os objetivos (alguns comuns, outros meus)

  1. Mantenha segredos em segredo - não os guarde em um repositório!

  2. Defina / leia chaves e segredos através das configurações do ambiente, estilo de 12 fatores .

  3. Tenha padrões de fallback sensatos. Idealmente para o desenvolvimento local, você não precisa de mais nada além dos padrões.

  4. … Mas tente manter a produção dos padrões segura. É melhor perder uma substituição de configuração localmente do que lembrar de ajustar as configurações padrão seguras para produção.

  5. Tem a capacidade de ligar DEBUG/ desligar de uma maneira que possa afetar outras configurações (por exemplo, usando javascript compactado ou não).

  6. A alternância entre as configurações de finalidade, como local / teste / preparação / produção, deve basear-se apenas em DJANGO_SETTINGS_MODULEnada mais.

  7. … Mas permita parametrização adicional por meio de configurações do ambiente como DATABASE_URL.

  8. … Também permite que eles usem configurações de finalidade diferentes e as executem localmente lado a lado, por exemplo. configuração de produção na máquina local do desenvolvedor, para acessar o banco de dados de produção ou as folhas de estilo compactadas para teste de fumaça.

  9. Falha se uma variável de ambiente não estiver definida explicitamente (exigindo um valor vazio no mínimo), especialmente na produção, por exemplo. EMAIL_HOST_PASSWORD.

  10. Responda ao padrão DJANGO_SETTINGS_MODULEdefinido em manage.py durante o django-admin startproject

  11. Mantenha as condições mínimas, se a condição for o tipo de ambiente proposto (por exemplo, para o arquivo de log do conjunto de produção e sua rotação), substitua as configurações no arquivo de configurações propostas associado.

Não

  1. Não permita que o django leia a configuração DJANGO_SETTINGS_MODULE de um arquivo.
    Ugh! Pense em como isso é meta. Se você precisar de um arquivo (como o docker env), leia-o no ambiente antes de iniciar um processo de django.

  2. Não substitua DJANGO_SETTINGS_MODULE no código do seu projeto / aplicativo, por exemplo. com base no nome do host ou no nome do processo.
    Se você tem preguiça de definir variáveis ​​de ambiente (como para setup.py test), faça-o em ferramentas imediatamente antes de executar o código do projeto.

  3. Evite magia e correções de como o django lê suas configurações, pré-processe as configurações, mas não interfira posteriormente.

  4. Nenhuma lógica complicada baseada em lógica. A configuração deve ser fixa e materializada, não computada em tempo real. Fornecer padrões de fallback é apenas lógica suficiente aqui.
    Deseja realmente depurar, por que localmente você tem um conjunto correto de configurações, mas em produção em um servidor remoto, em uma das cem máquinas, algo calculado de maneira diferente? Oh! Testes unitários? Para configurações? Seriamente?

Solução

Minha estratégia consiste no excelente django-environment usado com iniarquivos de estilo, fornecendo os.environmentpadrões para o desenvolvimento local, alguns settings/<purpose>.pyarquivos mínimos e curtos que possuem um import settings/base.py APÓS o os.environmentconjunto foi definido a partir de um INIarquivo. Isso efetivamente nos dá um tipo de injeção de configurações.

O truque aqui é modificar os.environmentantes de importar settings/base.py.

Para ver o exemplo completo, faça o repo: https://github.com/wooyek/django-settings-strategy

.
   manage.py
├───data
└───website
    ├───settings
          __init__.py   <-- imports local for compatibility
          base.py       <-- almost all the settings, reads from proces environment 
          local.py      <-- a few modifications for local development
          production.py <-- ideally is empty and everything is in base 
          testing.py    <-- mimics production with a reasonable exeptions
          .env          <-- for local use, not kept in repo
       __init__.py
       urls.py
       wsgi.py

configurações / .env

Um padrão para o desenvolvimento local. Um arquivo secreto, para definir principalmente as variáveis ​​de ambiente necessárias. Configure-os para valores vazios se não forem necessários no desenvolvimento local. Nós fornecemos os padrões aqui e não settings/base.pyfalharemos em nenhuma outra máquina se eles estiverem ausentes no ambiente.

configurações / local.py

O que acontece aqui é carregar o ambiente de settings/.enve importar configurações comuns de settings/base.py. Depois disso, podemos substituir alguns para facilitar o desenvolvimento local.

import logging
import environ

logging.debug("Settings loading: %s" % __file__)

# This will read missing environment variables from a file
# We wan to do this before loading a base settings as they may depend on environment
environ.Env.read_env(DEBUG='True')

from .base import *

ALLOWED_HOSTS += [
    '127.0.0.1',
    'localhost',
    '.example.com',
    'vagrant',
    ]

# https://docs.djangoproject.com/en/1.6/topics/email/#console-backend
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'

LOGGING['handlers']['mail_admins']['email_backend'] = 'django.core.mail.backends.dummy.EmailBackend'

# Sync task testing
# http://docs.celeryproject.org/en/2.5/configuration.html?highlight=celery_always_eager#celery-always-eager

CELERY_ALWAYS_EAGER = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True

settings / production.py

Para produção, não devemos esperar um arquivo de ambiente, mas é mais fácil ter um se estiver testando algo. De qualquer maneira, para que não forneça alguns padrões em linha, é settings/base.pypossível responder de acordo.

environ.Env.read_env(Path(__file__) / "production.env", DEBUG='False', ASSETS_DEBUG='False')
from .base import *

O principal ponto de interesse aqui são DEBUGe ASSETS_DEBUGsubstitui, eles serão aplicados ao python os.environSOMENTE se estiverem ausentes do ambiente e do arquivo.

Esses serão os padrões de produção, não é necessário colocá-los no ambiente ou no arquivo, mas podem ser substituídos, se necessário. Arrumado!

configurações / base.py

Essas são as configurações do seu baunilha, com alguns condicionais e muitas leituras do ambiente. Quase tudo está aqui, mantendo todos os ambientes propostos consistentes e o mais semelhante possível.

As principais diferenças estão abaixo (espero que sejam auto-explicativas):

import environ

# https://github.com/joke2k/django-environ
env = environ.Env()

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Where BASE_DIR is a django source root, ROOT_DIR is a whole project root
# It may differ BASE_DIR for eg. when your django project code is in `src` folder
# This may help to separate python modules and *django apps* from other stuff
# like documentation, fixtures, docker settings
ROOT_DIR = BASE_DIR

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('SECRET_KEY')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DEBUG', default=False)

INTERNAL_IPS = [
    '127.0.0.1',
]

ALLOWED_HOSTS = []

if 'ALLOWED_HOSTS' in os.environ:
    hosts = os.environ['ALLOWED_HOSTS'].split(" ")
    BASE_URL = "https://" + hosts[0]
    for host in hosts:
        host = host.strip()
        if host:
            ALLOWED_HOSTS.append(host)

SECURE_SSL_REDIRECT = env.bool('SECURE_SSL_REDIRECT', default=False)

# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

if "DATABASE_URL" in os.environ:  # pragma: no cover
    # Enable database config through environment
    DATABASES = {
        # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
        'default': env.db(),
    }

    # Make sure we use have all settings we need
    # DATABASES['default']['ENGINE'] = 'django.contrib.gis.db.backends.postgis'
    DATABASES['default']['TEST'] = {'NAME': os.environ.get("DATABASE_TEST_NAME", None)}
    DATABASES['default']['OPTIONS'] = {
        'options': '-c search_path=gis,public,pg_catalog',
        'sslmode': 'require',
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            # 'ENGINE': 'django.contrib.gis.db.backends.spatialite',
            'NAME': os.path.join(ROOT_DIR, 'data', 'db.dev.sqlite3'),
            'TEST': {
                'NAME': os.path.join(ROOT_DIR, 'data', 'db.test.sqlite3'),
            }
        }
    }

STATIC_ROOT = os.path.join(ROOT_DIR, 'static')

# django-assets
# http://django-assets.readthedocs.org/en/latest/settings.html

ASSETS_LOAD_PATH = STATIC_ROOT
ASSETS_ROOT = os.path.join(ROOT_DIR, 'assets', "compressed")
ASSETS_DEBUG = env('ASSETS_DEBUG', default=DEBUG)  # Disable when testing compressed file in DEBUG mode
if ASSETS_DEBUG:
    ASSETS_URL = STATIC_URL
    ASSETS_MANIFEST = "json:{}".format(os.path.join(ASSETS_ROOT, "manifest.json"))
else:
    ASSETS_URL = STATIC_URL + "assets/compressed/"
    ASSETS_MANIFEST = "json:{}".format(os.path.join(STATIC_ROOT, 'assets', "compressed", "manifest.json"))
ASSETS_AUTO_BUILD = ASSETS_DEBUG
ASSETS_MODULES = ('website.assets',)

O último bit mostra o poder aqui. ASSETS_DEBUGpossui um padrão sensato, que pode ser substituído settings/production.pye mesmo aquele que pode ser substituído por uma configuração de ambiente! Yay!

Com efeito, temos uma hierarquia mista de importância:

  1. settings / .py - define padrões com base na finalidade, não armazena segredos
  2. settings / base.py - é principalmente controlado pelo ambiente
  3. configurações do ambiente de processo - bebê de 12 fatores!
  4. settings / .env - padrões locais para inicialização fácil
Janusz Skonieczny
fonte
Ei Janusz ... então no arquivo .env todas as chaves de API e chaves de autenticação e senhas etc? Assim como TWILLIO_API = "abc123"? Ou TWILLIO_API = env ("TWILLIO_API")?
dbinott
Sim, mas isso é apenas um substituto para as configurações do ambiente. Esse arquivo é útil para desenvolvimento, mas não é salvo no repositório ou enviado à produção, onde você deve usar estritamente as configurações do ambiente ou o equivalente à sua plataforma que, por sua vez, definirá as configurações do ambiente para o processo do servidor.
Janusz Skonieczny
7

Eu gerencio minhas configurações com a ajuda de django-split-settings .

É um substituto para as configurações padrão. É simples, mas configurável. E a refatoração das configurações existentes não é necessária.

Aqui está um pequeno exemplo (arquivo example/settings/__init__.py):

from split_settings.tools import optional, include
import os

if os.environ['DJANGO_SETTINGS_MODULE'] == 'example.settings':
    include(
        'components/default.py',
        'components/database.py',
        # This file may be missing:
        optional('local_settings.py'),

        scope=globals()
    )

É isso aí.

Atualizar

Eu escrevi um post sobre djangoas configurações de gerenciamento com django-split-sttings. Dar uma olhada!

sobolevn
fonte
1
Tentei fazer isso .. correu para uma parede, uma vez eu tentei correr meus testes unitários do Django .. eu simplesmente não conseguia descobrir como especificar qual arquivo de configurações para ler
abbood
Eu criei uma essência para você: gist.github.com/sobolevn/006c734f0520439a4b6c16891d65406c
sobolevn
Eu tenho algo parecido com isso no meu código, então eu verifico o sinalizador settings.DEBUG para saber se eu quero importar coisas .. esse sinalizador é sempre definido como falso nos testes de unidade do django (veja aqui ); cada teste como esse #
abbood
aqui está outra pergunta: meu uwsgi.iniarquivo tem configurações diferentes em dev / prod .. alguma idéia de como fazê-lo escolher valores do meu arquivo de configurações?
Abbood
desculpe, eu não entendi a configuração. você pode fazer uma pergunta separada com mais detalhes e tentarei ajudá-lo.
Sobolevn
6

O problema com a maioria dessas soluções é que você tem suas configurações locais aplicadas antes das comuns ou depois delas.

Portanto, é impossível substituir coisas como

  • as configurações específicas do ambiente definem os endereços para o pool memcached e, no arquivo de configurações principais, esse valor é usado para configurar o back-end do cache
  • as configurações específicas do ambiente adicionam ou removem apps / middleware ao padrão

ao mesmo tempo.

Uma solução pode ser implementada usando arquivos de configuração no estilo "ini" com a classe ConfigParser. Ele suporta vários arquivos, interpolação lenta de cadeias, valores padrão e muitas outras vantagens. Depois que vários arquivos forem carregados, mais arquivos poderão ser carregados e seus valores substituirão os anteriores, se houver.

Você carrega um ou mais arquivos de configuração, dependendo do endereço da máquina, variáveis ​​de ambiente e valores pares nos arquivos de configuração carregados anteriormente. Depois, basta usar os valores analisados ​​para preencher as configurações.

Uma estratégia que usei com sucesso foi:

  • Carregar um defaults.iniarquivo padrão
  • Verifique o nome da máquina e carregue todos os arquivos que correspondam ao FQDN invertido, da correspondência mais curta à mais longa (então, carreguei net.inie net.domain.ini, em seguida net.domain.webserver01.ini, cada um possivelmente substituindo os valores do anterior). Essa conta também se aplica às máquinas dos desenvolvedores, para que cada um possa configurar seu driver de banco de dados preferido, etc. para desenvolvimento local
  • Verifique se existe um "nome de cluster" declarado e, nesse caso cluster.cluster_name.ini, carregue , o que pode definir coisas como IPs de banco de dados e cache

Como um exemplo de algo que você pode conseguir com isso, você pode definir um valor de "subdomínio" por env, que é usado nas configurações padrão (as hostname: %(subdomain).whatever.net) para definir todos os nomes de host e cookies necessários que o django precisa para funcionar.

É o DRY que eu pude obter, a maioria dos arquivos (existentes) tinha apenas 3 ou 4 configurações. Além disso, eu tinha que gerenciar a configuração do cliente, para que existisse um conjunto adicional de arquivos de configuração (com nomes de banco de dados, usuários e senhas, subdomínio designado etc.), um ou mais por cliente.

Pode-se dimensionar isso para baixo ou alto, conforme necessário, basta colocar no arquivo de configuração as chaves que você deseja configurar por ambiente e, quando houver necessidade de uma nova configuração, colocar o valor anterior na configuração padrão e substituí-lo onde necessário.

Este sistema provou ser confiável e funciona bem com o controle de versão. Ele é usado há muito tempo no gerenciamento de dois clusters de aplicativos separados (15 ou mais instâncias separadas do site django por máquina), com mais de 50 clientes, nos quais os clusters estavam mudando de tamanho e membros, dependendo do humor do administrador do sistema. .

reescrito
fonte
1
Você tem um exemplo de como você carrega as configurações do ini nas configurações do Django?
Kaleissin
Consulte docs.python.org/2/library/configparser.html . Você pode carregar um analisador config = ConfigParser.ConfigParser() e ler seus arquivos config.read(array_of_filenames)e obter valores usando config.get(section, option). Então, primeiro você carrega sua configuração e, em seguida, usa-a para ler os valores das configurações.
reescrito
5

Também estou trabalhando com o Laravel e gosto da implementação lá. Tentei imitá-lo e combiná-lo com a solução proposta por T. Stone (veja acima):

PRODUCTION_SERVERS = ['*.webfaction.com','*.whatever.com',]

def check_env():
    for item in PRODUCTION_SERVERS:
        match = re.match(r"(^." + item + "$)", socket.gethostname())
        if match:
            return True

if check_env():
    PRODUCTION = True
else:
    PRODUCTION = False

DEBUG = not PRODUCTION

Talvez algo assim possa ajudá-lo.

Robert Kuzma
fonte
4

Lembre-se de que settings.py é um arquivo de código ativo. Supondo que você não tenha DEBUG definido na produção (que é uma prática recomendada), você pode fazer algo como:

if DEBUG:
    STATIC_PATH = /path/to/dev/files
else:
    STATIC_PATH = /path/to/production/files

Bastante básico, mas você poderia, em teoria, subir para qualquer nível de complexidade com base apenas no valor de DEBUG - ou em qualquer outra verificação de variável ou código que desejasse usar.

Harper Shelby
fonte
4

Para a maioria dos meus projetos, utilizo o seguinte padrão:

  1. Crie settings_base.py onde armazeno configurações comuns a todos os ambientes
  2. Sempre que preciso usar um novo ambiente com requisitos específicos, crio um novo arquivo de configurações (por exemplo, settings_local.py) que herda o conteúdo de settings_base.py e substitui / adiciona variáveis ​​de configurações apropriadas ( from settings_base import *)

(Para executar manage.py com configurações personalizadas arquivo que você simplesmente usar --settings opção de comando: manage.py <command> --settings=settings_you_wish_to_use.py)

dzida
fonte
3

Minha solução para esse problema também é uma mistura de algumas soluções já declaradas aqui:

  • Eu mantenho um arquivo chamado local_settings.pyque tem o conteúdo USING_LOCAL = Trueem dev e USING_LOCAL = Falseem prod
  • Em settings.pyfaço uma importação nesse arquivo para obter a USING_LOCALconfiguração

Baseei todas as minhas configurações dependentes do ambiente nessa:

DEBUG = USING_LOCAL
if USING_LOCAL:
    # dev database settings
else:
    # prod database settings

Prefiro isso a ter dois arquivos settings.py separados que preciso manter, pois posso manter minhas configurações estruturadas em um único arquivo mais fácil do que tê-las espalhadas por vários arquivos. Assim, quando atualizo uma configuração, não esqueço de fazê-lo nos dois ambientes.

É claro que todo método tem suas desvantagens e este não é uma exceção. O problema aqui é que não posso sobrescrever o local_settings.pyarquivo sempre que coloco minhas alterações em produção, o que significa que não posso copiar todos os arquivos às cegas, mas é algo com o qual posso conviver.

Miguel Ventura
fonte
3

Eu uso uma variação do que jpartogi mencionado acima, que acho um pouco mais curto:

import platform
from django.core.management import execute_manager 

computername = platform.node()

try:
  settings = __import__(computername + '_settings')
except ImportError: 
  import sys
  sys.stderr.write("Error: Can't find the file '%r_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % (computername, __file__))
  sys.exit(1)

if __name__ == "__main__":
  execute_manager(settings)

Basicamente, em cada computador (desenvolvimento ou produção), tenho o arquivo hostname_settings.py apropriado que é carregado dinamicamente.

estratosgear
fonte
3

Há também as configurações elegantes do Django. Eu pessoalmente sou um grande fã disso. Foi construído por uma das pessoas mais ativas no Django IRC. Você usaria vários ambientes para definir as coisas.

http://django-classy-settings.readthedocs.io/en/latest/

SudoKid
fonte
3

1 - Crie uma nova pasta dentro do seu aplicativo e defina as configurações de nome para ele.

2 - Agora crie um novo __init__.pyarquivo nele e dentro dele escreva

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 - Crie três novos arquivos no nome da pasta de configurações local.pye production.pye base.py.

4 - No interior base.py, copie todo o conteúdo da settings.pypasta anterior e renomeie-o com algo diferente, digamos old_settings.py.

5 - Em base.py, altere o caminho BASE_DIR para apontar para o novo caminho de configuração

Caminho antigo-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Novo caminho -> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Dessa forma, o diretório do projeto pode ser estruturado e gerenciável entre produção e desenvolvimento local.

Jack Ryan
fonte
2

Para usar settingsconfigurações diferentes em ambientes diferentes, crie um arquivo de configurações diferentes. E no seu script de implementação, inicie o servidor usando o --settings=<my-settings.py>parâmetro, através do qual você pode usar diferentes configurações em diferentes ambientes.

Benefícios do uso dessa abordagem :

  1. Suas configurações serão modulares com base em cada ambiente

  2. Você pode importar o que master_settings.pycontém a configuração básica environmnet_configuration.pye substituir os valores que deseja alterar nesse ambiente.

  3. Se você possui uma equipe enorme, cada desenvolvedor pode ter o seu, o local_settings.pyqual pode adicionar ao repositório de códigos sem nenhum risco de modificar a configuração do servidor. Você pode adicionar essas configurações locais .gitnorese usar o git ou .hginorese usar o Mercurial for Version Control (ou qualquer outro). Dessa forma, as configurações locais nem farão parte da base de código real, mantendo-a limpa.

Moinuddin Quadri
fonte
2

Eu dividi minhas configurações da seguinte maneira

settings/
     |
     |- base.py
     |- dev.py
     |- prod.py  

Temos 3 ambientes

  • dev
  • encenação
  • Produção

Agora, obviamente, a preparação e a produção devem ter o ambiente semelhante máximo possível. Então ficamos prod.pycom os dois.

Mas houve um caso em que eu tive que identificar o servidor em execução é um servidor de produção. @T. A resposta de Stone me ajudou a escrever o cheque da seguinte forma.

from socket import gethostname, gethostbyname  
PROD_HOSTS = ["webserver1", "webserver2"]

DEBUG = False
ALLOWED_HOSTS = [gethostname(), gethostbyname(gethostname()),]


if any(host in PROD_HOSTS for host in ALLOWED_HOSTS):
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True  
Kishor Pawar
fonte
1

Eu o diferencio em manage.py e criei dois arquivos de configurações separados: local_settings.py e prod_settings.py.

No manage.py, verifico se o servidor é servidor local ou servidor de produção. Se for um servidor local, ele carregaria local_settings.py e, como servidor de produção, carregaria prod_settings.py. Basicamente, é assim que ficaria:

#!/usr/bin/env python
import sys
import socket
from django.core.management import execute_manager 

ipaddress = socket.gethostbyname( socket.gethostname() )
if ipaddress == '127.0.0.1':
    try:
        import local_settings # Assumed to be in the same directory.
        settings = local_settings
    except ImportError:
        import sys
        sys.stderr.write("Error: Can't find the file 'local_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
        sys.exit(1)
else:
    try:
        import prod_settings # Assumed to be in the same directory.
        settings = prod_settings    
    except ImportError:
        import sys
        sys.stderr.write("Error: Can't find the file 'prod_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file prod_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
        sys.exit(1)

if __name__ == "__main__":
    execute_manager(settings)

Achei mais fácil separar o arquivo de configurações em dois arquivos separados, em vez de fazer muitos ifs dentro do arquivo de configurações.

Joshua Partogi
fonte
1

Como alternativa para manter um arquivo diferente, se você quiser: Se você estiver usando o git ou qualquer outro VCS para enviar códigos do local para o servidor, o que você pode fazer é adicionar o arquivo de configurações ao .gitignore.

Isso permitirá que você tenha conteúdo diferente nos dois lugares, sem nenhum problema. SO no servidor, você pode configurar uma versão independente do settings.py e quaisquer alterações feitas no local não refletirão no servidor e vice-versa.

Além disso, ele removerá o arquivo settings.py do github também, a grande falha, que eu já vi muitos novatos fazendo.

sprksh
fonte
0

Eu acho que a melhor solução é sugerida por @T. Stone, mas não sei por que simplesmente não use a flag DEBUG no Django. Escreva o código abaixo para o meu site:

if DEBUG:
    from .local_settings import *

Sempre as soluções simples são melhores que as complexas.

seyedrezafar
fonte
-2

Eu achei as respostas aqui muito úteis. (Isso foi resolvido de forma mais definitiva? A última resposta foi um ano atrás.) Depois de considerar todas as abordagens listadas, criei uma solução que não vi listada aqui.

Meus critérios foram:

  • Tudo deve estar no controle de origem. Eu não gosto de pedaços complicados por aí.
  • Idealmente, mantenha as configurações em um arquivo. Eu esqueço as coisas se eu não estiver olhando direito para elas :)
  • Não há edições manuais para implantar. Deve poder testar / enviar por push / implantar com um único comando de malha.
  • Evite vazar configurações de desenvolvimento para a produção.
  • Mantenha o mais próximo possível do layout Django "padrão" (* tosse *) possível.

Eu pensei que ligar a máquina host fazia algum sentido, mas depois percebi que o problema real aqui é configurações diferentes para ambientes diferentes , e tive um momento aha. Coloquei esse código no final do meu arquivo settings.py:

try:
    os.environ['DJANGO_DEVELOPMENT_SERVER'] # throws error if unset
    DEBUG = True
    TEMPLATE_DEBUG = True
    # This is naive but possible. Could also redeclare full app set to control ordering. 
    # Note that it requires a list rather than the generated tuple.
    INSTALLED_APPS.extend([
        'debug_toolbar',
        'django_nose',
    ])
    # Production database settings, alternate static/media paths, etc...
except KeyError: 
    print 'DJANGO_DEVELOPMENT_SERVER environment var not set; using production settings'

Dessa forma, o aplicativo padroniza as configurações de produção, o que significa que você está explicitamente na "lista de permissões" de seu ambiente de desenvolvimento. É muito mais seguro esquecer de definir a variável de ambiente localmente do que se fosse o contrário e você esqueceu de definir algo na produção e permitir que algumas configurações de desenvolvimento sejam usadas.

Ao desenvolver localmente, a partir do shell ou em um .bash_profile ou em qualquer outro lugar:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(Ou, se você estiver desenvolvendo no Windows, defina através do Painel de Controle ou como é chamado hoje em dia ... O Windows sempre o tornou tão obscuro que você pode definir variáveis ​​de ambiente.

Com essa abordagem, as configurações do desenvolvedor estão todas em um local (padrão) e simplesmente substituem as configurações de produção, quando necessário. Qualquer alteração nas configurações de desenvolvimento deve ser completamente segura para se comprometer com o controle de origem, sem impacto na produção.

Jason Boyd
fonte
Melhor apenas manter diferentes arquivos de configuração, e escolher utilizar o DJANGO_SETTINGS_MODULE variável env padrão Django
Rob Grant