應用程式

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.pyurls.pyasgi.pywsgi.pymysite 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.defaultTrue 的單個子類別。

如果找不到 AppConfig 子類別,將使用基本 AppConfig 類別。

或者,INSTALLED_APPS 可以包含配置類別的虛線路徑來明確指定它

INSTALLED_APPS = [
    ...,
    "polls.apps.PollsAppConfig",
    ...,
]

對於應用程式作者

如果您正在建立名為「Rock 'n' roll」的可外掛應用程式,以下是如何為管理員提供適當名稱的方法

# rock_n_roll/apps.py

from django.apps import AppConfig


class RockNRollConfig(AppConfig):
    name = "rock_n_roll"
    verbose_name = "Rock ’n’ roll"

INSTALLED_APPS 包含 'rock_n_roll' 時,會自動載入 RockNRollConfig。如果您需要阻止這種情況,請在類別定義中將 default 設定為 False

您可以提供具有不同行為的數個 AppConfig 子類別。若要告知 Django 預設要使用哪一個,請在其定義中將 default 設定為 True。如果您的使用者想要選擇非預設的配置,他們必須在他們的 INSTALLED_APPS 設定中將 'rock_n_roll' 替換為該特定類別的虛線路徑。

AppConfig.name 屬性會告知 Django 此配置適用於哪個應用程式。您可以定義 AppConfig API 參考文件中記載的任何其他屬性。

AppConfig 子類別可以定義在任何地方。apps.py 慣例僅允許 Django 在 INSTALLED_APPS 包含應用程式模組的路徑而不是配置類別的路徑時自動載入它們。

注意

如果您的程式碼在應用程式的 __init__.py 中匯入應用程式註冊表,則名稱 apps 會與 apps 子模組衝突。最佳做法是將該程式碼移至子模組並匯入它。一種解決方法是以不同的名稱匯入註冊表

from django.apps import apps as django_apps

對於應用程式使用者

如果您在名為 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 必須包含配置類別的虛線路徑,因為它位於應用程式之外,因此無法自動偵測到。

應用程式配置

class AppConfig[原始碼]

應用程式配置物件會儲存應用程式的元資料。某些屬性可以在 AppConfig 子類別中配置。其他屬性則由 Django 設定且為唯讀。

可配置屬性

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_migratepost_migrate) 僅針對具有 models 模組的應用程式發出。

方法

AppConfig.get_models(include_auto_created=False, include_swapped=False)[原始碼]

傳回此應用程式的 Model 類別的可迭代物件。

需要完整填入應用程式註冊表。

AppConfig.get_model(model_name, require_ready=True)[原始碼]

傳回具有給定 model_nameModelmodel_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 應用程式

  1. 命名空間套件實際上只有一個位置 (即,不會分散在多個目錄中)。

  2. 用來設定應用程式的 AppConfig 類別具有 path 類別屬性,此屬性是 Django 將用作應用程式單一基本路徑的絕對目錄路徑。

如果以上任一條件皆不符合,Django 會拋出 ImproperlyConfigured 錯誤。

應用程式註冊表

apps

應用程式註冊表提供以下公開 API。未在下方列出的方法視為私有方法,可能會在不另行通知的情況下變更。

apps.ready

此布林屬性在註冊表完全填入且所有 AppConfig.ready() 方法皆呼叫後會設定為 True

apps.get_app_configs()

返回 AppConfig 實例的可迭代物件。

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_labelmodel_nameModel。作為快捷方式,此方法也接受單個參數,格式為 app_label.model_namemodel_name 不區分大小寫。

如果沒有此應用程式或模型,則會拋出 LookupError 錯誤。當使用不包含正好一個點的單個參數呼叫時,會拋出 ValueError 錯誤。

除非將 require_ready 參數設定為 False,否則需要完全填入應用程式註冊表。

require_ready 設定為 False 允許在 填入應用程式註冊表時 查詢模型,尤其是在匯入模型的第二階段。然後 get_model() 的效果與匯入模型相同。主要的使用案例是使用設定檔配置模型類別,例如 AUTH_USER_MODEL

require_readyFalse 時,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 的順序處理所有應用程式。

  1. 首先,Django 會匯入 INSTALLED_APPS 中的每個項目。

    如果是應用程式配置類別,則 Django 會匯入應用程式的根套件,由其 name 屬性定義。如果是 Python 套件,則 Django 會在 apps.py 子模組中尋找應用程式配置,否則會建立預設的應用程式配置。

    在此階段,您的程式碼不應匯入任何模型!

    換句話說,您應用程式的根套件以及定義您應用程式配置類別的模組,都不應匯入任何模型,即使是間接匯入也不行。

    嚴格來說,Django 允許在載入應用程式配置後匯入模型。但是,為了避免對 INSTALLED_APPS 的順序施加不必要的限制,強烈建議在此階段不要匯入任何模型。

    此階段完成後,即可使用對應用程式配置進行操作的 API,例如 get_app_config()

  2. 然後,Django 會嘗試匯入每個應用程式的 models 子模組(如果有的話)。

    您必須在應用程式的 models.pymodels/__init__.py 中定義或匯入所有模型。否則,應用程式註冊表可能不會在此時完全填入,這可能會導致 ORM 無法正常運作。

    此階段完成後,即可使用對模型進行操作的 API,例如 get_model()

  3. 最後,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

返回頂部