当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在Django中管理本地vs生产设置?》经验,为你挑选了9个好方法。

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

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

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



1> Rudolf Olah..:

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


请注意,不是每次都使用`--settings`,而是可以设置`DJANGO_SETTINGS_MODULE` envvar.这适用于例如Heroku:将其全局设置为生产,然后在.env文件中使用dev覆盖它.
您可能需要将`BASE_DIR`设置更改为`os.path.dirname(os.path.realpath(os.path.dirname(__ file __)+"/ .."))
感谢Simon,使用`DJANGO_SETTINGS_MODULE` env var是最好的主意.
@rsp根据django文档,你导入`来自django.conf import settings`,这是一个抽象接口并将代码与设置位置分离的对象,https://docs.djangoproject.com/en/dev /主题/设置/#使用 - 设置 - 在 - python的代码
如果我通过环境变量设置DJANGO_SETTINGS_MODULE,我还需要在我的wsgi.py文件中使用os.environ.setdefault("DJANGO_SETTINGS_MODULE","projectname.settings.production")吗?另外,我使用以下方法设置环境变量:export DJANGO_SETTINGS_MODULE = projectname.settings.local,但是当我关闭终端时它会丢失.我该怎么做才能确保它被保存?我应该将该行添加到bashrc文件中吗?
@PetrPeller这更干净:`BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__ file __))))`

2> ohnoes..:

settings.py:

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

你可以覆盖所需的东西local_settings.py; 它应该远离你的版本控制.但既然你提到复制,我猜你没有使用;)


这种方法意味着您在开发和生产中运行了无版本代码.每个开发人员都有不同的代码库.我在这里调用反模式.
这就是我的方式 - 在settings.py的末尾添加这些行,以便他们可以覆盖默认设置
@pydanny问题是Django将它的配置存储在.py文件中.您不能指望所有开发人员和生产服务器都使用相同的设置,因此您需要更改此.py文件或实现一些替代解决方案(.ini文件,环境等).
为了便于跟踪/部署新设置,请在生产/测试机器上使用"local_settings.py",在开发时使用"local_settings.py".
我更喜欢调用模块`settings_local`而不是`local_settings`,用字母文件夹列表中的`settings.py`对它进行分组.使用`.gitignore`将`settings_local.py`保留在版本控制之外,因为凭据不属于Git.想象一下,他们偶然开源.我在git中保存了一个名为`settings_local.py.txt`的模板文件.
@Tupteq你不能指望吗?在虚拟化和容器时代,我认为你可以而且应该为此努力.也许并非总是可行,但您可以通过定义环境变量来选择性地解决个别问题,以便在必要时允许覆盖.当大三学生问老人为什么不起作用时,肯定会打败"为我而工作".

3> MiniQuark..:

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

.
??? settings/
    ??? __init__.py  <= not versioned
    ??? common.py
    ??? dev.py
    ??? prod.py

common.py 是您的大多数配置存在的地方.

prod.py 从普通中导入所有内容,并覆盖它需要覆盖的任何内容:

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

我喜欢这个解决方案是:

    除了秘密之外,一切都在您的版本控制系统中

    大多数配置都在一个地方:common.py.

    特定于产品的东西进入prod.py,特定的东西进入dev.py.这很简单.

    您可以从覆盖的东西common.pyprod.py或者dev.py,你可以覆盖任何东西__init__.py.

    这是直截了当的蟒蛇.没有重新进口黑客.


两件事:1)最好在dev.py中设置Debug = True而不是在prod.py中设置= False.2)不是切换__init__.py,而是使用DJANGO_SETTINGS_MODULE环境变换切换.这将有助于PAAS部署(例如Heroku).
我仍在尝试弄清楚我的project.wsgi和manage.py文件中的设置文件.你会对此有所了解吗?具体来说,在我的manage.py文件中,我有`os.environ.setdefault("DJANGO_SETTINGS_MODULE","foobar.settings")`foobar是一个带有`__init __.py`文件的文件夹,settings是一个带有__init__的文件夹. py`文件包含我的秘密并导入dev.py,然后导入common.py.**编辑**没关系,我没有安装所需的模块.我的错!这很棒!!

4> T. Stone..:

我使用了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设置文件方法).


我刚刚第一次遇到这个并选择(成功!)使用你的解决方案,略有不同:我用uuid.getnode()找到我系统的uuid.所以我正在测试uuid.getnode()== 12345678901(实际上是一个不同的数字),而不是你使用的os.environ测试.我无法找到记录来说服os.environ ['COMPUTERNAME']在每台计算机上都是唯一的.

5> Kai..:

我使用了settings_local.py和settings_production.py.在尝试了几个选项之后,我发现只需简单地让两个设置文件感觉简单快捷,就很容易浪费时间使用复杂的解决方案.

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


我认为在开发中使用settings.py会更好,因为您不必一直指定它.
那么本地开发服务器呢?有没有办法告诉django webserver(使用`python manage.py runserver`运行),要使用哪个设置文件?
@akv如果在runserver命令的末尾添加--settings = [module name](无.py扩展名),则可以指定要使用的设置文件.如果您打算这样做,请帮自己一个忙,并制作一个配置了开发设置的shell脚本/批处理文件.相信我,你的手指会感谢你.

6> sobolevn..:

我在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.看一看!



7> rewritten..:

大多数这些解决方案的问题是,你要么有应用本地设置的常见的,或经过它们.

所以不可能覆盖像这样的东西

特定于env的设置定义了memcached池的地址,在主设置文件中,该值用于配置缓存后端

特定于env的设置将应用程序/中间件添加或删除为默认设置

同时.

可以使用ConfigParser类使用"ini"式配置文件来实现一个解决方案.它支持多个文件,惰性字符串插值,默认值和许多其他好东西.一旦加载了许多文件,就可以加载更多的文件,如果有的话,它们的值将覆盖以前的文件.

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

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

加载默认defaults.ini文件

检查机器名称,并加载它相匹配的逆转FQDN,从最短的匹配最长匹配的所有文件(所以,我装net.ini的话net.domain.ini,那么net.domain.webserver01.ini,以前的每一个可能会覆盖值).此帐户也适用于开发人员的计算机,因此每个人都可以为本地开发设置其首选数据库驱动程序等

检查是否声明了"群集名称",在这种情况下,加载cluster.cluster_name.ini可以定义数据库和缓存IP等内容

作为您可以通过此实现的一个示例,您可以定义"子域"值per-env,然后在默认设置(as hostname: %(subdomain).whatever.net)中使用它来定义django需要工作的所有必需的主机名和cookie.

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

可以根据需要将其扩展为低或高,只需在配置文件中输入要为每个环境配置的密钥,并且一旦需要新配置,将先前的值放在默认配置中,并覆盖它在必要时.

该系统经证明可靠,可与版本控制配合使用.它已被用于长时间管理两个独立的应用程序集群(每台机器的15个或更多单独的django站点实例),有超过50个客户,其中集群根据系统管理员的情绪改变大小和成员. .



8> Janusz Skoni..:

TL; DR:关键是要修改os.environment导入之前settings/base.py的任何settings/.py,这将大大简化事情.


想到所有这些交织在一起的文件让我很头疼.组合,导入(有时是有条件的),覆盖,修补已在案例DEBUG设置中设置的内容稍后更改.什么样的恶梦!

多年来,我经历了所有不同的解决方案.他们都有点工作,但管理起来很痛苦.WTF!我们真的需要一切麻烦吗?我们从一个settings.py文件开始.现在我们需要一个文档才能正确地将所有这些组合在一起!

我希望我终于用下面的解决方案击中(我的)最佳位置.

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

    秘密保密 - 不要将它们存放在回购中.

    通过环境设置,12因子样式设置/读取密钥和秘密.

    有明智的后备默认值.理想情况下,对于本地开发,除默认值之外不需要任何其他内容.

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

    能够以DEBUG对其他设置有影响的方式打开/关闭(例如,使用javascript压缩或不压缩).

    在目的设置(如本地/测试/登台/生产)之间切换应该仅基于,仅此DJANGO_SETTINGS_MODULE而已.

    ...但允许通过环境设置进一步参数化DATABASE_URL.

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

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

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

    条件语句保持到最低限度,如果条件旨意环境类型(例如,用于生产集日志文件和它的旋转),覆盖设置在相关的旨意设置文件.

不要

    不要让django读取DJANGO_SETTINGS_MODULE设置形成文件.
    啊! 想想这是多么美好.如果您需要在启动django进程之前将文件(如docker env)读入环境中.

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

    避免魔法和修补django如何读取它的设置,预处理设置但不会干扰.

    没有复杂的逻辑基础废话.配置应该是固定的,具体化不是动态计算的.提供回退默认值就足够了.
    你真的想调试吗,为什么本地你有正确的设置,但在远程服务器上生产,在一百台机器上,计算方式不同?哦! 单元测试?对于设置?真的吗?

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

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

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

设置/ .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

设置/ 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,只有当他们从环境和文件丢失.

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

设置/ 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,甚至可以被环境设置覆盖!好极了!

实际上,我们有一个重要的混合层次:

    settings/.py - 根据目的设置默认值,不存储机密

    settings/base.py - 主要由环境控制

    过程环境设置 - 12因素宝贝!

    settings/.env - 本地默认值,便于启动



9> 小智..:

我也在与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

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

推荐阅读
yzh148448
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有