在命令列執行有載入 django 相關模組的程式

剛用 django 時會有個困擾, 不透過 manage.py 無法執行有 import django modules 的 scripts。後來有找到 django-standalone, 可以方便在其它 scripts 內使用 django modules。不過我並不需要執行 manage.py 的其它 command, 而是單純想在命令列執行 scripts。以這需求來說, django-standalone 有點冗。

今天仔細地追了一下前因後果, 發覺解法很簡單, 必須做兩件事:
  • 設環境變數 DJANGO_SETTINGS_MODULE=APP.settings
  • 要在載入 module 的路徑中加上 django project 的根目錄, 即放 APP 的位置
所以若 myscript.py 有使用 django moduels (比方說 django.db.connection), 執行的方式就是:
$ PYTHONPATH=.. DJANGO_SETTINGS_MODULE=APP.settings myscript.py
myscript.py 位在 project root。明白運作方式後, 做起來很簡單。

不設 DJANGO_SETTINGS_MODULE 的話會出現下面的 exception:
Traceback (most recent call last):
  File "/.../site-packages/django/db/__init__.py", line 14, in 
    if not settings.DATABASES:
  File "/.../site-packages/django/utils/functional.py", line 276, in __getattr__
    self._setup()
  File "/.../site-packages/django/conf/__init__.py", line 38, in _setup
    raise ImportError("Settings cannot be imported, because environment variable %s is undefined." % ENVIRONMENT_VARIABLE)
ImportError: Settings cannot be imported, because environment variable DJANGO_SETTINGS_MODULE is undefined.
順著 traceback 看相關程式就會明白, django 會載入 DJANGO_SETTINGS_MODULE 作為整個 django 的設定檔。所以一定要設這變數, 而且載入 module 的路徑裡包念它的父目錄, 才能正確載入它。

若不想在命令列加那兩個變數, 一個簡單的替代方案是參考 manage.py, 在程式裡先 import settings, 再執行 django.core.management.execute_manager(settings)。execute_manager 會從 settings 中取得相關路徑和 module 名稱, 用來設 DJANGO_SETTINGS_MODULE 和在 sys.path 中附加 settings 的父目錄。

不明白為啥要用這種繞彎的方式設定環境, 也許和 django 的載入和執行模組的設計哲學有關。附帶一提, django 裡有太多 lazy initialization, 增加讀程式碼的難度, 令人有點困擾。

留言

這個網誌中的熱門文章

(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫

熟悉系統工具好處多多

virtualbox 使用 USB 裝置