如何在Django中管理本地和生产设置?

问题:如何在Django中管理本地和生产设置?

建议使用什么方式处理本地开发和生产服务器的设置?它们中的某些(例如常量等)都可以更改/访问,但是其中一些(例如静态文件的路径)需要保持不同,因此,每次部署新代码时都不应覆盖它们。

当前,我将所有常量添加到中settings.py。但是每次我在本地更改一些常量时,都必须将其复制到生产服务器并编辑文件以进行生产特定更改… :(

编辑:这个问题似乎没有标准答案,我已经接受了最受欢迎的方法。

What is the recommended way of handling settings for local development and the production server? Some of them (like constants, etc) can be changed/accessed in both, but some of them (like paths to static files) need to remain different, and hence should not be overwritten every time the new code is deployed.

Currently, I am adding all constants to settings.py. But every time I change some constant locally, I have to copy it to the production server and edit the file for production specific changes… :(

Edit: looks like there is no standard answer to this question, I’ve accepted the most popular method.


回答 0

settings.py

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

您可以覆盖local_settings.py;中的需要;然后,它应该脱离版本控制。但是由于您提到了复制,我猜您没有使用;)

In settings.py:

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

You can override what needed in local_settings.py; it should stay out of your version control then. But since you mention copying I’m guessing you use none ;)


回答 1

Django的两个摘要:Django 1.5最佳实践建议对您的设置文件使用版本控制并将文件存储在单独的目录中:

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

base.py文件包含常用的设置(如MEDIA_ROOT或ADMIN),而local.pyproduction.py有网站特定的设置:

在基本文件中settings/base.py

INSTALLED_APPS = (
    # common apps...
)

在本地开发设置文件中settings/local.py

from project.settings.base import *

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

在文件生产设置文件中settings/production.py

from project.settings.base import *

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

然后,在运行django时,添加以下--settings选项:

# 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

该书的作者还在Github上提供了一个示例项目布局模板

Two Scoops of Django: Best Practices for Django 1.5 suggests using version control for your settings files and storing the files in a separate directory:

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

The base.py file contains common settings (such as MEDIA_ROOT or ADMIN), while local.py and production.py have site-specific settings:

In the base file settings/base.py:

INSTALLED_APPS = (
    # common apps...
)

In the local development settings file settings/local.py:

from project.settings.base import *

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

In the file production settings file settings/production.py:

from project.settings.base import *

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

Then when you run django, you add the --settings option:

# 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

The authors of the book have also put up a sample project layout template on Github.


回答 2

代替settings.py使用以下布局:

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

common.py 是大多数配置的所在地。

prod.py 从common导入所有内容,并覆盖需要覆盖的所有内容:

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

# Production overrides
DEBUG = False
#...

同样,dev.py从中导入所有内容common.py并覆盖其需要覆盖的所有内容。

最后,__init__.py是您决定加载哪些设置的地方,也是存储机密的地方(因此,不应对该文件进行版本控制):

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..."

我喜欢这个解决方案的地方是:

  1. 除秘密外,一切都在您的版本控制系统中
  2. 大多数配置都集中在一个位置:common.py
  3. 特定于产品的东西进入了prod.py,特定于开发器的东西进入了dev.py。这很简单。
  4. 您可以从覆盖的东西common.pyprod.py或者dev.py,你可以覆盖任何东西__init__.py
  5. 这是简单易懂的python。没有重新导入的黑客。

Instead of settings.py, use this layout:

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

common.py is where most of your configuration lives.

prod.py imports everything from common, and overrides whatever it needs to override:

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

# Production overrides
DEBUG = False
#...

Similarly, dev.py imports everything from common.py and overrides whatever it needs to override.

Finally, __init__.py is where you decide which settings to load, and it’s also where you store secrets (therefore this file should not be versioned):

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..."

What I like about this solution is:

  1. Everything is in your versioning system, except secrets
  2. Most configuration is in one place: common.py.
  3. Prod-specific things go in prod.py, dev-specific things go in dev.py. It’s simple.
  4. You can override stuff from common.py in prod.py or dev.py, and you can override anything in __init__.py.
  5. It’s straightforward python. No re-import hacks.

回答 3

我使用Harper Shelby发布的“ if DEBUG”样式的设置的稍微修改的版本。显然,取决于环境(win / linux / etc),可能需要对代码进行一些调整。

我以前使用的是“ if DEBUG”,但发现偶尔需要将DEUBG设置为False进行测试。我真正想区分的是环境是生产环境还是开发环境,这使我可以自由选择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'

我仍然会考虑以这种方式设置正在进行的工作。我还没有一种方法可以处理涵盖所有基础的Django设置,但同时也没有设置的麻烦(我对5x设置文件方法并不感到失望)。

I use a slightly modified version of the “if DEBUG” style of settings that Harper Shelby posted. Obviously depending on the environment (win/linux/etc.) the code might need to be tweaked a bit.

I was in the past using the “if DEBUG” but I found that occasionally I needed to do testing with DEUBG set to False. What I really wanted to distinguish if the environment was production or development, which gave me the freedom to choose the DEBUG level.

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'

I’d still consider this way of settings a work in progress. I haven’t seen any one way to handling Django settings that covered all the bases and at the same time wasn’t a total hassle to setup (I’m not down with the 5x settings files methods).


回答 4

我使用settings_local.py和settings_production.py。尝试了几个选项之后,我发现,简单地拥有两个设置文件就容易又快速,很容易在复杂的解决方案上浪费时间。

当对您的Django项目使用mod_python / mod_wsgi时,需要将其指向您的设置文件。如果将其指向本地服务器上的app / settings_local.py和生产服务器上的app / settings_production.py,那么生活会变得很轻松。只需编辑适当的设置文件并重新启动服务器(Django开发服务器将自动重新启动)。

I use a settings_local.py and a settings_production.py. After trying several options I’ve found that it’s easy to waste time with complex solutions when simply having two settings files feels easy and fast.

When you use mod_python/mod_wsgi for your Django project you need to point it to your settings file. If you point it to app/settings_local.py on your local server and app/settings_production.py on your production server then life becomes easy. Just edit the appropriate settings file and restart the server (Django development server will restart automatically).


回答 5

TL; DR:诀窍是os.environment在导入settings/base.py任何文件之前进行修改settings/<purpose>.py,这将大大简化事情。


仅考虑所有这些相互交织的文件,就让我头疼。合并,导入(有时是有条件的),覆盖,修补已设置的内容,以防DEBUG稍后设置更改。什么样的恶梦!

这些年来,我经历了所有不同的解决方案。它们都有些起作用,但是管理起来非常痛苦。WTF!我们真的需要所有麻烦吗?我们从一个settings.py文件开始。现在,我们需要一个文档来将所有这些以正确的顺序正确地组合在一起!

我希望我最终能通过以下解决方法达到(我的)最佳解决方案。

让我们回顾一下目标(一些普通的,一些我的)

  1. 保守秘密-不要将其存储在仓库中!

  2. 通过环境设置(12因子样式)设置/读取密钥和机密。

  3. 具有合理的后备默认设置。理想情况下,对于本地开发,除默认值外,您不需要任何其他内容。

  4. …但请尝试确保默认生产的安全。最好不要在本地错过设置替代,而不必记住要调整默认设置以确保生产安全。

  5. 能够以DEBUG可能影响其他设置的方式打开/关闭(例如,是否使用javascript压缩)。

  6. 在目标设置(例如本地/测试/过渡/生产)之间切换时,仅应基于,而仅此DJANGO_SETTINGS_MODULE而已。

  7. …但是可以通过环境设置(例如)进行进一步的参数设置DATABASE_URL

  8. …还允许他们使用不同的用途设置,并在本地并排运行它们,例如 在本地开发人员机器上进行生产设置,以访问生产数据库或对压缩样式表进行烟雾测试。

  9. 如果未明确设置环境变量(至少需要一个空值),则失败,例如在生产中尤其如此。EMAIL_HOST_PASSWORD

  10. DJANGO_SETTINGS_MODULEdjango-admin startproject期间响应manage.py中的默认设置

  11. 条件语句保持到最低限度,如果条件旨意环境类型(例如,用于生产一系列的日志文件,它的自转),在相关的旨意设置文件中的设置优先。

  1. 不要让django从文件中读取DJANGO_SETTINGS_MODULE设置。
    啊! 想想这是元数据。如果您需要一个文件(例如docker env),请在启动django进程之前将其读入环境。

  2. 不要在您的项目/应用代码中覆盖DJANGO_SETTINGS_MODULE,例如。基于主机名或进程名。
    如果您懒于设置环境变量(例如setup.py test),请在运行项目代码之前在工具中进行设置。

  3. 避免使用django读取其设置的魔术和修补程序,对设置进行预处理,但之后不要干预。

  4. 没有复杂的基于逻辑的废话。配置应该是固定的,不能实时计算。提供备用默认值仅是这里的逻辑。
    您是否真的要调试,为什么要在本地设置正确的设置集,而在远程服务器上的生产环境中(在一台一百台计算机上)计算的结果却有所不同?哦! 单元测试?进行设定?认真吗

我的策略是由优秀的Django的ENVIRON与使用的ini样式文件,提供os.environment当地发展的默认设置,一些最起码的,短settings/<purpose>.py的是有一个文件 import settings/base.py os.environment是从一个设置INI文件。这有效地为我们提供了一种设置注入。

这里的窍门是os.environment在导入之前进行修改settings/base.py

要查看完整的示例,请执行回购: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

设置/.env

本地开发的默认设置。一个秘密文件,主要用于设置所需的环境变量。如果在本地开发中不需要它们,请将它们设置为空值。我们在这里提供默认值,settings/base.py如果环境中缺少其他默认值,则不会在其他任何机器上失败。

设置/ local.py

这里发生的是从中加载环境settings/.env,然后从中导入通用设置settings/base.py。之后,我们可以覆盖一些以简化本地开发。

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

对于生产环境,我们不应该期望环境文件,但是如果我们正在测试某些东西,那么拥有一个环境文件会更容易。但是无论如何,免得内联提供很少的默认值,因此settings/base.py可以做出相应的响应。

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

这里的主要关注点是DEBUGASSETS_DEBUG覆盖,os.environ仅当它们从环境和文件中丢失时,它们才会应用于python 。

这些将是我们的生产默认值,无需将它们放在环境或文件中,但是如果需要可以覆盖它们。整齐!

设置/ base.py

这些是您最常用的django设置,有一些条件和很多从环境中读取它们的条件。几乎所有内容都在这里,使所有目标环境保持一致并尽可能相似。

主要区别如下(我希望这些是自我解释):

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',)

最后一位显示此处的功率。ASSETS_DEBUG有一个合理的默认值,可以将其覆盖settings/production.py,甚至可以由环境设置覆盖!好极了!

实际上,我们具有不同的重要性等级:

  1. settings / .py-根据目的设置默认值,不存储秘密
  2. settings / base.py-主要由环境控制
  3. 过程环境设置-12要素宝贝!
  4. settings / .env-本地默认设置,易于启动

TL;DR: The trick is to modify os.environment before you import settings/base.py in any settings/<purpose>.py, this will greatly simplify things.


Just thinking about all these intertwining files gives me a headache. Combining, importing (sometimes conditionally), overriding, patching of what was already set in case DEBUG setting changed later on. What a nightmare!

Through the years I went through all different solutions. They all somewhat work, but are so painful to manage. WTF! Do we really need all that hassle? We started with just one settings.py file. Now we need a documentation just to correctly combine all these together in a correct order!

I hope I finally hit the (my) sweet spot with the solution below.

Let’s recap the goals (some common, some mine)

  1. Keep secrets a secret — don’t store them in a repo!

  2. Set/read keys and secrets through environment settings, 12 factor style.

  3. Have sensible fallback defaults. Ideally for local development you don’t need anything more beside defaults.

  4. …but try to keep defaults production safe. It’s better to miss a setting override locally, than having to remember to adjust default settings safe for production.

  5. Have the ability to switch DEBUG on/off in a way that can have an effect on other settings (eg. using javascript compressed or not).

  6. Switching between purpose settings, like local/testing/staging/production, should be based only on DJANGO_SETTINGS_MODULE, nothing more.

  7. …but allow further parameterization through environment settings like DATABASE_URL.

  8. …also allow them to use different purpose settings and run them locally side by side, eg. production setup on local developer machine, to access production database or smoke test compressed style sheets.

  9. Fail if an environment variable is not explicitly set (requiring an empty value at minimum), especially in production, eg. EMAIL_HOST_PASSWORD.

  10. Respond to default DJANGO_SETTINGS_MODULE set in manage.py during django-admin startproject

  11. Keep conditionals to a minimum, if the condition is the purposed environment type (eg. for production set log file and it’s rotation), override settings in associated purposed settings file.

Do not’s

  1. Do not let django read DJANGO_SETTINGS_MODULE setting form a file.
    Ugh! Think of how meta this is. If you need to have a file (like docker env) read that into the environment before staring up a django process.

  2. Do not override DJANGO_SETTINGS_MODULE in your project/app code, eg. based on hostname or process name.
    If you are lazy to set environment variable (like for setup.py test) do it in tooling just before you run your project code.

  3. Avoid magic and patching of how django reads it’s settings, preprocess the settings but do not interfere afterwards.

  4. No complicated logic based nonsense. Configuration should be fixed and materialized not computed on the fly. Providing a fallback defaults is just enough logic here.
    Do you really want to debug, why locally you have correct set of settings but in production on a remote server, on one of hundred machines, something computed differently? Oh! Unit tests? For settings? Seriously?

Solution

My strategy consists of excellent django-environ used with ini style files, providing os.environment defaults for local development, some minimal and short settings/<purpose>.py files that have an import settings/base.py AFTER the os.environment was set from an INI file. This effectively give us a kind of settings injection.

The trick here is to modify os.environment before you import settings/base.py.

To see the full example go do the 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

settings/.env

A defaults for local development. A secret file, to mostly set required environment variables. Set them to empty values if they are not required in local development. We provide defaults here and not in settings/base.py to fail on any other machine if the’re missing from the environment.

settings/local.py

What happens in here, is loading environment from settings/.env, then importing common settings from settings/base.py. After that we can override a few to ease local development.

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

For production we should not expect an environment file, but it’s easier to have one if we’re testing something. But anyway, lest’s provide few defaults inline, so settings/base.py can respond accordingly.

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

The main point of interest here are DEBUG and ASSETS_DEBUG overrides, they will be applied to the python os.environ ONLY if they are MISSING from the environment and the file.

These will be our production defaults, no need to put them in the environment or file, but they can be overridden if needed. Neat!

settings/base.py

These are your mostly vanilla django settings, with a few conditionals and lot’s of reading them from the environment. Almost everything is in here, keeping all the purposed environments consistent and as similar as possible.

The main differences are below (I hope these are self explanatory):

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',)

The last bit shows the power here. ASSETS_DEBUG has a sensible default, which can be overridden in settings/production.py and even that that can be overridden by an environment setting! Yay!

In effect we have a mixed hierarchy of importance:

  1. settings/.py – sets defaults based on purpose, does not store secrets
  2. settings/base.py – is mostly controlled by environment
  3. process environment settings – 12 factor baby!
  4. settings/.env – local defaults for easy startup

回答 6

我在django-split-settings的帮助下管理我的配置。

它是默认设置的替代品。它很简单,但可配置。并且不需要重构您的现有设置。

这是一个小示例(文件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()
    )

而已。

更新资料

我写了一篇博客文章,介绍如何使用来管理django设置django-split-sttings。看一看!

I manage my configurations with the help of django-split-settings.

It is a drop-in replacement for the default settings. It is simple, yet configurable. And refactoring of your exisitng settings is not required.

Here’s a small example (file 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()
    )

That’s it.

Update

I wrote a blog post about managing django‘s settings with django-split-sttings. Have a look!


回答 7

这些解决方案中的大多数问题是您在本地设置之前之后应用了本地设置。

因此,不可能覆盖诸如

  • 特定于环境的设置定义了内存缓存池的地址,在主设置文件中,此值用于配置缓存后端
  • 特定于环境的设置将应用程序/中间件添加或删除为默认设置

与此同时。

可以使用带有ConfigParser类的“ ini”样式的配置文件来实现一种解决方案。它支持多个文件,惰性字符串插值,默认值和许多其他功能。一旦加载了许多文件,就可以加载更多文件,并且它们的值将覆盖先前的文件(如果有)。

您加载一个或多个配置文件,具体取决于机器地址,环境变量甚至以前加载的配置文件中的值。然后,您只需使用解析后的值来填充设置。

我成功使用的一种策略是:

  • 加载默认defaults.ini文件
  • 检查机器名称,并加载与反向FQDN匹配的所有文件,从最短匹配到最长匹配(因此,我先加载net.ini,然后加载,然后net.domain.ini再加载net.domain.webserver01.ini,每个可能覆盖前一个值)。此帐户也用于开发人员的计算机,因此每个人都可以设置其首选的数据库驱动程序等以进行本地开发
  • 检查是否声明了“集群名称”,在这种情况下cluster.cluster_name.ini,请检查load ,它可以定义数据库和缓存IP之类的内容

作为可以实现此目标的示例,您可以为每个环境定义一个“子域”值,然后在默认设置(如hostname: %(subdomain).whatever.net)中使用它来定义django需要工作的所有必需的主机名和cookie。

这是我可以得到的DRY,大多数(现有)文件只有3或4个设置。最重要的是,我必须管理客户配置,因此存在另外一组配置文件(带有数据库名称,用户和密码,分配的子域等),每个客户一个或多个。

可以根据需要将其缩放为低或高,您只需将要在每个环境中配置的密钥放入配置文件中,然后在需要新配置时,将先前的值放入默认配置中,然后覆盖它即可。在必要时。

该系统已经证明是可靠的,并且可以与版本控制一起很好地工作。它已长期用于管理两个单独的应用程序集群(每台机器15个或更多django站点的单独实例),拥有50多个客户,这些集群根据sysadmin的心情来改变大小和成员。 。

The problem with most of these solutions is that you either have your local settings applied before the common ones, or after them.

So it’s impossible to override things like

  • the env-specific settings define the addresses for the memcached pool, and in the main settings file this value is used to configure the cache backend
  • the env-specific settings add or remove apps/middleware to the default one

at the same time.

One solution can be implemented using “ini”-style config files with the ConfigParser class. It supports multiple files, lazy string interpolation, default values and a lot of other goodies. Once a number of files have been loaded, more files can be loaded and their values will override the previous ones, if any.

You load one or more config files, depending on the machine address, environment variables and even values in previously loaded config files. Then you just use the parsed values to populate the settings.

One strategy I have successfully used has been:

  • Load a default defaults.ini file
  • Check the machine name, and load all files which matched the reversed FQDN, from the shortest match to the longest match (so, I loaded net.ini, then net.domain.ini, then net.domain.webserver01.ini, each one possibly overriding values of the previous). This account also for developers’ machines, so each one could set up its preferred database driver, etc. for local development
  • Check if there is a “cluster name” declared, and in that case load cluster.cluster_name.ini, which can define things like database and cache IPs

As an example of something you can achieve with this, you can define a “subdomain” value per-env, which is then used in the default settings (as hostname: %(subdomain).whatever.net) to define all the necessary hostnames and cookie things django needs to work.

This is as DRY I could get, most (existing) files had just 3 or 4 settings. On top of this I had to manage customer configuration, so an additional set of configuration files (with things like database names, users and passwords, assigned subdomain etc) existed, one or more per customer.

One can scale this as low or as high as necessary, you just put in the config file the keys you want to configure per-environment, and once there’s need for a new config, put the previous value in the default config, and override it where necessary.

This system has proven reliable and works well with version control. It has been used for long time managing two separate clusters of applications (15 or more separate instances of the django site per machine), with more than 50 customers, where the clusters were changing size and members depending on the mood of the sysadmin…


回答 8

我也在与Laravel合作,我喜欢在那里的实现。我试图模仿它,并将其与T.Stone提出的解决方案结合起来(见上):

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

也许这样的事情会帮助您。

I am also working with Laravel and I like the implementation there. I tried to mimic it and combining it with the solution proposed by T. Stone (look above):

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

Maybe something like this would help you.


回答 9

请记住,settings.py是实时代码文件。假设您没有在生产环境中设置DEBUG(这是最佳做法),则可以执行以下操作:

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

这很基本,但是从理论上讲,您可以仅根据DEBUG的值-或要使用的任何其他变量或代码检查,将复杂性提高到任何水平。

Remember that settings.py is a live code file. Assuming that you don’t have DEBUG set on production (which is a best practice), you can do something like:

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

Pretty basic, but you could, in theory, go up to any level of complexity based on just the value of DEBUG – or any other variable or code check you wanted to use.


回答 10

对于我的大多数项目,我使用以下模式:

  1. 在我存储所有环境通用设置的位置创建settings_base.py
  2. 每当需要使用具有特定要求的新环境时,我都会创建一个新的设置文件(例如settings_local.py),该文件将继承settings_base.py的内容并覆盖/添加适当的设置变量(from settings_base import *

(要使用自定义设置运行manage.py文件只需使用–settings命令选项:manage.py <command> --settings=settings_you_wish_to_use.py

For most of my projects I use following pattern:

  1. Create settings_base.py where I store settings that are common for all environments
  2. Whenever I need to use new environment with specific requirements I create new settings file (eg. settings_local.py) which inherits contents of settings_base.py and overrides/adds proper settings variables (from settings_base import *)

(To run manage.py with custom settings file you simply use –settings command option: manage.py <command> --settings=settings_you_wish_to_use.py)


回答 11

我对这个问题的解决方案在某种程度上也是这里已经提到的一些解决方案的混合:

  • 我保留了一个名为dev和prod中local_settings.py内容的文件USING_LOCAL = TrueUSING_LOCAL = False
  • settings.py我对该文件进行导入以获取USING_LOCAL设置

然后,我将所有与环境相关的设置都基于该设置:

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

我宁愿拥有两个需要维护的单独的settings.py文件,因为与将它们分散在多个文件中相比,可以将设置结构化为一个文件。这样,当我更新设置时,我不会忘记在两种环境下都进行设置。

当然,每种方法都有其缺点,这一方法也不exceptions。这里的问题是,local_settings.py每当我将更改推送到生产环境时,我都无法覆盖文件,这意味着我不能盲目地复制所有文件,但这是我可以忍受的。

My solution to that problem is also somewhat of a mix of some solutions already stated here:

  • I keep a file called local_settings.py that has the content USING_LOCAL = True in dev and USING_LOCAL = False in prod
  • In settings.py I do an import on that file to get the USING_LOCAL setting

I then base all my environment-dependent settings on that one:

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

I prefer this to having two separate settings.py files that I need to maintain as I can keep my settings structured in a single file easier than having them spread across several files. Like this, when I update a setting I don’t forget to do it for both environments.

Of course that every method has its disadvantages and this one is no exception. The problem here is that I can’t overwrite the local_settings.py file whenever I push my changes into production, meaning I can’t just copy all files blindly, but that’s something I can live with.


回答 12

我使用了上面提到的jpartogi的一种变体,发现它略短一些:

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)

基本上在每台计算机(开发或生产)上,我都有适当的hostname_settings.py文件,该文件会动态加载。

I use a variation of what jpartogi mentioned above, that I find a little shorter:

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)

Basically on each computer (development or production) I have the appropriate hostname_settings.py file that gets dynamically loaded.


回答 13

也有Django Classy Settings。我个人是它的忠实拥护者。它是由Django IRC上最活跃的人之一构建的。您将使用环境变量进行设置。

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

There is also Django Classy Settings. I personally am a big fan of it. It’s built by one of the most active people on the Django IRC. You would use environment vars to set things.

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


回答 14

1-在您的应用程序内创建一个新文件夹,并为其命名设置。

2-现在__init__.py在其中创建一个新文件,并在其中写入

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 -创建在设置三个新文件夹的名称local.pyproduction.pybase.py

4-在内部base.py,复制上一个settings.py文件夹的所有内容,并用其他不同的名称重命名old_settings.py

5-在base.py中,更改BASE_DIR路径以指向新的设置路径

旧路径-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

新路径-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

这样,可以在生产和本地开发之间构建项目目录并对其进行管理。

1 – Create a new folder inside your app and name settings to it.

2 – Now create a new __init__.py file in it and inside it write

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 – Create three new files in the settings folder name local.py and production.py and base.py.

4 – Inside base.py, copy all the content of previous settings.py folder and rename it with something different, let’s say old_settings.py.

5 – In base.py change your BASE_DIR path to point to your new path of setting

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

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

This way, the project dir can be structured and can be manageable among production and local development.


回答 15

为了settings在不同的环境上使用不同的配置,请创建不同的设置文件。然后,在部署脚本中,使用--settings=<my-settings.py>参数启动服务器,通过该参数可以在不同的环境上使用不同的设置

使用这种方法的好处

  1. 您的设置将根据每个环境进行模块化

  2. 您可以在中导入master_settings.py包含基本配置的内容,environmnet_configuration.py并覆盖要在该环境中更改的值。

  3. 如果您有庞大的团队,则每个开发人员可能都有自己的团队,可以将其local_settings.py添加到代码存储库中,而无需修改服务器配置。您可以添加这些本地设置,.gitnore如果您使用的git或者.hginore,如果你的Mercurial版本控制(或任何其他)。这样,本地设置甚至不会成为保持干净的实际代码库的一部分。

In order to use different settings configuration on different environment, create different settings file. And in your deployment script, start the server using --settings=<my-settings.py> parameter, via which you can use different settings on different environment.

Benefits of using this approach:

  1. Your settings will be modular based on each environment

  2. You may import the master_settings.py containing the base configuration in the environmnet_configuration.py and override the values that you want to change in that environment.

  3. If you have huge team, each developer may have their own local_settings.py which they can add to the code repository without any risk of modifying the server configuration. You can add these local settings to .gitnore if you use git or .hginore if you Mercurial for Version Control (or any other). That way local settings won’t even be the part of actual code base keeping it clean.


回答 16

我的设置如下拆分

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

我们有3个环境

  • 开发者
  • 分期
  • 生产

现在显然,登台和生产应该具有最大可能的相似环境。所以我们都坚持prod.py

但是在某些情况下,我不得不确定正在运行的服务器是生产服务器。@T。斯通的答案帮助我写了以下支票。

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  

I had my settings split as follows

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

We have 3 environments

  • dev
  • staging
  • production

Now obviously staging and production should have the maximum possible similar environment. So we kept prod.py for both.

But there was a case where I had to identify running server is a production server. @T. Stone ‘s answer helped me write check as follows.

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  

回答 17

我在manage.py中对其进行区分,并创建了两个单独的设置文件:local_settings.py和prod_settings.py。

在manage.py中,我检查服务器是本地服务器还是生产服务器。如果是本地服务器,则将加载local_settings.py,如果是生产服务器,则将加载prod_settings.py。基本上是这样的:

#!/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)

我发现将设置文件分为两个单独的文件比在设置文件中进行大量的ifs更为容易。

I differentiate it in manage.py and created two separate settings file: local_settings.py and prod_settings.py.

In manage.py I check whether the server is local server or production server. If it is a local server it would load up local_settings.py and it is a production server it would load up prod_settings.py. Basically this is how it would look like:

#!/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)

I found it to be easier to separate the settings file into two separate file instead of doing lots of ifs inside the settings file.


回答 18

如果愿意,也可以选择维护其他文件:如果使用git或任何其他VCS将代码从本地推送到服务器,则可以将设置文件添加到.gitignore。

这样您就可以在两个地方都拥有不同的内容,而不会出现任何问题。因此,在服务器上,您可以配置独立版本的settings.py,对本地所做的任何更改都不会反映在服务器上,反之亦然。

另外,它还会从github上删除settings.py文件,这是一个很大的错误,我看到很多新手都在这样做。

As an alternative to maintain different file if you wiil: If you are using git or any other VCS to push codes from local to server, what you can do is add the settings file to .gitignore.

This will allow you to have different content in both places without any problem. SO on server you can configure an independent version of settings.py and any changes made on the local wont reflect on server and vice versa.

In addition, it will remove the settings.py file from github also, the big fault, which i have seen many newbies doing.


回答 19

制作settings.py的多个版本是12 Factor App方法的反模式。使用python-decoupledjango-environ代替。

Making multiple versions of settings.py is an anti pattern for 12 Factor App methodology. use python-decouple or django-environ instead.


回答 20

我认为最好的解决方案是@T建议的。斯通,但我不知道为什么不在Django中使用DEBUG标志。我为我的网站编写以下代码:

if DEBUG:
    from .local_settings import *

总是简单的解决方案比复杂的解决方案好。

I think the best solution is suggested by @T. Stone, but I don’t know why just don’t use the DEBUG flag in Django. I Write the below code for my website:

if DEBUG:
    from .local_settings import *

Always the simple solutions are better than complex ones.


回答 21

我发现这里的回复非常有帮助。(是否已更明确地解决了此问题?上次答复是一年前。)在考虑了列出的所有方法之后,我想出了一个我未在此处列出的解决方案。

我的标准是:

  • 一切都应该在源代码控制中。我不喜欢随便摆姿势。
  • 理想情况下,将设置保存在一个文件中。如果我没看对的话我会忘记的:)
  • 无需手动编辑即可部署。应该能够使用单个结构命令进行测试/推送/部署。
  • 避免将开发设置泄漏到生产中。
  • 保持尽可能接近“标准”(*咳嗽*)Django布局。

我以为打开主机是有道理的,但后来发现这里的真正问题是针对不同环境的不同设置,并且花了很多时间。我把这个代码在结束我的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'

这样,该应用程序默认为生产设置,这意味着您将开发环境明确“列入白名单”。与在相反的情况下忘记在本地设置环境变量相比,如果忘记在生产环境中设置某些内容并使用某些开发设置,则要安全得多。

从外壳或.bash_profile或任何地方进行本地开发时:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(或者,如果您是在Windows上进行开发,则可以通过“控制面板”或目前称为“控制面板”的工具进行设置。Windows总是使它晦涩难懂,因此您可以设置环境变量。)

使用这种方法,开发人员设置全部集中在一个(标准)位置,并在需要时仅覆盖生产设置。任何与开发设置有关的更改都应该完全安全地进行源代码控制,而不会影响生产。

I found the responses here very helpful. (Has this been more definitively solved? The last response was a year ago.) After considering all the approaches listed, I came up with a solution that I didn’t see listed here.

My criteria were:

  • Everything should be in source control. I don’t like fiddly bits lying around.
  • Ideally, keep settings in one file. I forget things if I’m not looking right at them :)
  • No manual edits to deploy. Should be able to test/push/deploy with a single fabric command.
  • Avoid leaking development settings into production.
  • Keep as close as possible to “standard” (*cough*) Django layout as possible.

I thought switching on the host machine made some sense, but then figured the real issue here is different settings for different environments, and had an aha moment. I put this code at the end of my settings.py file:

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'

This way, the app defaults to production settings, which means you are explicitly “whitelisting” your development environment. It is much safer to forget to set the environment variable locally than if it were the other way around and you forgot to set something in production and let some dev settings be used.

When developing locally, either from the shell or in a .bash_profile or wherever:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(Or if you’re developing on Windows, set via the Control Panel or whatever its called these days… Windows always made it so obscure that you could set environment variables.)

With this approach, the dev settings are all in one (standard) place, and simply override the production ones where needed. Any mucking around with development settings should be completely safe to commit to source control with no impact on production.