为本地开发和生产服务器处理设置的推荐方法是什么?其中一些(如常量等)可以在两者中更改/访问,但其中一些(如静态文件的路径)需要保持不同,因此不应该在每次部署新代码时都重写。
目前,我正在将所有常量添加到settings.py中。但是每次我在本地更改一些常量时,我都必须将其复制到生产服务器并编辑文件以进行特定于生产的更改……:(
编辑:看起来这个问题没有标准答案,我已经接受了最流行的方法。
为本地开发和生产服务器处理设置的推荐方法是什么?其中一些(如常量等)可以在两者中更改/访问,但其中一些(如静态文件的路径)需要保持不同,因此不应该在每次部署新代码时都重写。
目前,我正在将所有常量添加到settings.py中。但是每次我在本地更改一些常量时,我都必须将其复制到生产服务器并编辑文件以进行特定于生产的更改……:(
编辑:看起来这个问题没有标准答案,我已经接受了最流行的方法。
当前回答
我发现这里的回答很有帮助。(这个问题是否得到了更明确的解决?上一次回复是在一年前。)在考虑了列出的所有方法之后,我提出了一个在这里没有列出的解决方案。
我的标准是:
所有东西都应该在源代码控制中。我不喜欢精细的东西到处乱放。 理想情况下,将设置保存在一个文件中。如果我不看东西,我就会忘记它们:) 无需部署手动编辑。应该能够用一个fabric命令测试/推送/部署。 避免将开发设置泄漏到生产环境中。 让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'
这样,应用程序默认为产品设置,这意味着你明确地将你的开发环境“白名单”。忘记在本地设置环境变量要比反过来忘记在生产环境中设置一些内容而使用一些开发设置安全得多。
在本地开发时,无论是在shell中,还是在.bash_profile中,或者其他任何地方:
$ export DJANGO_DEVELOPMENT_SERVER=yep
(或者如果你在Windows上开发,可以通过控制面板或者其他什么工具来设置…Windows总是让它变得如此模糊,以至于你可以设置环境变量。)
使用这种方法,开发设置都在一个(标准)位置,只需在需要的地方覆盖生产设置。任何与开发设置有关的事情都应该完全安全地提交到源代码控制中,而不会对生产产生影响。
其他回答
我认为最好的解决方案是@T提出的。但是我不知道为什么在Django中不使用DEBUG标志。我写下面的代码为我的网站:
if DEBUG:
from .local_settings import *
简单的解决方案总是比复杂的解决方案好。
还有Django Classy Settings。我个人是它的忠实粉丝。它是由Django IRC上最活跃的人之一构建的。你可以使用环境变量来设置。
http://django-classy-settings.readthedocs.io/en/latest/
我也在与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
也许这样能帮到你。
为了在不同的环境中使用不同的设置配置,创建不同的设置文件。在部署脚本中,使用——settings=<my-settings.py>参数启动服务器,通过该参数可以在不同的环境中使用不同的设置。
使用这种方法的好处:
Your settings will be modular based on each environment 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. 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.
TL;DR:诀窍在于修改操作系统。在任何settings/<purpose>.py中导入settings/base.py之前,这将极大地简化事情。
一想到这些缠在一起的文件我就头疼。 合并、导入(有时是有条件的)、覆盖、修补已经设置的内容,以防稍后DEBUG设置更改。 真是个噩梦!
多年来,我尝试了各种不同的解决方案。它们都有一定的作用,但管理起来很痛苦。 WTF !我们真的需要那么多麻烦吗?我们从一个settings.py文件开始。 现在我们需要一个文档,以正确的顺序将所有这些组合在一起!
我希望我终于用下面的解决方案达到了我的最佳境界。
让我们回顾一下目标(有些是共同的,有些是我的)
Keep secrets a secret — don't store them in a repo! Set/read keys and secrets through environment settings, 12 factor style. Have sensible fallback defaults. Ideally for local development you don't need anything more beside defaults. …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. 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). Switching between purpose settings, like local/testing/staging/production, should be based only on DJANGO_SETTINGS_MODULE, nothing more. …but allow further parameterization through environment settings like DATABASE_URL. …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. Fail if an environment variable is not explicitly set (requiring an empty value at minimum), especially in production, eg. EMAIL_HOST_PASSWORD. Respond to default DJANGO_SETTINGS_MODULE set in manage.py during django-admin startproject 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.
not'的
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. 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. Avoid magic and patching of how django reads it's settings, preprocess the settings but do not interfere afterwards. 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?
解决方案
我的策略包括与ini样式文件一起使用的优秀django-environ, 提供操作系统。环境默认为本地开发,一些最小和简短的设置/<purpose>.py文件有一个 导入设置/base.py环境是从INI文件设置的。这有效地给了我们一种设置注入。
这里的技巧是修改操作系统。导入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,然后导入公共设置 从设置/ 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 *
这里的主要兴趣点是DEBUG和ASSETS_DEBUG覆盖, 它们将应用于python操作系统。只有当它们从环境和文件中丢失时才使用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因素宝贝! 设置/。本地默认环境,便于启动