【问题标题】:Django: How to manage development and production settings?Django:如何管理开发和生产设置?
【发布时间】:2012-05-26 17:07:32
【问题描述】:

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

很高兴知道以下内容:

  • 如何最好地处理开发和生产设置。
  • 如何将 django-debug-toolbar 等应用仅保留在开发环境中。
  • 有关开发和部署设置的任何其他提示和最佳做法。

【问题讨论】:

标签: python django


【解决方案1】:

这就是我通过 6 个简单步骤完成的方法:

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

    项目结构:

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

    设置文件:

    settings/
         __init__.py
         base.py
         prod.py
         dev.py 
    
  3. 打开__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 *
    
  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 = [...]
    ...
    
  7. (根据 ANDRESMA 的评论)更新您的 base.py 文件中的 BASE_DIR,以通过在末尾添加另一个 .parent 来反映您的更新路径。例如:

    base.py:

    BASE_DIR = Path(__file__).resolve().parent.parent.parent
    

【讨论】:

  • 现在 {% extends 'base.html' %} 不起作用。你有什么改变吗?
  • 感谢分享!我偶然发现了两件事:我更改了 proddev if-case,所以当有疑问时将使用 prod 设置并且我正在访问 myproject 变量通过os.environ.get('myproject') ... 所以当没有设置变量时,代码运行没有错误。
  • @cikatomo 更改 BASE_DIR 变量以将项目文件夹提升一级
【解决方案2】:

您可能会使用 wsgi.py 文件进行生产(该文件是在您创建 django 项目时自动创建的)。该文件指向一个设置文件。所以制作一个单独的生产设置文件并在你的 wsgi.py 文件中引用它。

【讨论】:

    【解决方案3】:

    您希望能够根据您所在的 git 分支并依赖于不同的设置文件来切换设置、秘密、环境变量和其他设置,但在企业情况下,您希望隐藏所有敏感信息回购。向所有开发人员公开所有环境变量和所有环境(开发、登台、生产、质量保证等)的秘密并不是最佳安全实践。以下应该达到2。

    1. 根据部署环境隔离设置
    2. 在 git repo 中隐藏敏感信息

    我的 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 存储为环境变量

    【讨论】:

      【解决方案4】:

      我在生产的工作目录中创建了一个名为“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
      

      【讨论】:

        【解决方案5】:

        我正在使用不同的 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]
        

        【讨论】:

          【解决方案6】:

          默认情况下使用生产设置,但在与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 项目。

          【讨论】:

          • 这不是正确的答案吗?所以现在真的是一团糟。 Ty cs01
          • if os.environ.get('DJANGO_DEVELOPMENT', 'true') 也有效。我之所以提到这一点,只是因为上面的 is not true 方法无法在 Python 3.6 上为我导入。
          • @brt 这是个坏主意:它总是会使用你的DEV 设置,这会泄露公共服务器上的私人数据。你真的只是想检查DJANGO_DEVELOPMENT 环境变量是否存在(即is not None)。
          • 感谢@cs01 提供的信息。当我因加载不正确的设置而炸毁我的网站时,我意识到我做错了,但我不确定为什么 settings_dev.py 会在服务器上加载。
          • @cs01 我会尽量确保它存在并且是真实的,只需放弃is not None 检查。 os.getenv 也是简写
          【解决方案7】:

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

          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. 您的项目代码

          【讨论】:

            【解决方案8】:

            使用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 而没有别的。

            优势

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

            【讨论】:

              【解决方案9】:

              DJANGO_SETTINGS_MODULE environment variable 控制 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 环境变量。

              使用脚本或 shell 设置 DJANGO_SETTINGS_MODULE

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

              请注意,您可以随时从 shell 运行此导出 - 它不需要存在于您的 .bashrc 或任何东西中。

              使用进程管理器设置DJANGO_SETTINGS_MODULE

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


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

              【讨论】:

              • 澄清一下,由于 settings.py 文件默认存储在 SiteName/settings.py 中,如果您将备用设置文件放在同一目录中,则添加到 bin/activate 的行应为 DJANGO_SETTINGS_MODULE="SiteName.test_settings" 否则很好的答案!
              • 巧合的是,您知道有关如何逐步执行此操作的教程吗,我是 Django 新手,不知道在哪里设置 DJANGO_SETTINGS_MODULE 或 PYTHONPATH
              • 此解决方案似乎不适用于 conda 环境。 conda 环境中没有 bin/activate。
              • @PouyaYousefi:你绝对不需要需要使用 virtualenv 来使用这个答案。答案实际上归结为两个步骤:a) 使用单独的设置文件,b) 使用 DJANGO_SETTINGS_MODULE 选择您要使用的设置文件。修改bin/activate是后者(TBH,我不再认为这是一个好主意,所以我把它拿出来了),但它不是唯一的。
              • 如果您在 pycharm 社区版中使用 Django 并且需要在命令行和 pycharm 社区上正确运行单元测试,这也很有用。假设您在源存储库的 myapp/settings.py 中只创建了一个简单的配置文件。在这种情况下,您可以在菜单 RUN/Edit Configuration/Environment 变量中设置“DJANGO_SETTINGS_MODULE=myapp.settings”,以便稍后使用它来运行测试用例。
              【解决方案10】:

              这是我们使用的方法:

              • 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、staging 和 production;
              • 凭证不在存储库中

              我希望这会有所帮助,如果您发现此解决方案有任何警告,请告诉我。

              【讨论】:

              • 假设env 将替换为devprod 等?旧的settings.py 文件中有什么内容? storage.pydatabase.py 中有什么内容?
              • 嗨@dbinott,您可以轻松更新env.py 文件,以便您可以使用环境变量选择要加载的文件
              • 例如:conf = os.environ.get('CONF', '') file_ = f".env.{conf}.json"
              • 为什么要使用 json 而不是原生 python 数据类型?
              【解决方案11】:

              我使用以下文件结构:

              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_MODULEproject.settings.prod

              在文件prod.pylocal.py

              from .shared import *
              
              DATABASE = {
                  ...
              }
              

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

              【讨论】:

                【解决方案12】:

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

                在与本地开发环境中的设置相同的目录中设置一个 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 检查这使得整个事情变得更加简单。
                【解决方案13】:

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

                【讨论】:

                  【解决方案14】:

                  我使用了很棒的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 文件的引用,以便将它们包含在一个类中,例如 DOTENV = os.path.join(BASE_DIR, ".env"),它将类名作为 env 文件名。
                  【解决方案15】:

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

                  阅读更多:How do I check the operating system in Python?

                  【讨论】:

                    【解决方案16】:

                    这是我的解决方案,为开发、测试和生产提供不同的环境

                    import socket
                    
                    [...]
                    
                    DEV_PC = 'PC059'
                    host_name = socket.gethostname()
                    
                    if host_name == DEV_PC:
                       #do something
                       pass
                    elif [...]
                    

                    【讨论】:

                    • 我认为你没有理解这个问题。
                    • 这很糟糕,有几个原因,但最烦人的是,如果您使用多个主机进行开发,则必须跟踪多个 DEV_PC_X 变量,没有充分的理由。
                    【解决方案17】:

                    我通常每个环境有一个设置文件和一个共享设置文件:

                    /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 变量,因为它是“未定义的”
                    • 我们不对设置文件进行 lint,因为它们不是真正的代码,而是用 Python 表达的配置。
                    【解决方案18】:

                    创建多个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_dev 文件即可查看我的部署设置是否有效?
                    • 是,或者将导入替换为settings_prod.py
                    • 在部署中编辑主 settings.py 文件意味着它会与版本控制发生冲突,因此它不一定是最好的方法。我想说 Thomas Orozco 的选项是最好的——你可以在你的 virtualenv postactivate 脚本或你的 gunicorn 或 mod_wsgi 设置中设置 DJANGO_SETTINGS_MODULE
                    • 也许应该提到,您永远不会将特定于阶段的文件添加到源代码控制中。我假设你不会推送特定于项目阶段的设置。
                    • 如果你使用 virtualenv,它通常默认为 {{project_name}}.settings。所以“设置”不会是 sys.modules 中的关键。它将是“myproject.settings”(或您的项目名称)。您可以使用modname = "%s.settings" % ".".join(__name__.split('.')[:-1]) 获取完整的模块名称,然后使用globals().update(vars(sys.modules[modname]))。我发现这对我很有效。当然,在大多数情况下,放弃以编程方式确定模块名称以支持字符串的部分可能也有效。
                    猜你喜欢
                    • 2016-04-07
                    • 2021-09-01
                    • 1970-01-01
                    • 2012-10-08
                    • 2017-07-21
                    • 2020-08-05
                    • 2017-08-18
                    • 2012-07-11
                    相关资源
                    最近更新 更多