关于python:如何在django中管理本地与生产设置?

How to manage local vs production settings in Django?

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

目前,我正在将所有常量添加到settings.py。但每次我在本地更改某个常量时,我都必须将其复制到生产服务器,并为生产特定的更改编辑文件…:(

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


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

1
2
3
4
5
6
7
8
9
10
11
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中:

1
2
3
INSTALLED_APPS = (
    # common apps...
)

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

1
2
3
4
5
6
from project.settings.base import *

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

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

1
2
3
4
5
6
from project.settings.base import *

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

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

1
2
3
4
5
# 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中:

1
2
3
4
try:
    from local_settings import *
except ImportError as e:
    pass

您可以覆盖local_settings.py中所需的内容;那么它应该不受版本控制。但既然你提到复制,我猜你一点用都不用;)


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

1
2
3
4
5
6
.
└── settings/
 &nbsp;&nbsp; ├── __init__.py  <= not versioned
 &nbsp;&nbsp; ├── common.py
 &nbsp;&nbsp; ├── dev.py
 &nbsp;&nbsp; └── prod.py

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

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

1
2
3
4
5
6
from __future__ import absolute_import # optional, but I like it
from .common import *

# Production overrides
DEBUG = False
#...

同样,dev.pycommon.py导入所有内容,并覆盖需要覆盖的内容。

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

1
2
3
4
5
6
7
8
9
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..."

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

  • 所有东西都在你的版本控制系统中,除了秘密
  • 大多数配置都在一个地方:common.py
  • 特定于产品的东西放在prod.py里,特定于开发的东西放在dev.py里。这很简单。
  • 您可以覆盖prod.pydev.pycommon.py的内容,也可以覆盖__init__.py中的任何内容。
  • 这是一条直截了当的Python。没有重新导入的黑客。

  • 我使用了哈珀·谢尔比发布的稍微修改过的"if-debug"设置样式。显然,根据环境(win/linux/etc),代码可能需要稍微调整一下。

    我以前使用过"if-debug",但我发现有时候我需要使用deubg设置为false进行测试。我真正想要区分的是环境是生产还是开发,这给了我选择调试级别的自由。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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设置文件方法)。


    我使用设置"local.py"和设置"production.py"。在尝试了几个选项后,我发现,当简单地拥有两个设置文件时,很容易在复杂的解决方案中浪费时间。

    在Django项目中使用mod_python/mod_wsgi时,需要将其指向设置文件。如果您将其指向本地服务器上的app/settings_local.py和生产服务器上的app/settings_production.py,那么生活就变得简单了。只需编辑适当的设置文件并重新启动服务器(Django Development Server将自动重新启动)。


    我使用Django拆分设置管理配置。

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

    下面是一个小例子(文件example/settings/__init__.py):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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-sttings管理django的设置的博客文章。看一看!


    大多数这些解决方案的问题在于,要么在普通解决方案之前或之后应用本地设置。

    所以不可能超越

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

    同时。

    一个解决方案可以使用带有configParser类的"ini"样式的配置文件来实现。它支持多个文件、延迟字符串插值、默认值和许多其他优点。一旦加载了多个文件,就可以加载更多的文件,如果有的话,这些文件的值将覆盖以前的文件。

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

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

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

    作为一个可以用这个实现的示例,您可以为每个env定义一个"子域"值,然后在默认设置(如hostname: %(subdomain).whatever.net中)中使用该值来定义Django需要工作的所有必要主机名和cookie。

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

    您可以根据需要将其调低或调高,只需在配置文件中输入要为每个环境配置的键,一旦需要新的配置,就将以前的值调到默认配置中,并在必要时覆盖它。

    该系统已被证明是可靠的,并与版本控制工作良好。它已经用于管理两个独立的应用程序集群(每台机器15个或更多独立的django站点实例),拥有50多个客户,其中集群的大小和成员根据系统管理员的心情而变化……


    tl;dr:诀窍是在任何settings/.py中导入settings/base.py之前修改os.environment,这将大大简化事情。好的。

    想到这些相互缠绕的文件,我就头疼。合并、导入(有时是有条件的)、重写、修补已设置的内容,以防稍后DEBUG设置更改。真是个噩梦!好的。

    这些年来,我经历了所有不同的解决方案。它们都有点作用,但管理起来很痛苦。世界跆拳道联盟!我们真的需要这么多麻烦吗?我们从一个settings.py文件开始。现在我们需要一个文档来正确地将所有这些按正确的顺序组合在一起!好的。

    我希望我终于找到了下面的解决方案。好的。让我们回顾一下目标(一些共同的,一些我的)

  • 保守秘密-不要把它们储存在回购协议中。好的。

  • 通过环境设置设置/读取密钥和机密,12因子样式。好的。

  • 具有合理的回退默认值。对于本地开发来说,除了默认值之外,您不需要其他任何东西。好的。

  • …但是尽量保证默认的生产安全。最好在本地忽略设置覆盖,比必须记住调整默认设置安全生产。好的。

  • 能够以对其他设置有影响的方式(例如,是否使用Javascript压缩)打开/关闭DEBUG。好的。

  • 在目的设置之间的切换,如本地/测试/分段/生产,应该只基于DJANGO_SETTINGS_MODULE,而不是更多。好的。

  • …但是允许通过环境设置进行进一步的参数化,如DATABASE_URL。好的。

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

  • 如果没有显式设置环境变量(至少需要空值),特别是在生产环境中,例如EMAIL_HOST_PASSWORD,则失败。好的。

  • 在Django管理启动项目期间响应在manage.py中设置的默认DJANGO_SETTINGS_MODULE。好的。

  • 将条件保持在最小值,如果条件是目标环境类型(例如,对于生产集日志文件及其旋转),则覆盖关联目标设置文件中的设置。好的。

  • 不要

  • 不要让django读取django_设置_模块设置形成文件。呸!想想这是多么的元。如果你需要一个文件(比如Dockerenv)在启动Django进程之前将其读到环境中。好的。

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

  • 避免魔术和补丁如何德扬戈读取它的设置,预处理设置,但不要干涉以后。好的。

  • 没有复杂的逻辑基础的废话。配置应该是固定的和物化的,而不是动态计算的。提供一个回退默认值只是这里的逻辑而已。您真的想调试,为什么在本地设置正确,但在远程服务器上生产,在一百台机器中,有什么计算方法不同吗?哦!单元测试?设置?真的吗?好的。

  • 解决方案

    我的策略包括与ini样式的文件一起使用的出色的django environ,为本地开发提供os.environment默认值,一些最小和较短的settings/.py文件具有从ini文件设置os.environment后的import settings/base.py。这实际上给了我们一种设置注入。好的。

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    .
    │   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

    设置/

    本地开发的默认值。一个秘密文件,主要用于设置所需的环境变量。如果在本地开发中不需要这些值,请将它们设置为空值。我们在这里提供缺省值,而不是在settings/base.py中提供缺省值,以便在环境中缺少的任何其他机器上发生故障。好的。设置/local.py

    这里发生的是从settings/.env加载环境,然后导入公共设置来自settings/base.py。在那之后,我们可以覆盖一些以简化本地开发。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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

    设置/生产.py

    对于生产,我们不应该期望有一个环境文件,但是如果我们在测试一些东西,就更容易有一个。但是无论如何,LEST提供了很少的内联默认值,因此settings/base.py可以相应地作出响应。好的。

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

    这里的主要关注点是DEBUGASSETS_DEBUG覆盖,只有在环境和文件中缺少它们时,它们才会应用于python os.environ。好的。

    这些将是我们的生产默认值,不需要将它们放在环境或文件中,但如果需要,可以覆盖它们。整洁!好的。设置/base.py

    这些都是您最普通的django设置,有一些条件和很多从环境中读取它们。几乎所有的东西都在这里,保持所有目标环境的一致性和尽可能相似。好的。

    主要区别如下(我希望这些是不言而喻的):好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    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)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    # 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'),
                }
            }
        }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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中被覆盖,甚至可以被环境设置覆盖!哎呀!好的。

    实际上,我们有一个复杂的重要性层次:好的。

  • settings/.py-根据目的设置默认值,不存储机密
  • settings/base.py-主要由环境控制
  • 过程环境设置-12因子宝贝!
  • settings/.env-轻松启动的本地默认值
  • 好啊。


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

    1
    2
    3
    4
    if DEBUG:
        STATIC_PATH = /path/to/dev/files
    else:
        STATIC_PATH = /path/to/production/files

    非常基本,但是理论上,您可以根据调试的值或您想要使用的任何其他变量或代码检查,达到任何复杂程度。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

    也许像这样的东西会对你有所帮助。


    我使用了JPartogi上面提到的一个变体,我发现它有点短:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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.
    You'll have to run django-admin.py, passing it your settings module.
    (If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)
    "
    % (computername, __file__))
      sys.exit(1)

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

    基本上,在每台计算机(开发或生产)上,我都有相应的主机名_settings.py文件,可以动态加载。


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

  • 创建设置_base.py,在其中存储所有环境通用的设置
  • 每当我需要使用具有特定要求的新环境时,我都会创建新的设置文件(如settings_local.py),它继承settings_base.py的内容,并覆盖/添加适当的设置变量(from settings_base import *)
  • (要使用自定义设置文件运行manage.py,只需使用--settings命令选项:manage.py --settings=settings_you_wish_to_use.py)


    我对这个问题的解决方案也有点混合了一些已经在这里说明的解决方案:

    • 我保存了一个名为local_settings.py的文件,它在dev中有USING_LOCAL = True的内容,在prod中有USING_LOCAL = False的内容。
    • settings.py中,我对该文件执行导入,以获取USING_LOCAL设置。

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

    1
    2
    3
    4
    5
    DEBUG = USING_LOCAL
    if USING_LOCAL:
        # dev database settings
    else:
        # prod database settings

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

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


    还有Django Classy设置。我个人很喜欢它。它是由Django IRC上最活跃的人建造的。您将使用环境变量来设置内容。

    http://django-classy-settings.readthedocs.io/en/latest(最新)/


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

    使用这种方法的好处:

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

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

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


  • 1-在应用程序中创建一个新文件夹并为其命名设置。

    2-现在在其中创建一个新的in it.py文件并在其中写入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
        from .base import *

        try:

            from .local import *

        except:

            pass

         try:

             from .production import *

         except:

             pass

    3-在设置文件夹中创建三个新文件:local.py和production.py以及base.py。

    4-在base.py中,复制先前settings.p文件夹中的所有内容,并用不同的名称重命名,比如说old_settings.py。

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

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

    现在,通过这种方式,项目目录可以结构化,并且可以在生产和本地开发之间进行管理。


    作为维护不同文件的替代方法,如果您愿意:如果您使用Git或任何其他VCS将代码从本地推送到服务器,您可以做的是将设置文件添加到.gitignore。

    这将允许您在两个地方都有不同的内容,没有任何问题。因此,在服务器上,您可以配置一个独立版本的settings.py,在本地进行的任何更改都不会反映在服务器上,反之亦然。

    此外,它还将从Github中删除settings.py文件,这是一个很大的错误,我已经看到许多新手在做这个。


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #!/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.
    You'll have to run django-admin.py, passing it your settings module.
    (If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)
    "
    % __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.
    You'll have to run django-admin.py, passing it your settings module.
    (If the file prod_settings.py does indeed exist, it's causing an ImportError somehow.)
    "
    % __file__)
            sys.exit(1)

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

    我发现将设置文件分为两个单独的文件要容易一些,而不是在设置文件中进行大量的IFS。


    我的设置拆分如下

    1
    2
    3
    4
    5
    settings/
         |
         |- base.py
         |- dev.py
         |- prod.py

    我们有3个环境

    • DEV
    • 分期
    • 生产

    很明显,分段生产应该有尽可能多的相似环境。所以我们保留了prod.py

    但有一个案例,我必须确定运行的服务器是生产服务器。@斯通的回答帮助我写了如下支票。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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


    我认为最好的解决方案是由@t.stone提出的,但我不知道为什么不在django中使用debug标志。我为我的网站编写以下代码:

    1
    2
    if DEBUG:
        from .local_settings import *

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


    制作多个版本的settings.py是12因子应用程序方法的反模式。使用python decouple或django environ代替。


    我发现这里的回答很有帮助。(这是否得到了更明确的解决?最后一个回答是一年前。)在考虑了所有列出的方法之后,我提出了一个解决方案,但在这里我没有看到。

    我的标准是:

    • 所有内容都应该在源代码管理中。我不喜欢到处胡闹。
    • 理想情况下,将设置保存在一个文件中。如果我看得不对,我就会忘记这些事情。)
    • 没有要部署的手动编辑。应该能够使用单个结构命令测试/推送/部署。
    • 避免将开发设置泄漏到生产中。
    • 尽可能靠近"标准"(*咳嗽*)django布局。

    我认为打开主机是有意义的,但后来发现真正的问题是不同环境下的不同设置,并有了一个"啊哈"的时刻。我将此代码放在settings.py文件的末尾:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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'

    这样,应用程序默认为生产设置,这意味着您的开发环境显式地"白名单"。忘记在本地设置环境变量要比从另一个角度设置环境变量安全得多,并且忘记在生产环境中设置一些内容,并使用一些开发人员设置。

    在本地开发时,无论是从shell还是在.bash_概要文件中还是在任何地方:

    1
    $ export DJANGO_DEVELOPMENT_SERVER=yep

    (或者,如果您正在Windows上开发,可以通过控制面板或其他所谓的工具来设置…Windows总是让它变得如此模糊,以至于您可以设置环境变量。)

    使用这种方法,开发人员的设置都在一个(标准)位置,并在需要时简单地覆盖生产设置。任何与开发设置有关的混乱都应该是完全安全的,以承诺在不影响生产的情况下进行源代码控制。