如何設置django項目的設置(settings.py)和部署文件(requirements.txt)

在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/
發佈了11 篇原創文章 · 獲贊 72 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章