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

处理本地开发和生产服务器设置的build议方法是什么? 它们中的一些(如常量等)可以在两者中进行更改/访问,但其中一些(如静态文件的path)需要保持不同,因此每次部署新代码时都不应该被覆盖。

目前,我将所有常量添加到settings.py 。 但每当我在本地更改一些常量时,我​​必须将其复制到生产服务器并编辑生产特定更改的文件… 🙁

编辑:看起来像这个问题没有标准答案,我接受了最stream行的方法。

settings.py

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

你可以覆盖local_settings.py需要的local_settings.py ; 它应该保持你的版本控制,然后。 但是既然你提到复制我猜你没有使用;)

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

 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上提供了一个示例项目布局模板 。

而不是settings.py ,使用这个布局:

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

common.py是大多数configuration所在的地方。

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

 from __future__ import absolute_import # optional, but I like it from .common import * # Production overrides DEBUG = False #... 

同样, dev.pycommon.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..." 

我喜欢这个解决scheme是:

  1. 一切都在你的版本系统中,除了秘密
  2. 大多数configuration位于一个地方: common.py
  3. Prod特定的东西进入prod.py ,开发的具体事情去dev.py 这很简单。
  4. 您可以覆盖prod.pydev.py common.py dev.py ,并且可以覆盖__init__.py任何内容。
  5. 这是直截了当的Python。 没有重新导入黑客。

我使用Harper Shelby发布的“if DEBUG”风格的设置稍作修改。 很明显,取决于环境(win / linux / etc),代码可能需要稍微调整一下。

我曾经在使用“if DEBUG”,但是我发现偶尔需要将DEUBG设置为False。 如果环境是生产环境还是开发环境,我真的很想区分这个环境,这让我可以自由selectDEBUG级别。

 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设置文件的方法)。

我使用了settings_local.py和settings_production.py。 在尝试了几个选项之后,我发现当简单地使两个设置文件变得简单和快速时,用复杂的解决scheme浪费时间是很容易的。

当你为你的Django项目使用mod_python / mod_wsgi时,你需要把它指向你的设置文件。 如果将其指向本地服务器上的app / settings_local.py和生产服务器上的app / settings_production.py,则生活将变得非常简单。 只需编辑适当的设置文件并重新启动服务器(Django开发服务器将自动重启)。

大多数解决scheme的问题在于,您要么在常用的设置之前应用本地设置,要么在之后应用本地设置。

所以这是不可能的

  • 特定于env的设置定义了memcached池的地址,在主设置文件中,该值用于configurationcaching后端
  • 环境特定的设置添加或删除应用程序/中间件到默认的设置

与此同时。

一个解决scheme可以使用ConfigParser类使用“ini”风格的configuration文件来实现。 它支持多个文件,懒惰的string插值,默认值和其他许多好东西。 一旦加载了多个文件,可以加载更多的文件,并且它们的值将覆盖以前的文件(如果有的话)。

您可以加载一个或多个configuration文件,具体取决于机器地址,环境variables以及之前加载的configuration文件中的值。 然后,您只需使用parsing的值来填充设置。

我已经成功使用的一个策略是:

  • 加载默认的defaults.ini文件
  • 检查机器名称,并加载所有匹配反向FQDN的文件,从最短匹配到最长匹配(所以,我加载net.ini ,然后net.domain.ini ,然后net.domain.webserver01.ini ,每一个可能压倒前面的值)。 这个帐号也是开发者的机器,所以每个人都可以设置自己喜欢的数据库驱动等等,供本地开发
  • 检查是否声明了“集群名称”,在这种情况下加载cluster.cluster_name.ini ,它可以定义数据库和cachingIP

作为你可以实现的一个例子,你可以定义一个“subdomain”值per-env,然后在默认设置中使用(如hostname: %(subdomain).whatever.net )来定义所有必要的主机名和cookiesdjango需要工作。

这是干我可以得到,大多数(现有)文件只有3或4个设置。 除此之外,我必须pipe理客户configuration,因此还有一组configuration文件(包括数据库名称,用户和密码,分配的子域等等)存在,每个客户一个或多个configuration文件。

你可以根据需要调低或调高,你只需要在configuration文件中input你想要configuration的密钥,一旦需要一个新的configuration,把之前的值放在默认configuration中,然后覆盖它在必要时。

这个系统已被certificate是可靠的,并且与版本控制一起使用。 它已经被用来pipe理两个独立的应用程序集群(每个机器有15个或更多个单独的django站点实例),拥有50多个客户,其中集群根据系统pipe理员的意愿改变了大小和成员。 。

我在django-split-settings的帮助下pipe理我的configuration。

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

下面是一个小例子(文件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-split-sttingspipe理django的设置的博客文章 。 看一看!

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

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

非常基本的,但是理论上,你可以根据DEBUG的价值或任何你想要使用的任何其他variables或代码检查来达到任何复杂程度。

我也在和Laravel合作,我喜欢那里的实施。 我试图模仿它,并将其与T. Stone所提出的解决scheme相结合(请看上面):

 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 

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

我对这个问题的解决办法也有些混合了这里已经提到的一些解决scheme:

  • 我保留一个名为local_settings.py的文件,该文件的内容USING_LOCAL = True在开发中USING_LOCAL = False
  • settings.py我做一个导入该文件来获得USING_LOCAL设置

然后,我将所有依赖于环境的设置都置于该设置上:

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

我更喜欢使用两个独立的settings.py文件,因为我可以将我的设置保存在单个文件中,而不是将它们分散到多个文件中。 像这样,当我更新一个设置时,我不会忘记在两个环境中都这样做。

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

对于我的大部分项目,我使用以下模式:

  1. 创buildsettings_base.py,在其中存储所有环境通用的设置
  2. 每当我需要使用具有特定要求的新环境时,我创build新的设置文件(例如settings_local.py),它inheritancesettings_base.py的内容,并覆盖/添加适当的设置variables( from settings_base import *

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

我使用上面提到的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文件被dynamic加载。

还有Django的优雅设置。 我个人很喜欢它。 它是由Django IRC上最活跃的人员之一构build的。 你会使用环境variables来设置的东西。

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

我将其区分为manage.py并创build了两个单独的设置文件: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。

如果您愿意,可以select保留不同的文件:如果您使用git或其他VCS将代码从本地推送到服务器,您可以将设置文件添加到.gitignore。

这将允许你在两个地方有不同的内容没有任何问题。 所以在服务器上,你可以configuration一个独立版本的settings.py,在本地做的任何修改都不会反映在服务器上,反之亦然。

另外,它还会从github中删除settings.py文件,这是我看到很多新手做的大错。

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

使用这种方法的好处

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

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

  3. 如果您拥有庞大的团队,每个开发人员都可以拥有自己的local_settings.py ,他们可以将其添加到代码存储库,而不会有任何修改服务器configuration的风险。 如果您使用git.hginore则可以将这些本地设置添加到.gitnore如果是Mercurial for Version Control (或其他任何版本 ))。 这样,本地设置甚至不会成为实际代码保持清洁的一部分。

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


想想所有这些交织在一起的文件让我头疼。 结合,导入(有时是有条件的),覆盖,修改事件已经设置的情况下DEBUG设置稍后改变。 什么样的恶梦!

多年来,我经历了所有不同的解决scheme。 他们都有点工作,但是很难pipe理。 WTF! 我们真的需要一切麻烦吗? 我们从一个settings.py文件开始。 现在我们需要一个文档来正确地将所有这些按照正确的顺序组合在一起!

我希望我终于用下面的解决scheme击中(我)的甜蜜点。

让我们回顾一下目标(一些常见的,一些是我的)

  1. 保守秘密 – 不要将它们存储在回购中。

  2. 通过环境设置, 12个因子风格设置/读取密钥和秘密。

  3. 有明智的后备默认值。 对于本地开发来说,理想情况下,除了默认设置外,不需要任何其他的东西

  4. …但尝试保持默认生产安全。 最好错过本地的设置覆盖,而不必忘记调整安全生产的默认设置。

  5. 有能力打开/closuresDEBUG的方式,可以影响其他设置(例如使用JavaScript压缩或不)。

  6. 在本地/ testing / staging / production之类的用途设置之间切换应该仅仅基于DJANGO_SETTINGS_MODULE ,仅此而已。

  7. …但通过像DATABASE_URL这样的环境设置允许进一步的参数化。

  8. …也允许他们使用不同的目的设置并在本地并排运行,例如。 在本地开发者机器上进行生产设置,访问生产数据库或烟雾testing压缩样式表。

  9. 如果一个环境variables没有明确设置(最低要求一个空的值),尤其是在生产环境中会失败。 EMAIL_HOST_PASSWORD

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

  11. 如果条件是目标环境types(例如,生产集logging文件和它的旋转),则将条件保持为最小值,覆盖相关目的设置文件中的设置。

不要

  1. 不要让Django从文件中读取DJANGO_SETTINGS_MODULE设置。
    啊! 想想这是多么的元。 如果你需要有一个文件(像docker env)在开始Django进程之前将其读入环境。

  2. 不要在项目/应用程序代码中覆盖DJANGO_SETTINGS_MODULE,例如。 基于主机名或进程名称。
    如果你懒得设置环境variables(比如setup.py test ),那么就在你运行你的项目代码之前在工具中进行。

  3. 避免魔术和补丁如何Django读取它的设置,预处理设置,但不会干预之后。

  4. 没有复杂的逻辑基础的废话。 configuration应该是固定的,并不是实时计算的。 提供回退默认值就是足够的逻辑。
    你真的想debugging,为什么本地你有一套正确的设置,但在远程服务器生产,在百台机器之一,有不同的计算? 哦! unit testing? 对于设置? 真的吗?

我的策略包括用于ini样式文件的优秀django-environ ,为本地开发提供os.environment默认值,一些最小化和简短的settings/<purpose>.py文件,在os.environment 之后有一个import settings/base.pyINI文件中设置。 这有效地给了我们一种设置注入。

这里的技巧是在导入settings/base.py之前修改os.environment

要查看完整的示例,请执行回购: https : //github.com/wooyek/django-settings-strategy

 . │ manage.py ├───data └───website ├───settings │ │ __init__.py <-- imports local for compatybility │ │ base.py <-- almost all the settings, reads from proces environment │ │ local.py <-- a few modifications for local development │ │ production.py <-- ideally is empy 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

默认为本地开发。 一个秘密文件,主要是设置所需的环境variables。 如果它们在本地开发中不需要,则将它们设置为空值。 我们在这里提供默认值,而不是在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 

设置/ production.py

对于生产,我们不应该期望一个环境文件,但是如果我们正在testing某个东西,那么更容易拥有一个。 但是无论如何,为了settings/base.py内联提供一些默认设置,所以settings/base.py可以做出相应的响应。

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

这里感兴趣的主要是DEBUGASSETS_DEBUG覆盖,如果它们是从环境和文件丢失,它们将被应用到python os.environ

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

设置/ 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',) 

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! 好极了!

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

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: 

通过

  try: 

from .production import *

  except: 

通过

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.p folder and rename it with something different let 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 )))

now in this way the project dir can be structured and can be manageable among production and local development.

I had my settings split as follows

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

We have 3 environments

  • 开发
  • 分期
  • 生产

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 

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.