Django:如何管理开发和生产设置?


128

我一直在开发一个基本的应用程序。现在在部署阶段,很明显,我需要本地设置和生产设置。

很高兴知道以下内容:

  • 如何最好地处理开发和生产设置。
  • 如何仅在开发环境中保留django-debug-toolbar之类的应用程序。
  • 开发和部署设置的其他任何技巧和最佳做法。

Answers:


108

DJANGO_SETTINGS_MODULE环境变量,其设置文件Django的控件将加载。

因此,您可以为各自的环境创建单独的配置文件(请注意,它们当然可以import *来自单独的“共享设置”文件),并用于DJANGO_SETTINGS_MODULE控制使用哪个文件。

这是如何做:

如Django文档中所述:

DJANGO_SETTINGS_MODULE的值应采用Python路径语法,例如mysite.settings。请注意,设置模块应位于Python导入搜索路径上。

所以,让我们假设你创建myapp/production_settings.pymyapp/test_settings.py在源存储库。

在这种情况下,您将分别设置DJANGO_SETTINGS_MODULE=myapp.production_settings使用前者和DJANGO_SETTINGS_MODULE=myapp.test_settings后者。


从现在开始,问题归结为设置DJANGO_SETTINGS_MODULE环境变量。

设置DJANGO_SETTINGS_MODULE使用脚本或shell

然后,您可以使用引导脚本或流程管理器来加载正确的设置(通过设置环境),或者仅在启动Django:之前从您的Shell中运行它export DJANGO_SETTINGS_MODULE=myapp.production_settings

请注意,您可以随时从Shell运行此导出-它不需要存在于您的.bashrc容器中。

DJANGO_SETTINGS_MODULE使用流程管理器进行设置

如果您不喜欢编写用于设置环境的引导脚本(并且有很好的理由!),我建议您使用流程管理器:


最后,请注意,您可以利用PYTHONPATH变量将设置存储在完全不同的位置(例如,在生产服务器上,将其存储在中/etc/)。这允许将配置与应用程序文件分开。您可能想要或不想要它,这取决于您的应用程序的结构。


7
需要说明的是,由于默认情况下settings.py存储文件SiteName/settings.py,因此,如果将备用设置文件放置在同一目录中,则添加到bin / activate的行应为“ DJANGO_SETTINGS_MODULE="SiteName.test_settings"否则”为最佳答案!
alexbhandari 2014年

2
巧合的是,您是否了解有关如何逐步进行此操作的教程,我是Django的新手,所以不知道在哪里设置DJANGO_SETTINGS_MODULE或PYTHONPATH
Jesus Almaral-Hackaprende

对于conda env,此解决方案似乎并不成立。conda env中没有bin / activate。
普亚·尤塞菲

1
@PouyaYousefi:您绝对不需要使用virtualenv来使用此答案。答案实际上可以归结为两个步骤:a)使用单独的设置文件,b)使用DJANGO_SETTINGS_MODULE选择要使用的文件。修改bin/activate 是要做后者的一种方法(TBH,无论如何我都不再认为这是个好主意,所以我把它删除了),但这不是唯一的方法。
Thomas Orozco

如果您在pycharm社区版中使用Django,并且需要在命令行和pycharm社区中正确运行单元测试,它也很有用。假设您在源存储库的myapp / settings.py中仅创建了一个简单的配置文件。在这种情况下,您可以在菜单RUN / Edit Configuration / Environment变量中设置“ DJANGO_SETTINGS_MODULE = myapp.settings”,以使用后者运行测试用例。
F.Tamy,

57

默认情况下,使用生产设置,但settings_dev.py在与文件相同的文件夹中创建一个名为的settings.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项目。


8
这怎么不是正确的答案?所以现在真的很乱。Ty cs01
codyc4321

if os.environ.get('DJANGO_DEVELOPMENT', 'true')也可以。我之所以提及这一点,仅是因为上述is not true方法无法在Python 3.6上导入。
英国

1
@brt这是个坏主意:它将始终使用您的DEV设置,这将泄漏公共服务器上的私有数据。您真的只想检查DJANGO_DEVELOPMENT环境变量是否存在(即is not None)。
cs01

感谢您提供信息@ cs01。当我用不正确的设置加载炸毁网站时,我意识到做错了事,但是我不确定为什么要settings_dev.py在服务器上加载。
英国广播公司

2
@ cs01我只要删除is not None支票就可以确保它的存在和真实性。同样os.getenv是速记
Tjorriemorrie

35

通常每个环境有一个设置文件,还有一个共享设置文件:

/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

2
您如何处理pep8禁止import *?您是否禁用该检查?我将此导入文件包装在中,exec()但随后我无法对该文件中未定义的变量设置条件,也无法更改INSTALLED_APPS变量,因为它是“未定义的”
Mikhail 2013年

11
我们不会掉毛我们的设置文件,因为它们的代码不是真正的代码,而是用Python表示的配置。
Daniel Watkins 2013年

17

这是我通过6个简单步骤完成的操作:

  1. 在项目目录中创建一个文件夹,并将其命名settings

    项目结构:

    myproject/
           myapp1/
           myapp2/              
           myproject/
                  settings/
  2. 创建内部4个Python文件settings目录,即__init__.pybase.pydev.pyprod.py

    设置文件:

    settings/
         __init__.py
         base.py
         prod.py
         dev.py 
  3. 打开__init__.py并填写以下内容:

    初始化 .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 *
  4. 打开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/'
  5. 打开dev.py并包含特定于开发的内容,例如:

    dev.py:

    DEBUG = True
    ALLOWED_HOSTS = ['localhost']
    ...
  6. 打开prod.py并包含特定于生产的内容,例如:

    prod.py:

    DEBUG = False
    ALLOWED_HOSTS = ['www.example.com']
    LOGGING = [...]
    ...

10

创建多个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条目提供有关如何拆分设置的更多想法。


谢谢伯勒姆!部署应用程序时,我只需要删除settings_dev文件即可查看我的部署设置在起作用吗?
克里斯蒂安·罗巴克

是的,或更换与进口settings_prod.py
布尔汗·哈立德

1
但是,在部署上编辑master settings.py主文件意味着它将与版本控制发生冲突,因此不一定是最好的前进方式。我想说的是Thomas Orozco的选项是最好的-您可以在virtualenv后激活脚本或gunicorn或mod_wsgi设置中设置DJANGO_SETTINGS_MODULE
Steve Jalim 2012年

1
也许应该提到的是,您永远不会将特定于阶段的文件添加到源代码控制中。我以为可以理解,您不会围绕项目的某个阶段推送设置。
Burhan Khalid 2012年

如果您使用的是virtualenv,则通常默认为{{project_name}}。settings。因此,“设置”在sys.modules中不会成为关键。这将是“ myproject.settings”(或任何项目名称)。您可以使用modname = "%s.settings" % ".".join(__name__.split('.')[:-1])获取完整的模块名称,然后单击globals().update(vars(sys.modules[modname]))。我发现这很适合我。当然,在大多数情况下,也可以放弃以编程方式确定模块名称以支持字符串这一点。
埃里克

9

我使用了很棒的django-configuration,所有设置都存储在我的计算机中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即可


7

这是我们使用的方法:

  • 一个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',
    }
}

该解决方案的好处是:

  • 用于本地开发的用户专用凭证和配置,而无需修改git存储库;
  • 针对特定环境的配置,例如,您可以具有三个不同的环境,其中具有三个不同的环境,.env.json例如dev,stagging和production;
  • 凭证不在存储库中

希望对您有所帮助,请告诉我您是否对此解决方案有所了解。


在假定env是更换devprod等?旧settings.py文件中包含什么?是什么在storage.pydatabase.py
dbinott

@dbinott,您好,您可以轻松地更新env.py文件,以便您可以使用环境变量选择要加载的文件
Charlesthk

例如:conf = os.environ.get('CONF','')file_ = f“ .env。{conf} .json”
Charlesthk

为什么要使用json而不是原生python数据类型?
空袭

4

我使用以下文件结构:

project/
   ...
   settings/
   settings/common.py
   settings/local.py
   settings/prod.py
   settings/__init__.py -> local.py

所以,__init__.py是(在Windows LN在UNIX或mklink)的链接,local.py或者可以是prod.py这样的配置仍然是在project.settings模块清洁和有组织的,如果你想使用一个特定的配置,你可以使用环境变量DJANGO_SETTINGS_MODULEproject.settings.prod,如果你需要在生产环境中运行命令。

在文件prod.pylocal.py

from .shared import *

DATABASE = {
    ...
}

并且该shared.py文件在没有特定配置的情况下保持全局状态。


3

建立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 *


1

如果要保留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

阅读更多:如何检查Python中的操作系统?


1

这似乎已经得到了回答,但是我将其与版本控制结合使用的方法如下:

在与我也添加到.gitignore的本地开发环境上的设置相同的目录中设置一个env.py文件:

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,我们还保持所有内容无缝集成。


最简单的解决方案。也可以Configenv.py文件中的类中定义所有内容。然后import *,可以使用导入模块,而不是使用from env import Config。这样,您也不需要使用if os.path检查,这会使整个过程变得更加简单。
Siddharth Pant

0

使用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

在生产机器上运行,就好像您只是拥有settings.py而已。

优点

  1. settings.py (用于生产)完全不知道存在任何其他环境。
  2. 要查看prod和dev之间的区别,您只需查看一个位置- settings_dev.py。无需配置收集散落settings_prod.pysettings_dev.pysettings_shared.py
  3. 如果在解决生产问题后有人将设置添加到您的产品配置中,您可以放心,该设置也会出现在您的开发配置中(除非被显式覆盖)。因此,不同配置文件之间的差异将被最小化。

0

对于设置文件的问题,我选择复制

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直接修改。
  • 制作一个bash文件进行修改__init__.py
  • 在linux中修改env,然后__init__.py读取此变量。

为什么要用这种方式?

因为我不喜欢同一目录中的太多文件,所以太多的文件会使其他合作伙伴感到困惑,并且对于IDE来说不是很好(IDE无法找到我们使用的文件)

如果您不想看到所有这些详细信息,可以将项目分为两部分。

  1. 制作像Spring Initializr这样的小工具,仅用于设置您的项目。(像复制文件一样)
  2. 您的项目代码

0

我正在使用不同的app.yaml文件来更改Google Cloud App Engine中环境之间的配置。

您可以使用此命令在终端命令中创建代理连接:

./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]

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.