應用程式¶
Django 包含已安裝應用程式的註冊表,該註冊表儲存配置並提供內省功能。它還維護可用模型的清單。
此註冊表稱為 apps
,它在 django.apps
中可用
>>> from django.apps import apps
>>> apps.get_app_config("admin").verbose_name
'Administration'
專案與應用程式¶
術語「專案」描述 Django 網頁應用程式。專案 Python 套件主要由設定模組定義,但通常包含其他內容。例如,當您執行 django-admin startproject mysite
時,您會得到一個 mysite
專案目錄,其中包含一個帶有 settings.py
、urls.py
、asgi.py
和 wsgi.py
的 mysite
Python 套件。專案套件通常會擴展為包含諸如夾具、CSS 和範本等,這些不屬於特定應用程式的內容。
專案的根目錄(包含 manage.py
的目錄)通常是專案所有未單獨安裝的應用程式的容器。
術語「應用程式」描述提供某些功能集的 Python 套件。應用程式可以在各種專案中重複使用。
應用程式包含模型、視圖、範本、範本標籤、靜態檔案、URL、中介軟體等的一些組合。它們通常使用 INSTALLED_APPS
設定以及其他機制(如 URLconf、MIDDLEWARE
設定或範本繼承)連入專案中。
重要的是要了解,Django 應用程式是一組與框架各個部分互動的程式碼。沒有所謂的 Application
物件。但是,在一些地方,Django 需要與已安裝的應用程式互動,主要用於配置和內省。這就是為什麼應用程式註冊表在每個已安裝應用程式的 AppConfig
實例中維護元資料的原因。
專案套件也可以被視為應用程式並擁有模型等,沒有任何限制(這將需要將其新增到 INSTALLED_APPS
中)。
配置應用程式¶
若要配置應用程式,請在應用程式內建立一個 apps.py
模組,然後在那裡定義 AppConfig
的子類別。
當 INSTALLED_APPS
包含應用程式模組的虛線路徑時,預設情況下,如果 Django 在 apps.py
子模組中找到一個 AppConfig
子類別,它會使用該配置來配置應用程式。可以透過將 AppConfig.default
設定為 False
來停用此行為。
如果 apps.py
模組包含多個 AppConfig
子類別,Django 會尋找 AppConfig.default
為 True
的單個子類別。
如果找不到 AppConfig
子類別,將使用基本 AppConfig
類別。
或者,INSTALLED_APPS
可以包含配置類別的虛線路徑來明確指定它
INSTALLED_APPS = [
...,
"polls.apps.PollsAppConfig",
...,
]
對於應用程式使用者¶
如果您在名為 anthology
的專案中使用「Rock 'n' roll」,但您希望它顯示為「Jazz Manouche」,則您可以提供自己的配置
# anthology/apps.py
from rock_n_roll.apps import RockNRollConfig
class JazzManoucheConfig(RockNRollConfig):
verbose_name = "Jazz Manouche"
# anthology/settings.py
INSTALLED_APPS = [
"anthology.apps.JazzManoucheConfig",
# ...
]
此範例顯示位於名為 apps.py
的子模組中的專案特定配置類別。這是一種慣例,而不是要求。AppConfig
子類別可以定義在任何地方。
在這種情況下,INSTALLED_APPS
必須包含配置類別的虛線路徑,因為它位於應用程式之外,因此無法自動偵測到。
應用程式配置¶
可配置屬性¶
- AppConfig.name¶
應用程式的完整 Python 路徑,例如
'django.contrib.admin'
。此屬性定義配置適用於哪個應用程式。必須在所有
AppConfig
子類別中設定。它在 Django 專案中必須是唯一的。
- AppConfig.label¶
應用程式的簡短名稱,例如
'admin'
當兩個應用程式的標籤衝突時,此屬性允許重新標記應用程式。它預設為
name
的最後一個元件。它應該是有效的 Python 識別符。它在 Django 專案中必須是唯一的。
警告
在應用程式套用遷移之後變更此屬性,將會導致專案發生重大變更,或者在可重複使用應用程式的情況下,也會導致該應用程式的任何現有安裝發生重大變更。這是因為在依賴項清單中參考應用程式時,資料庫表格和遷移檔案會使用
AppConfig.label
。
- AppConfig.verbose_name¶
應用程式的人性化名稱,例如「管理」。
此屬性預設為
label.title()
。
- AppConfig.path¶
應用程式目錄的檔案系統路徑,例如
'/usr/lib/pythonX.Y/dist-packages/django/contrib/admin'
。在大多數情況下,Django 可以自動偵測並設定此路徑,但您也可以在您的
AppConfig
子類別上提供明確的覆寫作為類別屬性。在少數情況下,這是必要的;例如,如果應用程式套件是一個具有多個路徑的命名空間套件。
- AppConfig.default¶
將此屬性設定為
False
以防止 Django 自動選取設定類別。當apps.py
僅定義一個AppConfig
子類別,但您不希望 Django 預設使用它時,這會很有用。將此屬性設定為
True
以告訴 Django 自動選取設定類別。當apps.py
定義多個AppConfig
子類別,而您希望 Django 預設使用其中一個時,這會很有用。預設情況下,不會設定此屬性。
- AppConfig.default_auto_field[原始碼]¶
要新增至此應用程式內模型的隱式主鍵類型。您可以使用此選項來為第三方應用程式保留
AutoField
作為主鍵類型。預設情況下,這是
DEFAULT_AUTO_FIELD
的值。
唯讀屬性¶
- AppConfig.module¶
應用程式的根模組,例如
<module 'django.contrib.admin' from 'django/contrib/admin/__init__.py'>
。
- AppConfig.models_module¶
包含模型的模組,例如
<module 'django.contrib.admin.models' from 'django/contrib/admin/models.py'>
。如果應用程式不包含
models
模組,則可能會是None
。請注意,資料庫相關的訊號 (例如pre_migrate
和post_migrate
) 僅針對具有models
模組的應用程式發出。
方法¶
- AppConfig.get_models(include_auto_created=False, include_swapped=False)[原始碼]¶
傳回此應用程式的
Model
類別的可迭代物件。需要完整填入應用程式註冊表。
- AppConfig.get_model(model_name, require_ready=True)[原始碼]¶
傳回具有給定
model_name
的Model
。model_name
不區分大小寫。如果此應用程式中不存在此模型,則會引發
LookupError
。除非將
require_ready
引數設定為False
,否則需要完整填入應用程式註冊表。require_ready
的行為與apps.get_model()
完全相同。
- AppConfig.ready()[原始碼]¶
子類別可以覆寫此方法以執行初始化工作,例如註冊訊號。它會在註冊表完全填入後立即呼叫。
雖然您無法在定義
AppConfig
類別的模組層級匯入模型,但您可以使用import
陳述式或get_model()
在ready()
中匯入它們。如果您正在註冊
model signals
,則可以使用其字串標籤來參照發送者,而不是使用模型類別本身。範例
from django.apps import AppConfig from django.db.models.signals import pre_save class RockNRollConfig(AppConfig): # ... def ready(self): # importing model classes from .models import MyModel # or... MyModel = self.get_model("MyModel") # registering signals with the model's string label pre_save.connect(receiver, sender="app_label.MyModel")
警告
雖然您可以如上所述存取模型類別,但請避免在您的
ready()
實作中與資料庫互動。這包括執行查詢的模型方法 (save()
、delete()
、管理員方法等等),以及透過django.db.connection
進行的原始 SQL 查詢。您的ready()
方法會在每個管理命令啟動期間執行。例如,即使測試資料庫設定與生產設定分開,manage.py test
仍然會對您的生產資料庫執行一些查詢!注意
在通常的初始化過程中,Django 只會呼叫
ready
方法一次。但在某些特殊情況下,特別是在修改已安裝應用程式的測試中,可能會多次呼叫ready
。在這種情況下,請編寫冪等方法,或在您的AppConfig
類別上設定一個旗標,以防止重新執行應該只執行一次的程式碼。
將命名空間套件作為應用程式¶
沒有 __init__.py
檔案的 Python 套件稱為「命名空間套件」,並且可能會分散在 sys.path
上不同位置的多個目錄中 (請參閱 PEP 420)。
Django 應用程式需要一個基本檔案系統路徑,Django (根據組態) 會在此路徑中搜尋範本、靜態資產等。因此,只有在符合下列其中一個條件時,命名空間套件才可以是 Django 應用程式
命名空間套件實際上只有一個位置 (即,不會分散在多個目錄中)。
用來設定應用程式的
AppConfig
類別具有path
類別屬性,此屬性是 Django 將用作應用程式單一基本路徑的絕對目錄路徑。
如果以上任一條件皆不符合,Django 會拋出 ImproperlyConfigured
錯誤。
應用程式註冊表¶
- apps¶
應用程式註冊表提供以下公開 API。未在下方列出的方法視為私有方法,可能會在不另行通知的情況下變更。
- apps.ready¶
此布林屬性在註冊表完全填入且所有
AppConfig.ready()
方法皆呼叫後會設定為True
。
- apps.get_app_config(app_label)¶
返回指定
app_label
的應用程式的AppConfig
。如果沒有此應用程式,則會拋出LookupError
錯誤。
- apps.is_installed(app_name)¶
檢查註冊表中是否存在具有指定名稱的應用程式。
app_name
是應用程式的完整名稱,例如'django.contrib.admin'
。
- apps.get_model(app_label, model_name, require_ready=True)¶
返回具有指定
app_label
和model_name
的Model
。作為快捷方式,此方法也接受單個參數,格式為app_label.model_name
。model_name
不區分大小寫。如果沒有此應用程式或模型,則會拋出
LookupError
錯誤。當使用不包含正好一個點的單個參數呼叫時,會拋出ValueError
錯誤。除非將
require_ready
參數設定為False
,否則需要完全填入應用程式註冊表。將
require_ready
設定為False
允許在 填入應用程式註冊表時 查詢模型,尤其是在匯入模型的第二階段。然後get_model()
的效果與匯入模型相同。主要的使用案例是使用設定檔配置模型類別,例如AUTH_USER_MODEL
。當
require_ready
為False
時,get_model()
返回的模型類別可能不完全正常運作(例如,反向存取器可能遺失),直到應用程式註冊表完全填入。因此,盡可能將require_ready
保留為預設值True
是最好的做法。
初始化過程¶
如何載入應用程式¶
當 Django 啟動時,django.setup()
負責填入應用程式註冊表。
- setup(set_prefix=True)[原始碼]¶
透過以下方式配置 Django:
載入設定。
設定記錄。
如果
set_prefix
為 True,則將 URL 解析器腳本前綴設定為FORCE_SCRIPT_NAME
(如果已定義),否則設定為/
。初始化應用程式註冊表。
此函式會自動呼叫:
透過 Django 的 ASGI 或 WSGI 支援執行 HTTP 伺服器時。
呼叫管理命令時。
在其他情況下,例如在純 Python 腳本中,必須明確呼叫此函式。
在 Django 5.0 中變更當應用程式在應用程式註冊表完全填入之前與資料庫互動時,會拋出
RuntimeWarning
警告。
應用程式註冊表分三個階段初始化。在每個階段,Django 會按照 INSTALLED_APPS
的順序處理所有應用程式。
首先,Django 會匯入
INSTALLED_APPS
中的每個項目。如果是應用程式配置類別,則 Django 會匯入應用程式的根套件,由其
name
屬性定義。如果是 Python 套件,則 Django 會在apps.py
子模組中尋找應用程式配置,否則會建立預設的應用程式配置。在此階段,您的程式碼不應匯入任何模型!
換句話說,您應用程式的根套件以及定義您應用程式配置類別的模組,都不應匯入任何模型,即使是間接匯入也不行。
嚴格來說,Django 允許在載入應用程式配置後匯入模型。但是,為了避免對
INSTALLED_APPS
的順序施加不必要的限制,強烈建議在此階段不要匯入任何模型。此階段完成後,即可使用對應用程式配置進行操作的 API,例如
get_app_config()
。然後,Django 會嘗試匯入每個應用程式的
models
子模組(如果有的話)。您必須在應用程式的
models.py
或models/__init__.py
中定義或匯入所有模型。否則,應用程式註冊表可能不會在此時完全填入,這可能會導致 ORM 無法正常運作。此階段完成後,即可使用對模型進行操作的 API,例如
get_model()
。最後,Django 會執行每個應用程式配置的
ready()
方法。
疑難排解¶
以下是在初始化期間可能會遇到的一些常見問題:
AppRegistryNotReady
:當匯入應用程式配置或模型模組觸發依賴應用程式註冊表的程式碼時,會發生此情況。例如,
gettext()
使用應用程式註冊表來查詢應用程式中的翻譯目錄。若要在匯入時進行翻譯,您需要改用gettext_lazy()
。(使用gettext()
會是個錯誤,因為翻譯會在匯入時發生,而不是在每次請求時依據使用中的語言發生。)在模型模組中於匯入時執行 ORM 的資料庫查詢也會觸發此例外。在所有模型都可用之前,ORM 無法正常運作。
如果您忘記在獨立 Python 腳本中呼叫
django.setup()
,也會發生此例外。ImportError: cannot import name ...
:如果匯入序列最終陷入循環,則會發生此情況。為了解決此類問題,您應盡量減少模型模組之間的依賴關係,並在匯入時盡可能少做工作。為了避免在匯入時執行程式碼,您可以將其移動到函式中並快取其結果。程式碼會在您第一次需要其結果時執行。此概念稱為「延遲求值」。
django.contrib.admin
會自動執行已安裝應用程式中admin
模組的自動探索。若要防止此行為,請變更您的INSTALLED_APPS
,使其包含'django.contrib.admin.apps.SimpleAdminConfig'
,而不是'django.contrib.admin'
。RuntimeWarning: Accessing the database during app initialization is discouraged.
這個警告會在應用程式準備好之前執行資料庫查詢時觸發,例如在模組導入期間或AppConfig.ready()
方法中。不建議過早進行資料庫查詢,因為這些查詢會在每個管理指令啟動時執行,這會減慢您的專案啟動速度,可能會快取過時的資料,甚至在有待處理的遷移時可能會失敗。例如,一個常見的錯誤是進行資料庫查詢來填充表單欄位選項
class LocationForm(forms.Form): country = forms.ChoiceField(choices=[c.name for c in Country.objects.all()])
在上面的範例中,由於迭代了
QuerySet
,因此Country.objects.all()
的查詢會在模組導入期間執行。為了避免警告,表單可以使用ModelChoiceField
來替代class LocationForm(forms.Form): country = forms.ModelChoiceField(queryset=Country.objects.all())
為了更容易找到觸發此警告的程式碼,您可以讓 Python 將警告視為錯誤 以顯示堆疊追蹤,例如使用
python -Werror manage.py shell
。