如何处理本地开发和生产服务器的设置是推荐的方法?其中一些设置(例如常数等)可以在两者中更改/访问,但某些设置(例如静态文件路径)需要保持不同,因此不应该在每次部署新代码时被覆盖。
目前,我将所有常量添加到settings.py
中。但是每次在本地更改一些常量后,我都必须将其复制到生产服务器并编辑用于生产环境的文件... :(
编辑:看起来这个问题没有标准答案,我接受了最流行的方法。
如何处理本地开发和生产服务器的设置是推荐的方法?其中一些设置(例如常数等)可以在两者中更改/访问,但某些设置(例如静态文件路径)需要保持不同,因此不应该在每次部署新代码时被覆盖。
目前,我将所有常量添加到settings.py
中。但是每次在本地更改一些常量后,我都必须将其复制到生产服务器并编辑用于生产环境的文件... :(
编辑:看起来这个问题没有标准答案,我接受了最流行的方法。
《Django最佳实践:第二版》建议为您的设置文件使用版本控制,并将这些文件存储在一个单独的目录中:
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上发布了一个样例项目布局模板(链接)。
DJANGO_SETTINGS_MODULE
来代替每次使用 --settings
。这种方法很适合在Heroku等平台上使用:将它全局设置为生产环境,然后在您的.env
文件中覆盖为开发环境。请注意,不要更改原意。 - Simon WeberDJANGO_SETTINGS_MODULE
环境变量是最好的思路,感谢 Simon。 - kibibuBASE_DIR
设置更改为os.path.dirname(os.path.realpath(os.path.dirname(__file__) + "/.."))
。该设置旨在使路径更准确且不易出错。 - Petr Pellerfrom django.conf import settings
这个模块,它是一个对象,用于抽象出设置的接口,并将代码与设置的位置解耦。详情请参考:https://docs.djangoproject.com/en/dev/topics/settings/#using-settings-in-python-code - user9903BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
。 - Amir Ali Akbari在 settings.py
文件中:
try:
from local_settings import *
except ImportError as e:
pass
你可以在 local_settings.py
中覆盖需要的内容;它应该不包含在你的版本控制中。但既然你提到了复制,我猜你没用过;)
settings_local
而不是local_settings
,以便在按字母排序的文件夹列表中与settings.py
分组。使用.gitignore
将settings_local.py
排除在版本控制之外,因为凭据不属于Git,以防意外公开它们。我保留一个名为settings_local.py.txt
的模板文件放在git中。 - fmalinaexcept ImportError as e:
而不只是 except ImportError:
吗?其实这两种写法都可以,但是加上as e
的话,可以把捕捉到的错误信息存储在变量e
中,方便后续处理或输出。 - Costantin不要使用settings.py
,而是使用这个布局:
.
└── settings/
├── __init__.py <= not versioned
├── common.py
├── dev.py
└── prod.py
common.py
是大部分配置所在的地方。
prod.py
导入了来自 common 的所有内容,并覆盖了它需要覆盖的任何内容:
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.py
中。它很简单。prod.py
或dev.py
中覆盖common.py
中的某些内容,并且您可以覆盖__init__.py
中的任何内容。os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings")
,其中foobar是一个带有__init__.py
文件的文件夹,而settings是一个带有__init__.py
文件的文件夹,其中包含我的机密信息并导入dev.py,然后导入common.py。 编辑 算了,我没有安装所需的模块。我的错!这个方法很好用!! - teewuanePRODUCTION_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设置方法(我不喜欢五个设置文件的方法)。
os.environ['COMPUTERNAME']
在 PythonAnywhere 上不起作用,会出现 KeyError。 - nbeuchatsettings/<purpose>.py
文件中导入settings/base.py
之前,修改os.environment
,这将大大简化事情。
DEBUG
设置后来发生变化。真是噩梦!settings.py
文件。现在我们需要文档才能正确地将它们正确组合在一起!保持秘密的秘密 - 不要将它们存储在仓库中!
通过环境设置设置/读取密钥和密码,遵循12 factor风格。
具有明智的回退默认值。理想情况下,对于本地开发,您除了默认值之外什么都不需要。
...但尽量保持默认设置的安全性。在本地缺少设置覆盖比记住调整适用于生产的默认设置更好。
有能力以可以影响其他设置的方式切换DEBUG
的开/关状态(例如使用压缩的javascript或不使用)。
仅基于DJANGO_SETTINGS_MODULE
切换目的设置,没有其他的。
...但允许通过环境设置进行进一步参数化,例如DATABASE_URL
。
...还允许它们使用不同的目的设置并在本地并行运行,例如在本地开发人员机器上设置为生产设置,以访问生产数据库或测试压缩样式表。
如果未明确设置环境变量,则失败(至少需要一个空值),特别是在生产中,例如EMAIL_HOST_PASSWORD
。
在django-admin startproject期间响应于默认的DJANGO_SETTINGS_MODULE
设置。
将条件语句最小化,如果条件是目的环境类型(例如,为生产设置日志文件及其旋转),则在关联的目的设置文件中覆盖设置。
不要让Django从文件中读取DJANGO_SETTINGS_MODULE设置。
哎呀!考虑一下这是多么的元啊。如果您需要读取文件(例如docker env),请在启动django进程之前将其读入环境中。
不要在项目/应用程序代码中覆盖DJANGO_SETTINGS_MODULE,例如基于主机名或进程名称进行覆盖。
如果您懒得设置环境变量(例如对于setup.py test
),请在运行项目代码之前的工具中设置。
避免使用魔法和补丁来读取Django设置,预处理设置但不干扰后续过程。
不要使用基于复杂逻辑的胡言乱语。配置应该是固定的和具体化的,不应该在运行时被计算。提供一个后备默认值就足够了。
你真的想要调试吗?为什么在本地你有正确的设置,但在生产环境的远程服务器上有些机器计算出来的结果却不同呢?哦!单元测试?针对设置?认真的吗?
我的策略包括使用 django-environ 和ini
格式文件,
为本地开发提供os.environment
默认设置,一些最小和简短的settings /<purpose>.py
文件,在从INI
文件中设置os.environment
之后,
导入import settings/base.py
。这为我们提供了一种设置注入方式。
此处的诀窍是在导入settings/base.py
之前修改os.environment
。
要查看完整示例,请转到库: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
这是一个用于本地开发的默认设置文件,它包含了大多数必要的环境变量。如果某些变量在本地开发中不需要,请将其设置为空值。我们在此提供默认值,而不是在 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
。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
- 根据目的设置默认值,不存储机密信息
2. settings/base.py
- 大部分由环境控制
3. 进程环境设置 - 12因素宝宝!
4. settings/.env
- 用于简便启动的本地默认值我使用 settings_local.py 和 settings_production.py,尝试了几种方案后,发现只需使用两个设置文件即感觉轻松快捷,不需要复杂的解决方案。
当您在 Django 项目中使用 mod_python/mod_wsgi 时,您需要指向您的设置文件。如果在本地服务器上指向 app/settings_local.py,在生产服务器上指向 app/settings_production.py,则会变得轻松。只需编辑相应的设置文件并重新启动服务器(Django 开发服务器将自动重启)。
python manage.py runserver
运行),要使用哪个设置文件? - akvexample/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-sttings
管理 django
设置的博客文章。看一看吧!
uwsgi.ini
文件在开发和生产环境中有不同的设置.. 有什么办法可以让它从我的设置文件中获取值吗? - abbood请记住,settings.py是一个实时的代码文件。假设您在生产环境中没有设置DEBUG(这是最佳实践),您可以执行以下操作:
if DEBUG:
STATIC_PATH = /path/to/dev/files
else:
STATIC_PATH = /path/to/production/files
这很基础,但理论上,你可以根据DEBUG的值或任何其他变量或代码检查来提高复杂度。
config = ConfigParser.ConfigParser()
加载解析器,然后使用 config.read(array_of_filenames)
读取文件并使用 config.get(section, option)
获取值。因此,首先加载配置文件,然后使用它来读取设置的值。 - rewritten我也在使用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