【发布时间】:2012-05-26 17:07:32
【问题描述】:
我一直在开发一个基本的应用程序。现在在部署阶段,很明显我需要本地设置和生产设置。
很高兴知道以下内容:
- 如何最好地处理开发和生产设置。
- 如何将 django-debug-toolbar 等应用仅保留在开发环境中。
- 有关开发和部署设置的任何其他提示和最佳做法。
【问题讨论】:
我一直在开发一个基本的应用程序。现在在部署阶段,很明显我需要本地设置和生产设置。
很高兴知道以下内容:
【问题讨论】:
这就是我通过 6 个简单步骤完成的方法:
在您的项目目录中创建一个文件夹并将其命名为settings。
项目结构:
myproject/
myapp1/
myapp2/
myproject/
settings/
在settings目录内创建四个python文件,即__init__.py、base.py、dev.py和prod.py
设置文件:
settings/
__init__.py
base.py
prod.py
dev.py
打开__init__.py,填入以下内容:
init.py:
from .base import *
# you need to set "myproject = 'prod'" as an environment variable
# in your OS (on which your website is hosted)
if os.environ['myproject'] == 'prod':
from .prod import *
else:
from .dev import *
打开base.py 并填写所有常用设置(将在生产和开发中使用)。例如:
base.py:
import os
...
INSTALLED_APPS = [...]
MIDDLEWARE = [...]
TEMPLATES = [{...}]
...
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
MEDIA_ROOT = os.path.join(BASE_DIR, '/path/')
MEDIA_URL = '/path/'
打开 dev.py 并包含开发特定的内容,例如:
dev.py:
DEBUG = True
ALLOWED_HOSTS = ['localhost']
...
打开 prod.py 并包含特定于生产的内容,例如:
prod.py:
DEBUG = False
ALLOWED_HOSTS = ['www.example.com']
LOGGING = [...]
...
(根据 ANDRESMA 的评论)更新您的 base.py 文件中的 BASE_DIR,以通过在末尾添加另一个 .parent 来反映您的更新路径。例如:
base.py:
BASE_DIR = Path(__file__).resolve().parent.parent.parent
【讨论】:
os.environ.get('myproject') ... 所以当没有设置变量时,代码运行没有错误。
您可能会使用 wsgi.py 文件进行生产(该文件是在您创建 django 项目时自动创建的)。该文件指向一个设置文件。所以制作一个单独的生产设置文件并在你的 wsgi.py 文件中引用它。
【讨论】:
您希望能够根据您所在的 git 分支并依赖于不同的设置文件来切换设置、秘密、环境变量和其他设置,但在企业情况下,您希望隐藏所有敏感信息回购。向所有开发人员公开所有环境变量和所有环境(开发、登台、生产、质量保证等)的秘密并不是最佳安全实践。以下应该达到2。
我的 run.sh
#!/bin/bash
# default environment
export DJANGO_ENVIRONMENT="develop"
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ $BRANCH == "main" ]; then
export DJANGO_ENVIRONMENT="production"
elif [ $BRANCH == "release/"* ]; then
export DJANGO_ENVIRONMENT="staging"
else
# for all other branches (feature, support, hotfix etc.,)
echo ''
fi
echo "
BRANCH: $BRANCH
ENVIRONMENT: $DJANGO_ENVIRONMENT
"
python3 myapp/manage.py makemigrations
python3 myapp/manage.py migrate --noinput
python3 myapp/manage.py runserver 0:8000
我的 vars.py(或 secrets.py 或其他名称)与 django 的 settings.py 位于同一文件夹中
vars = {
'develop': {
'environment': 'develop',
'SECRET_KEY': 'mysecretkey',
"DEBUG": "True"
},
'production': {
'environment': 'production',
'SECRET_KEY': 'mysecretkey',
"DEBUG": "False"
},
'staging': {
'environment': 'staging',
'SECRET_KEY': 'mysecretkey',
"DEBUG": "True"
}
}
然后在 settings.py 中执行以下操作
from . import vars # container environment specific vars
import os
DJANGO_ENVIRONMENT = os.getenv("DJANGO_ENVIRONMENT") # declared in run.sh
envs = vars.vars[DJANGO_ENVIRONMENT] # SECURITY WARNING: keep the secret key
used in production secret!
SECRET_KEY = envs["SECRET_KEY"]
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = envs["DEBUG"]
让开发人员在他们的本地机器上拥有自己的 vars.py,但在部署期间,您的 cicd 管道可以插入带有实际值的实际 vars.py,或者某些脚本应该插入它。如果您使用的是 gitlab cicd,那么您可以将整个 vars.py 存储为环境变量
【讨论】:
我在生产的工作目录中创建了一个名为“production”的文件。
#settings.py
production = Path("production")
DEBUG = False
#if it's dev mode
if not production.is_file():
INSTALLED_APPS +=[
#apps_in_development_mode,
#...
]
DEBUG = True
#other settings to override the default production settings
【讨论】:
我正在使用不同的 app.yaml 文件来更改谷歌云应用引擎中环境之间的配置。
您可以使用它在终端命令中创建代理连接:
./cloud_sql_proxy -instances=<INSTANCE_CONNECTION_NAME>=tcp:1433
https://cloud.google.com/sql/docs/sqlserver/connect-admin-proxy#macos-64-bit
文件:app.yaml
# [START django_app]
service: development
runtime: python37
env_variables:
DJANGO_DB_HOST: '/cloudsql/myproject:myregion:myinstance'
DJANGO_DEBUG: True
handlers:
# This configures Google App Engine to serve the files in the app's static
# directory.
- url: /static
static_dir: static/
# This handler routes all requests not caught above to your main app. It is
# required when static routes are defined, but can be omitted (along with
# the entire handlers section) when there are no static files defined.
- url: /.*
script: auto
# [END django_app]
【讨论】:
默认情况下使用生产设置,但在与settings.py 文件相同的文件夹中创建一个名为settings_dev.py 的文件。在那里添加覆盖,例如DEBUG=True。
在将用于开发的计算机上,将其添加到您的 ~/.bashrc 文件中:
export DJANGO_DEVELOPMENT=true
在settings.py 文件的底部,添加以下内容。
# Override production variables if DJANGO_DEVELOPMENT env variable is set
if os.environ.get('DJANGO_DEVELOPMENT'):
from settings_dev import * # or specific overrides
(请注意,在 Python 中通常应避免导入 *)
默认情况下,生产服务器不会覆盖任何内容。完成!
与其他答案相比,这个答案更简单,因为它不需要更新PYTHONPATH,或设置DJANGO_SETTINGS_MODULE,这一次只允许您处理一个django 项目。
【讨论】:
if os.environ.get('DJANGO_DEVELOPMENT', 'true') 也有效。我之所以提到这一点,只是因为上面的 is not true 方法无法在 Python 3.6 上为我导入。
DEV 设置,这会泄露公共服务器上的私人数据。你真的只是想检查DJANGO_DEVELOPMENT 环境变量是否存在(即is not None)。
settings_dev.py 会在服务器上加载。
is not None 检查。 os.getenv 也是简写
关于设置文件的问题,我选择复制
Project
|---__init__.py [ write code to copy setting file from subdir to current dir]
|---settings.py (do not commit this file to git)
|---setting1_dir
| |-- settings.py
|---setting2_dir
| |-- settings.py
当你运行 django 时,会运行 __init__py。此时settings.py in setting1_dir将替换settings.py in Project。
如何选择不同的环境?
__init__.py。__init__.py。__init__.py读取这个变量。为什么要用这种方式?
因为我不喜欢同一个目录下那么多文件,太多的文件会迷惑其他伙伴,对IDE也不是很好。(IDE找不到我们用的什么文件)
如果你不想看到所有这些细节,你可以把项目分成两部分。
【讨论】:
使用settings.py 进行生产。在同一目录中创建 settings_dev.py 以进行覆盖。
# settings_dev.py
from .settings import *
DEBUG = False
在开发机器上运行您的 Django 应用程序:
DJANGO_SETTINGS_MODULE=<your_app_name>.settings_dev python3 manage.py runserver
在 prod 机器上运行,就好像你只有 settings.py 而没有别的。
优势
settings.py(用于生产)完全不知道是否存在任何其他环境。settings_dev.py。无需收集分散在 settings_prod.py、settings_dev.py 和 settings_shared.py 的配置。【讨论】:
DJANGO_SETTINGS_MODULE environment variable 控制 Django 将加载哪些设置文件。
因此,您可以为各自的环境创建单独的配置文件(请注意,它们当然可以从单独的“共享设置”文件中创建 import *),并使用 DJANGO_SETTINGS_MODULE 来控制要使用的配置文件。
方法如下:
如 Django 文档中所述:
DJANGO_SETTINGS_MODULE 的值应该是 Python 路径语法,例如mysite.settings。请注意,设置模块应位于 Python 导入搜索路径中。
因此,假设您在源代码库中创建了 myapp/production_settings.py 和 myapp/test_settings.py。
在这种情况下,您将分别设置DJANGO_SETTINGS_MODULE=myapp.production_settings 使用前者,设置DJANGO_SETTINGS_MODULE=myapp.test_settings 使用后者。
从这里开始,问题归结为设置DJANGO_SETTINGS_MODULE 环境变量。
DJANGO_SETTINGS_MODULE
然后您可以使用引导脚本或进程管理器来加载正确的设置(通过设置环境),或者在启动 Django 之前从 shell 中运行它:export DJANGO_SETTINGS_MODULE=myapp.production_settings。
请注意,您可以随时从 shell 运行此导出 - 它不需要存在于您的 .bashrc 或任何东西中。
DJANGO_SETTINGS_MODULE
如果您不喜欢编写设置环境的引导脚本(并且有充分的理由有这种感觉!),我建议您使用流程管理器:
environment configuration key 将环境变量传递给托管进程。.env) file 中定义环境变量。最后,请注意您可以利用PYTHONPATH 变量将设置存储在完全不同的位置(例如,在生产服务器上,将它们存储在/etc/ 中)。这允许将配置与应用程序文件分开。您可能想要也可能不想要,这取决于您的应用程序的结构。
【讨论】:
settings.py 文件默认存储在 SiteName/settings.py 中,如果您将备用设置文件放在同一目录中,则添加到 bin/activate 的行应为 DJANGO_SETTINGS_MODULE="SiteName.test_settings" 否则很好的答案!
DJANGO_SETTINGS_MODULE 选择您要使用的设置文件。修改bin/activate是后者(TBH,我不再认为这是一个好主意,所以我把它拿出来了),但它不是唯一的。
这是我们使用的方法:
settings 模块可将设置拆分为多个文件以提高可读性;.env.json 文件,用于存储我们希望从 git 存储库中排除或特定于环境的凭据和参数;env.py 文件来读取.env.json 文件...
.env.json # the file containing all specific credentials and parameters
.gitignore # the .gitignore file to exclude `.env.json`
project_name/ # project dir (the one which django-admin.py creates)
accounts/ # project's apps
__init__.py
...
...
env.py # the file to load credentials
settings/
__init__.py # main settings file
database.py # database conf
storage.py # storage conf
...
venv # virtualenv
...
.env.json 一样:{
"debug": false,
"allowed_hosts": ["mydomain.com"],
"django_secret_key": "my_very_long_secret_key",
"db_password": "my_db_password",
"db_name": "my_db_name",
"db_user": "my_db_user",
"db_host": "my_db_host",
}
project_name/env.py:<!-- language: lang-python -->
import json
import os
def get_credentials():
env_file_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
with open(os.path.join(env_file_dir, '.env.json'), 'r') as f:
creds = json.loads(f.read())
return creds
credentials = get_credentials()
<!-- language: lang-py -->
# project_name/settings/__init__.py
from project_name.env import credentials
from project_name.settings.database import *
from project_name.settings.storage import *
...
SECRET_KEY = credentials.get('django_secret_key')
DEBUG = credentials.get('debug')
ALLOWED_HOSTS = credentials.get('allowed_hosts', [])
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
...
]
if DEBUG:
INSTALLED_APPS += ['debug_toolbar']
...
# project_name/settings/database.py
from project_name.env import credentials
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': credentials.get('db_name', ''),
'USER': credentials.get('db_user', ''),
'HOST': credentials.get('db_host', ''),
'PASSWORD': credentials.get('db_password', ''),
'PORT': '5432',
}
}
.env.json,例如 dev、staging 和 production;我希望这会有所帮助,如果您发现此解决方案有任何警告,请告诉我。
【讨论】:
env 将替换为dev、prod 等?旧的settings.py 文件中有什么内容? storage.py 和 database.py 中有什么内容?
env.py 文件,以便您可以使用环境变量选择要加载的文件
我使用以下文件结构:
project/
...
settings/
settings/common.py
settings/local.py
settings/prod.py
settings/__init__.py -> local.py
所以__init__.py 是到local.py 的链接(在unix 中是ln 或在windows 中是mklink),或者可以是prod.py,所以配置仍然在project.settings 模块中干净且有条理,如果你愿意要使用特定配置,如果您需要为生产环境运行命令,可以使用环境变量 DJANGO_SETTINGS_MODULE 到 project.settings.prod。
在文件prod.py 和local.py:
from .shared import *
DATABASE = {
...
}
并且shared.py 文件在没有特定配置的情况下保持全局。
【讨论】:
这似乎已经得到解答,但是我结合版本控制使用的方法如下:
在与本地开发环境中的设置相同的目录中设置一个 env.py 文件,我也将其添加到 .gitignore:
env.py:
#!usr/bin/python
DJANGO_ENV = True
ALLOWED_HOSTS = ['127.0.0.1', 'dev.mywebsite.com']
.gitignore:
mywebsite/env.py
settings.py:
if os.path.exists(os.getcwd() + '/env.py'):
#env.py is excluded using the .gitignore file - when moving to production we can automatically set debug mode to off:
from env import *
else:
DJANGO_ENV = False
DEBUG = DJANGO_ENV
我只是觉得这很有效,而且更优雅 - 使用 env.py 可以很容易地查看我们的本地环境变量,我们可以在没有多个 settings.py 文件或类似文件的情况下处理所有这些。这种方法允许使用我们不想在生产服务器上设置的各种本地环境变量。通过版本控制利用 .gitignore,我们还保持一切无缝集成。
【讨论】:
env.py 文件中的Config 类中定义所有内容。然后可以通过from env import Config 导入模块,而不是import *。这样你也不需要使用 if os.path 检查这使得整个事情变得更加简单。
建立 cs01 的答案:
如果环境变量有问题,请将其值设置为字符串(例如,我使用了DJANGO_DEVELOPMENT="true")。
我还修改了cs01的文件工作流程如下:
#settings.py
import os
if os.environ.get('DJANGO_DEVELOPMENT') is not None:
from settings_dev import *
else:
from settings_production import *
#settings_dev.py
development settings go here
#settings_production.py
production settings go here
这样,Django 在运行适当的设置文件之前不必通读整个设置文件。如果您的生产文件需要仅在生产服务器上的内容,此解决方案会派上用场。
注意:在 Python 3 中,导入的文件需要附加.(例如from .settings_dev import *)
【讨论】:
我使用了很棒的django-configurations,所有设置都存储在我的settings.py中:
from configurations import Configuration
class Base(Configuration):
# all the base settings here...
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
...
class Develop(Base):
# development settings here...
DEBUG = True
...
class Production(Base):
# production settings here...
DEBUG = False
要配置 Django 项目,我只是按照docs.
【讨论】:
DOTENV = os.path.join(BASE_DIR, ".env"),它将类名作为 env 文件名。
如果您想保留 1 个设置文件,并且您的开发操作系统与生产操作系统不同,则可以将其放在 settings.py 的底部:
from sys import platform
if platform == "linux" or platform == "linux2":
# linux
# some special setting here for when I'm on my prod server
elif platform == "darwin":
# OS X
# some special setting here for when I'm developing on my mac
elif platform == "win32":
# Windows...
# some special setting here for when I'm developing on my pc
【讨论】:
这是我的解决方案,为开发、测试和生产提供不同的环境
import socket
[...]
DEV_PC = 'PC059'
host_name = socket.gethostname()
if host_name == DEV_PC:
#do something
pass
elif [...]
【讨论】:
我通常每个环境有一个设置文件和一个共享设置文件:
/myproject/
settings.production.py
settings.development.py
shared_settings.py
我的每个环境文件都有:
try:
from shared_settings import *
except ImportError:
pass
这允许我在必要时覆盖共享设置(通过在该节下方添加修改)。
然后我通过将其链接到 settings.py 来选择要使用的设置文件:
ln -s settings.development.py settings.py
【讨论】:
import *的pep8禁止?您是否禁用该检查?我已将此导入包装在 exec() 中,但我不能对未在此文件中定义的变量设置条件,也不能更改 INSTALLED_APPS 变量,因为它是“未定义的”
创建多个settings*.py 文件,推断每个环境需要更改的变量。然后在你的主settings.py文件末尾:
try:
from settings_dev import *
except ImportError:
pass
您为每个阶段保留单独的 settings_* 文件。
在settings_dev.py 文件的顶部,添加以下内容:
import sys
globals().update(vars(sys.modules['settings']))
导入需要修改的变量。
这个wiki entry 有更多关于如何拆分设置的想法。
【讨论】:
settings_prod.py
modname = "%s.settings" % ".".join(__name__.split('.')[:-1]) 获取完整的模块名称,然后使用globals().update(vars(sys.modules[modname]))。我发现这对我很有效。当然,在大多数情况下,放弃以编程方式确定模块名称以支持字符串的部分可能也有效。