在Django中的settings.py中可以修改130多項設置, 但大多數都繼承自默認值. 設置是在web服務器啓動時首次載入的, 服務器重啓時重新載入, 因此, 程序員們應儘量避免修改正式服務器上使用的settings.py文件. 以下是一些我們應當遵循的原則:
- 所有的設置文件應當進行版本管理
- 不要重複自己 (don't repeat yourself)
- 妥善保存關鍵信息
之前, 我們採用的方法是, 不將設置文件放入git庫中, 每個開發人員本地有一份自己的設置文件. 但我們發現這樣做是錯誤的. 因爲:
- 在debug之前, 我們可能已經花費了大量精力去模擬正式服務器上出現的錯誤, 但最終發現這是由於正式服務器的settings文件設置和本地不同而 出現的問題. 這時你的心情會是怎樣?
- 當你在開發django項目時, 發現並修復了一個bug. 當將這一commit push到服務器後, 你突然發現這一bug的出現完全是因爲你修改了本地的 settings文件而產生的, 而由於你的push, 又導致了服務器的宕機. 這時你又會是怎樣的感受?
- 每個人都會從另一個程序員那裏拷貝/黏貼settings文件內容, 這難道不是違反了"不要重複自己"的原則嗎?
正式由於這些問題, 所以我們現在採用不一樣的設置方式. 我們首先建立一個基本的設置文件, 然後將開發和正式部署的設置文件分離成不同的模塊, 但 這些模塊都繼承自同一個基本設置文件:
1. 使用分離式的設置文件
django項目建立時, 會自動生成settings.py文件. 爲了實現分離式的設置文件, 我們首先刪除settings.py, 然後建立settings目錄:
settings/ __init__.py base.py local.py test.py production.py ...
設置文件 | 目的 |
---|---|
base.py | 基本設置文件, 在各個環境中都相同的設置可以放入其中. |
local.py | 當在開發時使用的設置文件. 可以設置開發時的選項, 包括DEBUG, log的等級, 是否開啓例如 django-debug-toolbar等開發工具等. |
test.py | 運行test時的配置, 包括test runners, in-memory數據庫定義和log設置等. |
production.py | 當部署到正式服務器上所用的設置. |
我們可以使用以下命令使用這些不同的設置文件:
python manage.py shell --settings=mysite.settings.local python manage.py runserver --settings=mysite.settings.local
當然如果你熟悉 DJANGO_SETTINGS_MODULE 和 PYTHONPATH 的話, 也可以事先設置好 DJANGO_SETTINGS_MODULE 和 PYTHONPATH 環境變量, 這樣做的好處就是你不必使用--settings了.
如果你對virtualenv有深入的瞭解的話, 也可以在postactivate腳本中設置 DJANGO_SETTINGS_MODULE 和 PYTHONPATH.
local.py的例子
# settings/local.py from .base import * DEBUG = True TEMPLATE_DEBUG = DEBUG EMAIL_BACKEND = 'django.core.email.backends.console.EmailBackend' DATABASES = { "defaults": { "ENGINE": "django.db.backends.postgresql_psycopg2", "NAME": "weiguda", "USER": "", "PASSWORD": "", "HOST": "localhost", "PORT": "", } } INSTALLED_APPS += ("debug_toolbar",)
有時, 一個開發人員的配置文件可能與另一個不同, 這時, 我們可以在settings目錄中新建local_name.py:
# settings/local_name.py from .local import * # 設置不同的配置 CACHE_TIMEOUT = 30
2. 將關鍵信息和設置文件分離
將SECTET_KEY, AWS key文件, API key文件等關鍵信息放入設置文件中也是違反基本原則的. 因爲:
- 配置環境不同時關鍵信息會改變, 程序卻不會.
- 關鍵信息不是程序.
- 關鍵信息應當是隱蔽的, 如果儲存在了版本管理系統中, 則任何有權訪問該版本庫的用戶都能獲知這些關鍵信息.
- 許多PAAS服務無法爲每臺服務器編輯設置文件, 即使可以, 這也是不正確的做法.
環境變量
爲了避免以上的問題, 我們使用環境變量 (environment variables) 來儲存這些關鍵信息, (需要注意的是, apache不支持環境變量, 我們會在下面講到). 使用環境變量儲存關鍵信息有以下好處:
- 將關鍵信息從代碼中移除, 這樣你就可以安心的將所有文件放入版本管理系統中.
- 每個開發人員都擁有一樣的local.py文件.
- 在部署django項目時, 不需要修改程序代碼.
- 大多數PAAS都推薦這一方法, 並提供了方便的設置方法, 因此容易實現.
設置環境變量
在使用bash的Mac或Linux中設置環境變量比較容易, 你只需要將以下代碼加入.bashrc, .bash_profile, 或.profile其中之一即可. 如果多個項目 使用相同的API, 並且關鍵信息都不同時, 可以將以下代碼加入到virtualenv的bin/activate腳本中:
$ export SOME_SECRET_KEY=654-3jgwg-4r3-2t4h-76jk $ export ANOTHER_SECRET_KEY=y5y-5jk8-75i5h-5g4/.-,o.
如果使用的是windows系統, 則設置稍微複雜一點. 如果使用cmd.exe, 你必須使用setx命令一個一個的設置, 一個較好的方式是使用virtualenv的 bin/activate.bat
> setx OME_SECRET_KEY=654-3jgwg-4r3-2t4h-76jk
PowerShell是Windows Vista及以上自帶的shell, 它比cmd.exe強大得多. 因此可以使用PowerShell來設置環境變量:
# 爲用戶User設置 [Environment]::SetEnvironmentVariable("SOME_SECRET_KEY", "654-3jgwg-4r3-2t4h-76jk", "User") # 爲全局設置 [Environment]::SetEnvironmentVariable("SOME_SECRET_KEY", "654-3jgwg-4r3-2t4h-76jk", "Machine")
如果你使用virtuanenvwrapper, 那麼可以使用virtualenvwrapper的pre-virtualenv設置環境變量, 這樣可能會更方便.
如果你使用PAAS, 則請參閱不同的PAAS提供的設置方法.
獲取環境變量
在設置好環境變量後, 我們來看如何在django的settings代碼中獲取這些關鍵信息:
# 在settings/production.py頂部 import os SOME_SECRET_KEY = os.environ["SOME_SECRET_KEY"]
在以上代碼中, 如果SOME_SECRET_KEY無法被獲取到的話, 就會出現KeyError錯誤, 導致django項目無法啓動. 這很好, 但KeyError沒有提供更有 用的信息, 導致debug的困難, 因此, 我們在base.py(希望你還記得這是哪個文件)加入以下function, 爲我們提供哪個關鍵信息無法獲取的信息:
# settings/base.py import os # 通常你不應該從django引入任何代碼, 但ImproperlyConfigured是個例外 from django.core.exceptions import ImproperlyConfigured def get_env_variable(var_name): try: return os.environ[var_name] except KeyError: error_msg = "Set the %s environment variable" % var_name raise ImproperlyConfigured('error_msg')
然後修改之前的production.py:
# 在settings/production.py頂部 import os SOME_SECRET_KEY = get_env_variable('SOME_SECRET_KEY')
此時, 當你沒有設置SOME_SECRET_KEY環境變量時, 系統會提示錯誤信息, 告訴你是哪個環境變量沒有設置.
無法使用環境變量時
當我們使用apache時, 我們會發現, django無法使用環境變量. 這時, 我們推薦將關鍵信息儲存在JSON格式的文件中, 已達到將關鍵信息和代碼分離的 目的. 首先我們可以創建secrets.json文件:
{ "FILENAME": "secrets.json", "SOME_SECRET_KEY": "654-3jgwg-4r3-2t4h-76jk" }
在settings中使用該文件:
# settings/base.py import json # 通常你不應該從django引入任何代碼, 但ImproperlyConfigured是個例外 from django.core.exceptions import ImproperlyConfigured # 讀取json文件 with open("secrets.json") as f: secrets = json.loads(f.read()) def get_secret(setting, secrets=secrets): try: return secrets[setting] except KeyError: error_msg = "Set the {0} environment variable".format(setting) raise ImproperlyConfigured('error_msg') SOME_SECRET_KEY = get_secret('SOME_SECRET_KEY')
3. 使用不同的部署文件(requirements.txt)
部署文件(requirements.txt)中儲存的是該django項目的依賴庫, 一般使用pip freeze --local生成. 本着"只安裝需要的模塊"的原則, 不同的設 置文件, 應當對應不同的requirements.txt文件. 就像分離式的settings文件一樣, 我們使用分離式的requirements文件. 首先我們刪除前文中提到的repository_root中的requirements.txt, 然後建立requirements目錄:
requirements/ base.txt local.txt production.txt
在base.txt中, 儲存的是所有開發環境中都會用到的依賴庫, 例如:
Django==1.6.5 psycopg2==2.5.3 South==0.8.4
在local.txt中, 儲存的是本地開發時用到的依賴庫:
# 導入base.txt中的依賴庫 -r base.txt coverage==3.7.1 django-debug-toolbar==1.2
當重新配置本地開發環境時, 可以使用以下代碼安裝依賴庫:
pip install -r requirements/local.txt
4. 設置文件中的文件路徑
我們強烈反對將絕對路徑寫入設置文件中, 因爲如果將絕對路徑寫入設置文件中的話, 遇到不同的環境, 就需要重新修改, 給開發和部署帶來了許多麻煩 和不確定性.
我們推薦使用Unipath來設置media, static和templates的 路徑. 這樣無論部署測試環境如何變化, media和templates文件夾的設置都不會有問題.
# settings/base.py 頂部 from unipath import Path BASE_DIR = Path(__file__).ancestor(3) MEDIA_ROOT = BASE_DIR.child("media") STAIC_ROOT = BASE_DIR.child("static") TEMPLATES_DIRS = ( BASE_DIR.child("templates"), )
鏈接:http://www.weiguda.com/blog/7/