处理本地开发和生产服务器设置的推荐方法是什么?其中一些(如常量等)可以在两者中进行更改/访问,但其中一些(如静态文件的路径)需要保持不同,因此每次部署新代码时都不应覆盖它们。
目前,我正在将所有常量添加到settings.py
。但是每当我在本地更改一些常量时,我必须将其复制到生产服务器并编辑文件以进行特定于生产的更改...... :(
编辑:看起来这个问题没有标准答案,我接受了最流行的方法。
答案 0 :(得分:275)
Two Scoops of Django: Best Practices for 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.py
和production.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上提出了a sample project layout template。
答案 1 :(得分:124)
在settings.py
:
try:
from local_settings import *
except ImportError as e:
pass
您可以覆盖local_settings.py
中所需的内容;它应该远离你的版本控制。但既然你提到复制,我猜你没有使用;)
答案 2 :(得分:66)
使用此布局代替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.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..."
我喜欢这个解决方案的是:
common.py
。prod.py
中,特定于dev的内容放在dev.py
中。这很简单。common.py
或prod.py
中的dev.py
内容,您可以覆盖__init__.py
中的任何内容。答案 3 :(得分:20)
我使用了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设置文件方法)。
答案 4 :(得分:14)
我使用了settings_local.py和settings_production.py。在尝试了几个选项后,我发现只需简单地让两个设置文件感觉简单快捷,就可以轻松地将时间浪费在复杂的解决方案上。
当您对Django项目使用mod_python / mod_wsgi时,需要将其指向您的设置文件。如果您将其指向本地服务器上的app / settings_local.py和生产服务器上的app / settings_production.py,那么生活将变得轻松。只需编辑相应的设置文件并重新启动服务器(Django开发服务器将自动重启)。
答案 5 :(得分:7)
我在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()
)
就是这样。
我写了blog post关于使用django
管理django-split-sttings
设置的问题。看看吧!
答案 6 :(得分:6)
大多数这些解决方案的问题在于,您可以在普通设置之前应用,或者在之后
。所以不可能覆盖像
这样的东西同时。
使用ConfigParser类的“ini”式配置文件可以实现一个解决方案。它支持多个文件,惰性字符串插值,默认值和许多其他好东西。 一旦加载了许多文件,就可以加载更多的文件,如果有的话,它们的值将覆盖以前的文件。
您加载一个或多个配置文件,具体取决于计算机地址,环境变量甚至以前加载的配置文件中的值。然后,您只需使用已解析的值来填充设置。
我成功使用的一个策略是:
defaults.ini
文件net.ini
,然后加载net.domain.ini
,然后加载net.domain.webserver01.ini
,每个可能覆盖前一个的值)。此帐户也适用于开发人员的计算机,因此每个人都可以为本地开发设置其首选数据库驱动程序等cluster.cluster_name.ini
,它可以定义数据库和缓存IP等内容作为您可以通过此实现的一个示例,您可以定义“subdomain”值per-env,然后在默认设置(如hostname: %(subdomain).whatever.net
)中使用它来定义所有必需的主机名和cookie django需要工作的东西。
这就像我可以获得的干,大多数(现有)文件只有3或4个设置。除此之外,我必须管理客户配置,因此存在一组额外的配置文件(包括数据库名称,用户和密码,分配的子域等),每个客户一个或多个。
可以根据需要将其扩展为低或高,只需在配置文件中输入要为每个环境配置的密钥,并且一旦需要新配置,将前一个值放在默认配置中,并在必要时覆盖它。
该系统经证实可靠,适用于版本控制。它已被用于长时间管理两个独立的应用程序集群(每台机器的15个或更多单独的django站点实例),有超过50个客户,其中集群根据系统管理员的情绪改变大小和成员。
答案 7 :(得分:5)
TL; DR:诀窍是在os.environment
中导入settings/base.py
之前修改settings/<purpose>.py
,这将大大简化事情。
考虑所有这些交织在一起的文件让我很头疼。
组合,导入(有时是有条件的),覆盖,修补已在DEBUG
设置的情况下已设置的内容稍后更改。
多么噩梦!
多年来,我经历了各种不同的解决方案。他们都有点工作,但管理起来却很痛苦。
WTF!我们真的需要一切麻烦吗?我们从一个settings.py
文件开始。
现在我们需要一个文档才能正确地将所有这些组合在一起!
我希望我终于通过下面的解决方案点击(我的)最佳位置。
保密秘密 - 不要将它们存放在回购中。
通过环境设置12 factor style设置/读取密钥和密钥。
有合理的后备默认值。理想情况下,对于本地开发,除了默认值之外,您不需要任何其他内容。
...但尝试保持默认生产安全。最好错过本地设置覆盖, 而不必记住调整生产安全的默认设置。
能够以对其他设置产生影响的方式打开/关闭DEBUG
(例如,使用压缩的javascript)。
在目的设置(如本地/测试/暂存/制作)之间切换应仅基于DJANGO_SETTINGS_MODULE
,仅此而已。
...但允许通过DATABASE_URL
等环境设置进一步参数化。
...还允许他们使用不同的目的设置并在本地并排运行,例如。本地开发人员机器上的生产设置,访问生产数据库或烟雾测试压缩样式表。
如果未明确设置环境变量(至少需要空值),则会失败,尤其是在生产环境中,例如。 EMAIL_HOST_PASSWORD
。
DJANGO_SETTINGS_MODULE
将条件保持在最低限度,如果条件是 目的环境类型(例如,对于生产集日志文件及其旋转),则覆盖相关目的设置中的设置文件。
不要让django将DJANGO_SETTINGS_MODULE设置形成文件。
啊!想想这是多么美好。如果你需要一个文件(如docker
env)在开始django过程之前将其读入环境。
请勿覆盖项目/应用代码中的DJANGO_SETTINGS_MODULE,例如。基于主机名或进程名称
如果您懒于设置环境变量(例如setup.py test
),请在运行项目代码之前在工具中执行此操作。
避免魔术和修补django如何读取它的设置,预处理设置但不会干扰。
没有复杂的基于逻辑的废话。配置应该是固定的,具体化不是动态计算的。
提供后备默认值就足够了。
你真的想调试吗,为什么本地你有正确的设置,但在远程服务器上生产,
在一百台机器上,计算出不同的东西?哦!单元测试?对于设置?真的吗?
我的策略包括与ini
样式文件一起使用的优秀django-environ,
提供os.environment
本地开发的默认值,一些最小和短settings/<purpose>.py
个文件
import settings/base.py
AFTER 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 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
中的默认设置,如果环境中缺少任何其他计算机,则会失败。
这里发生的事情是从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/base.py
可以做出相应的响应。
environ.Env.read_env(Path(__file__) / "production.env", DEBUG='False', ASSETS_DEBUG='False')
from .base import *
此处的主要观点是DEBUG
和ASSETS_DEBUG
覆盖,
只有当它们从环境和文件中丢失时,它们才会应用于python os.environ
。
这些将是我们的生产默认值,无需将它们放在环境或文件中,但如果需要,可以覆盖它们。整齐!
这些是你的大多数香草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
覆盖,甚至可以被环境设置覆盖!好极了!
实际上,我们有一个重要的混合层次结构:
答案 8 :(得分:4)
请记住,settings.py是一个实时代码文件。假设您没有在生产中设置DEBUG(这是最佳做法),您可以执行以下操作:
if DEBUG:
STATIC_PATH = /path/to/dev/files
else:
STATIC_PATH = /path/to/production/files
非常基础,但理论上,你可以根据DEBUG的值 - 或者你想要使用的任何其他变量或代码检查来达到任何复杂程度。
答案 9 :(得分:4)
我也在和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
也许这样的事情可以帮到你。
答案 10 :(得分:3)
对于我的大多数项目,我使用以下模式:
from settings_base import *
)(要使用自定义设置文件运行manage.py,只需使用--settings命令选项:manage.py <command> --settings=settings_you_wish_to_use.py
)
答案 11 :(得分:3)
我使用了上面提到的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文件,可以动态加载。
答案 12 :(得分:3)
我对这个问题的解决方案也有点混合了这里已经说过的一些解决方案:
local_settings.py
的文件,该文件的内容为USING_LOCAL = True
,而USING_LOCAL = False
位于prod settings.py
我对该文件进行导入以获取USING_LOCAL
设置然后,我将所有与环境相关的设置基于该设置:
DEBUG = USING_LOCAL
if USING_LOCAL:
# dev database settings
else:
# prod database settings
我更喜欢这个有两个单独的settings.py文件,我需要维护,因为我可以将我的设置结构化在一个文件中比将它们分布在多个文件中更容易。像这样,当我更新设置时,我不会忘记为两种环境都这样做。
当然,每种方法都有其缺点,这一点也不例外。这里的问题是,每当我将更改投入生产时,我都无法覆盖local_settings.py
文件,这意味着我不能盲目地复制所有文件,但这是我可以忍受的。
答案 13 :(得分:3)
还有Django Classy设置。我个人非常喜欢它。它由Django IRC上最活跃的人之一构建。您可以使用环境变量进行设置。
答案 14 :(得分:2)
要在不同环境中使用不同的settings
配置,请创建不同的设置文件。在您的部署脚本中,使用--settings=<my-settings.py>
参数启动服务器,通过该参数,您可以在不同的环境中使用不同的设置。
使用此方法的好处:
您的设置将根据每个环境进行模块化
您可以在master_settings.py
中导入包含基本配置的environmnet_configuration.py
,并覆盖您要在该环境中更改的值。
如果您拥有庞大的团队,每个开发人员可能拥有自己的local_settings.py
,他们可以将其添加到代码存储库中,而不会有任何修改服务器配置的风险。如果您使用 git ,则可以将这些本地设置添加到.gitnore
;如果 Mercurial 用于版本控制,则可以将.hginore
添加到.py
或任何其他)。这样,本地设置甚至不会成为保持干净的实际代码库的一部分。
答案 15 :(得分:1)
我在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。
答案 16 :(得分:1)
如果您愿意,可以选择维护不同的文件: 如果您使用git或任何其他VCS将代码从本地推送到服务器,您可以做的是将设置文件添加到.gitignore。
这将允许您在两个地方都有不同的内容,没有任何问题。在服务器上,您可以配置一个独立版本的settings.py,在本地进行的任何更改都不会反映在服务器上,反之亦然。
此外,它还将从github中删除settings.py文件,这是我见过许多新手所做的大错。
答案 17 :(得分:1)
1 - 在您的应用内创建一个新文件夹,并为其命名设置。
2 - 现在在其中创建一个新的 init .py文件并在其中写入
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中更改您的BASE_DIR路径以指向您的新设置路径
旧路径 - &gt; BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath( file )))
新途径 - &gt; BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath( file )))
现在通过这种方式,项目目录可以结构化,并且可以在生产和本地开发中进行管理。
答案 18 :(得分:0)
制作settings.py的多个版本是12 Factor App methodology的反模式。 改用python-decouple或django-environ。
答案 19 :(得分:0)
我认为@T建议最好的解决方案。斯通,但我不知道为什么不在Django中使用DEBUG标志。我为我的网站编写以下代码:
if DEBUG:
from .local_settings import *
简单的解决方案总比复杂的解决方案好。
答案 20 :(得分:-3)
我发现这里的回复非常有帮助。 (这是否已经更明确地解决了?最后一次回复是一年前的事了。)在考虑了列出的所有方法之后,我想出了一个我没有在这里列出的解决方案。
我的标准是:
我认为切换主机有一定意义,但后来认为真正的问题是不同的环境的不同设置,并且有一个aha时刻。我将此代码放在我的settings.py文件的 end 中:
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_profile开发,或者在任何地方开发:
$ export DJANGO_DEVELOPMENT_SERVER=yep
(或者,如果你是在Windows上开发,通过控制面板设置或者这些天所称的那些...... Windows总是让它变得如此模糊以至于你可以设置环境变量。)
使用这种方法,开发设置都在一个(标准)位置,并根据需要简单地覆盖生产设置。任何涉及开发设置的问题应该是完全安全的,可以在不影响生产的情况下进行源代码管理。