Two Scoops Django 推薦的 Settings 和 Requirements 文件設(shè)置

基本原則

Django 1.8 有約 140 個(gè)配置項(xiàng)可通過(guò) settings 模塊進(jìn)行設(shè)置。settings 模塊在每次啟動(dòng) Django 服務(wù)時(shí)進(jìn)行初始化,因此對(duì) settings.py 文件修改后,都要重啟 Django 服務(wù)器才能生效。

  1. 所有 settings 文件都應(yīng)進(jìn)行版本控制,包括對(duì)配置項(xiàng)的修改日期/時(shí)間和注釋信息進(jìn)行版本控制

  2. DRY,通過(guò) import base_settings 進(jìn)行繼承,避免復(fù)制粘貼

  3. 機(jī)密信息不要放在版本控制中

使用多個(gè) settings 文件

settings/
    __init__.py
    base.py
    local.py
    staging.py
    test.py
    production.py

并且每個(gè) settings 文件都對(duì)應(yīng)有一個(gè) requirements 文件

settings 文件名 | 目的
-----------------------|
local.py, dev.py | 本地開(kāi)發(fā)環(huán)境配置內(nèi)容,如 DEBUG=True, 開(kāi)啟 django-debug-toolbar 等
staging.py | 針對(duì) Staging 階段的配置內(nèi)容
test.py | 針對(duì)運(yùn)行測(cè)試的配置內(nèi)容
production.py, prod.py | 生產(chǎn)環(huán)境下的配置內(nèi)容
ci.py | 針對(duì)持續(xù)集成服務(wù)器的配置內(nèi)容

使用方法:

  1. shell 中
python manage.py shell --settings=twoscoops.settings.local
  1. 啟動(dòng)服務(wù)
python manage.py runserver --settings=twoscoops.settings.local
  1. 設(shè)置 DJANGO_SETTINGS_MODULEPYTHONPATH 環(huán)境變量。若使用了 virtualenv,可以在每個(gè)環(huán)境的激活腳本中設(shè)置 DJANGO_SETTINGS_MODULEPYTHONPATH。

一個(gè)開(kāi)發(fā)環(huán)境中的 settings 文件的例子:

# settings/local.py
from .base import *

DEBUG = True

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql_psycopg2",
        "NAME": "twoscoops",
        "USER": "",
        "PASSWORD": "",
        "HOST": "localhost",
        "PORT": "",
    }
}

INSTALLED_APPS += ("debug_toolbar", )

上面的例子中有 from .base import *, 這是 import * 在 Django 中唯一被贊成使用的地方。因?yàn)槲覀兿朐?settings 文件中覆蓋所有的命名空間。

針對(duì)開(kāi)發(fā)環(huán)境也可以有多個(gè) settings 文件

基本原則是每個(gè) settings 文件都需要版本控制??舍槍?duì)不同的開(kāi)發(fā)者創(chuàng)建相應(yīng)的 settings 文件,如:

# settings/dev_pydanny.py
from .dev import *

# Set short cache timeout
CACHE_TIMEOUT = 30

而創(chuàng)建后的所有 settings 文件會(huì)是:

settings/
    __init__.py
    base.py
    dev.py
    dev_audreyr.py
    dev_pydanny.py
    local.py
    staging.py
    test.py
    production.py

將配置信息從代碼中分離出來(lái)

SECRET_KEY、API KEY 等信息存放在代碼庫(kù)中有以下問(wèn)題:

  1. 這些信息針對(duì)每次部署都要變動(dòng)

  2. SECRET_KEY 等值是配置值,不是代碼

  3. 存放在代碼庫(kù)中,有代碼庫(kù)訪問(wèn)權(quán)限的人都能看到

  4. 多數(shù) PaaS 不提供針對(duì)單臺(tái)服務(wù)器的配置功能

解決方案是使用 環(huán)境變量。

使用環(huán)境變量來(lái)存放 SECRET_KEY 等信息的好處:

  1. 由于這些敏感信息已保存它處,你會(huì)毫不猶豫地對(duì)每個(gè)文件進(jìn)行版本控制

  2. 針對(duì)每次部署,不再需要對(duì)這些配置信息進(jìn)行修改

  3. 多數(shù) PaaS 平臺(tái)推薦使用環(huán)境變量,并提供了相應(yīng)的配置和管理工具

如何在本地進(jìn)行環(huán)境變量設(shè)置

在 Linux/Mac 的 bash 中,通過(guò)將配置代碼添加到 .bashrc、.bash_profile.profile 等文件后面進(jìn)行配置。若使用 virtualenv,也可以在 virtualenv 的 bin/activate 腳本中添加配置代碼進(jìn)行配置:

配置代碼:

$ export SOME_SECRET_KEY=1c3-cr3am-15-yummy

$ export AUDREY_FREEZER_KEY=y34h-r1ght-d0nt-t0uch-my-1c3-cr34m

在 Win 上,可以在 cmd.exe 中通過(guò) setx 命令進(jìn)行配置,也可以在 virtualenv 的 bin/activate.bat 腳本中進(jìn)行配置。

配置代碼:

> set SOME_SECRET_KEY 1c3-cr3am-15-yummy

PowerShellcmd.exe 更加強(qiáng)大,在 Vista 及以上版本中可用。使用 PowerShell 進(jìn)行環(huán)境變量設(shè)置:

只針對(duì)當(dāng)前用戶:

[Environment]::SetEnvironmentVariable("SOME_SECRET_KEY",
                                     "1c3-cr3am-15-yummy", "User")
[Environment]::SetEnvironmentVariable("AUDREY_FREEZER_KEY",
                        "y34h-r1ght-d0nt-t0uch-my-1c3-cr34m", "User")

針對(duì)本機(jī)的全部用戶:

[Environment]::SetEnvironmentVariable("SOME_SECRET_KEY",
                                "1c3-cr3am-15-yummy", "Machine")
[Environment]::SetEnvironmentVariable("AUDREY_FREEZER_KEY",
                        "y34h-r1ght-d0nt-t0uch-my-1c3-cr34m", "Machine")

生產(chǎn)環(huán)境中的環(huán)境變量配置舉例

  1. 在 Heroku 上配置
$ heroku config:set SOME_SECRET_KEY=1c3-cr3am-15-yummy
  1. 在 Python 中存取這些配置信息
>>> import os
>>> os.environ["SOME_SECRET_KEY"]
"1c3-cr3am-15-yummy"
  1. 在 settings 文件中存取這些配置信息
# Top of settings/production.py
import os
SOME_SECRET_KEY = os.environ["SOME_SECRET_KEY"]

對(duì)未設(shè)置 SECRET_KEY 的異常進(jìn)行處理

如果沒(méi)有 SECRET_KEY 值, 上面的存取代碼會(huì)拋出 KeyError,項(xiàng)目也無(wú)法啟動(dòng)。但是該異常沒(méi)有提供有效的提示信息,不利于調(diào)試。

settings/base.py 中使用以下代碼進(jìn)行處理:

# settings/base.py
import os

# Normally you should not import ANYTHING from Django directly
# into your settings, but ImproperlyConfigured is an exception.
from django.core.exceptions import ImproperlyConfigured

def get_env_variable(var_name):
    """Get the environment variable or return exception."""
    try:
        return os.environ[var_name]
    except KeyError:
        error_msg = "Set the {} environment variable".format(var_name)
        raise ImproperlyConfigured(error_msg)

然后在 settings 文件中,使用:

SOME_SECRET_KEY = get_env_variable("SOME_SECRET_KEY")

之后,如果沒(méi)有設(shè)置 SOME_SECRET_KEY 這個(gè)環(huán)境變量,會(huì)出現(xiàn)以下的錯(cuò)誤提示:

django.core.exceptions.ImproperlyConfigured: Set the SOME_SECRET_KEY
environment variable.

manage.py 會(huì)默認(rèn)將 DJANGO_SETTINGS_MODULE 指向 settings.py,推薦在多 settings 文件時(shí)使用 django-admin,而單 settings 文件時(shí)使用 manage.py, 這兩個(gè)命令基本是等同的:

$ django-admin <command> [options]
$ manage.py <command> [options]

當(dāng)不能設(shè)置環(huán)境變量時(shí)

Apache 等使用自己的環(huán)境變量,如以上的針對(duì)系統(tǒng)的環(huán)境變量設(shè)置方法無(wú)效,此時(shí)可以將敏感信息保存在一個(gè)不可執(zhí)行的文件中,并且不對(duì)該文件進(jìn)行版本控制:

  1. 為保存敏感信息生成一個(gè)文件,格式可以為 JSON、Config、YAML 或 XML。

  2. 增加一個(gè) loader 來(lái)對(duì)這些信息進(jìn)行管理

  3. 將該文件名增加到 .gitignore.hgignore

使用 JSON 格式

  1. 生成 secrets.json 文件:
{
    "FILENAME": "secrets.json",
    "SECRET_KEY": "I've got a secret!",
    "DATABASES_HOST": "127.0.0.1",
    "PORT": "5432"
}
  1. 在 settings/base.py 中添加 loader,來(lái)存取這些信息:
# settings/base.py

import json

# Normally you should not import ANYTHING from Django directly
# into your settings, but ImproperlyConfigured is an exception.
from django.core.exceptions import ImproperlyConfigured

# JSON-based secrets module
with open("secrets.json") as f:
    secrets = json.loads(f.read())

def get_secret(setting, secrets=secrets):
    """Get the secret variable or return explicit exception."""
    try:
        return secrets[setting]
    except KeyError:
        error_msg = "Set the {0} environment variable".format(setting)
        raise ImproperlyConfigured(error_msg)

SECRET_KEY = get_secret("SECRET_KEY")

使用多個(gè) requirements 文件

每個(gè) settings 文件需對(duì)應(yīng)有一個(gè) requirements 文件,并且對(duì)應(yīng)不同的配置,只安裝相應(yīng)的依賴文件。

requirements 文件舉例:

requirements/
    base.txt
    local.txt
    staging.txt
    production.txt

base.txt 中存放全局依賴,如:

Django==1.8.0
psycopg2==2.6
djangorestframework==3.1.1

而針對(duì)本地開(kāi)發(fā)環(huán)境的 local.txt,可以在 base.txt 的基礎(chǔ)上添加其它依賴:

-r base.txt # includes the base.txt requirements file

coverage==3.7.1
django-debug-toolbar==1.3.0

對(duì)于持續(xù)集成服務(wù)器的 ci.txt 可以是:

-r base.txt # includes the base.txt requirements file

coverage==3.7.1
django-jenkins==0.16.4

production.txt 基本會(huì)和 base.txt 相同,可能會(huì)是:

-r base.txt # includes the base.txt requirements file

安裝

針對(duì)本地開(kāi)發(fā):

$ pip install -r requirements/local.txt

針對(duì)生產(chǎn)環(huán)境:

$ pip install -r requirements/production.txt

所有 requirements 文件中的依賴包都指定為特定的一個(gè)版本,這樣能確保項(xiàng)目更加穩(wěn)定。

在 settings 文件中處理文件路徑

不要對(duì)文件路徑進(jìn)行硬編碼

  1. 使用 Unipath 進(jìn)行文件路徑處理
# At the top of settings/base.py
from unipath import Path

BASE_DIR = Path(__file__).ancestor(3)
MEDIA_ROOT = BASE_DIR.child("media")
STATIC_ROOT = BASE_DIR.child("static")
STATICFILES_DIRS = (
    BASE_DIR.child("assets"),
)
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        DIRS = (BASE_DIR.child("templates"),)
    },
]
  1. 使用 os.path 進(jìn)行文件路徑處理
# At the top of settings/base.py
from os.path import join, abspath, dirname

here = lambda *dirs: join(abspath(dirname(__file__)), *dirs)
BASE_DIR = here("..", "..")
root = lambda *dirs: join(abspath(BASE_DIR), *dirs)

# Configuring MEDIA_ROOT
MEDIA_ROOT = root("media")

# Configuring STATIC_ROOT
STATIC_ROOT = root("collected_static")

# Additional locations of static files
STATICFILES_DIRS = (
    root("assets"),
)

# Configuring TEMPLATE_DIRS
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        DIRS = (root("templates"),)
    },
]

要找到你的配置文件與 Django 默認(rèn)配置的區(qū)別,使用 Django 的 diffsettings 命令。

參考文獻(xiàn): Two Scoops of Django: Best Practices for Django 1.8

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容