应用

Django包含已安装应用程序的注册表,这些应用程序存储配置并提供自省。它还维护一个可用的列表 models .

这个注册表被称为 apps 它在以下位置提供 django.apps

>>> from django.apps import apps
>>> apps.get_app_config("admin").verbose_name
'Administration'

项目和应用

术语 项目 描述Django Web应用程序。项目python包主要由设置模块定义,但它通常包含其他内容。例如,当你跑步时 django-admin startproject mysite 你会得到一个 mysite 包含一个 mysite python包 settings.pyurls.pyasgi.pywsgi.py . 项目包通常被扩展为包括一些不与特定应用程序绑定的固定装置、CSS和模板。

A 项目的根目录 (包含 manage.py )通常是一个项目的所有应用程序的容器,这些应用程序不是单独安装的。

术语 应用 描述一个提供一些特性的python包。应用 may be reused 在各种项目中。

应用程序包括一些模型、视图、模板、模板标记、静态文件、URL、中间件等的组合。它们通常与 INSTALLED_APPS 设置并可选使用其他机制,如urlconfs, MIDDLEWARE 设置或模板继承。

了解Django应用程序是与框架的各个部分交互的一组代码,这一点很重要。根本就没有 Application 对象。然而,在一些地方,Django需要与已安装的应用程序进行交互,主要是为了配置和自省。这就是为什么应用程序注册表在 AppConfig 每个已安装应用程序的实例。

没有任何限制,项目包也不能被视为应用程序并具有模型等(这需要将其添加到 INSTALLED_APPS

配置应用程序

要配置应用程序,请创建 apps.py 模块,然后定义 AppConfig 在那里。

什么时候? INSTALLED_APPS 默认情况下,如果Django只找到一个应用程序模块,则包含指向该模块的虚线路径 AppConfig 中的子类 apps.py 子模块,它将该配置用于应用程序。可以通过设置禁用此行为 AppConfig.defaultFalse .

如果 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"

RockNRollConfigINSTALLED_APPS 包含 'rock_n_roll' . 如果你需要防止这种情况,设置 defaultFalse 在类定义中。

你可以提供几个 AppConfig 具有不同行为的子类。要告诉Django默认使用哪一个,请设置 defaultTrue 在它的定义中。如果用户希望选择非默认配置,则必须替换 'rock_n_roll' 在他们的 INSTALLED_APPS 设置。

这个 AppConfig.name 应用程序告诉哪个配置适用于Django。您可以定义中记录的任何其他属性 AppConfig API参考。

AppConfig 子类可以在任何地方定义。这个 apps.py 约定只允许Django在 INSTALLED_APPS 包含指向模块的路径,而不是指向类的路径。

备注

如果代码在应用程序的 __init__.py ,这个名字 apps 将与 apps 子模块。最佳实践是将代码移动到子模块并导入它。解决方法是以不同的名称导入注册表:

from django.apps import apps as django_apps

对于应用程序用户

如果你在一个叫做 anthology ,但是您希望它显示为“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.verbose_name

应用程序的可读名称,例如“管理”。

此属性默认为 label.title() .

AppConfig.path

应用程序目录的文件系统路径,例如 '/usr/lib/pythonX.Y/dist-packages/django/contrib/admin' .

在大多数情况下,Django可以自动检测和设置该属性,但您也可以在 AppConfig 子类。在一些情况下,这是必需的;例如,如果应用程序包是 namespace package 有多条路径。

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'> .

它可能是 None 如果应用程序不包含 models 模块。注意与数据库相关的信号,如 pre_migratepost_migrate 仅对具有 models 模块。

方法

AppConfig.get_models(include_auto_created=False, include_swapped=False)[源代码]

返回的iterable Model 此应用程序的类。

需要完全填充应用程序注册表。

AppConfig.get_model(model_name, require_ready=True)[源代码]

返回 Model 用给定的 model_name . model_name 不区分大小写。

加薪 LookupError 如果此应用程序中不存在此类模型。

需要完全填充应用程序注册表,除非 require_ready 参数设置为 False . require_ready 行为与 apps.get_model() .

AppConfig.ready()[源代码]

子类可以重写此方法以执行初始化任务,如注册信号。一旦注册表完全填充,就调用它。

虽然您不能在模块级导入模型,但是 AppConfig 类已定义,可以将它们导入 ready() ,使用 import 语句或 get_model() .

如果你在注册 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 . 你的 ready() 方法将在每个管理命令的启动过程中运行。例如,即使测试数据库配置与生产设置分开, manage.py test 仍然会对您的 生产 数据库!

备注

在通常的初始化过程中, ready 方法只被Django调用一次。但在一些特殊情况下,特别是在摆弄安装的应用程序的测试中, ready 可能会被多次调用。在这种情况下,要么编写幂等方法,要么在 AppConfig 类来防止重新运行应该只执行一次的代码。

作为应用程序的命名空间包

没有 __init__.py 文件被称为“名称空间包”,可以分布在多个目录中的不同位置 sys.path (见 PEP 420

Django应用程序需要一个单一的基本文件系统路径,其中Django(取决于配置)将搜索模板、静态资产等。因此,只有在以下情况之一为真时,命名空间包才可能是Django应用程序:

  1. 名称空间包实际上只有一个位置(即不分布在多个目录中)。

  2. 这个 AppConfig 用于配置应用程序的类具有 path class属性,它是绝对目录路径django将用作应用程序的单基路径。

如果这两个条件都不满足,Django将提高 ImproperlyConfigured .

应用程序注册表

apps

应用程序注册表提供以下公共API。以下未列出的方法被视为私有方法,可能会更改,恕不另行通知。

apps.ready

设置为的布尔属性 True 在注册表完全填充之后 AppConfig.ready() 方法被调用。

apps.get_app_configs()

返回的iterable AppConfig 实例。

apps.get_app_config(app_label)

返回一个 AppConfig 对于给定的应用程序 app_label . 加薪 LookupError 如果不存在此类应用程序。

apps.is_installed(app_name)

检查注册表中是否存在具有给定名称的应用程序。 app_name 是应用程序的全名,例如 'django.contrib.admin' .

apps.get_model(app_label, model_name, require_ready=True)

返回 Model 用给定的 app_labelmodel_name . 作为快捷方式,此方法还接受表单中的单个参数 app_label.model_name . model_name 不区分大小写。

加薪 LookupError 如果不存在此类应用程序或模型。加薪 ValueError 当用不包含一个点的单个参数调用时。

需要完全填充应用程序注册表,除非 require_ready 参数设置为 False .

设置 require_readyFalse 允许查找模型 while the app registry is being populated 特别是在导入模型的第二个阶段。然后 get_model() 与导入模型的效果相同。主要的用例是使用设置配置模型类,例如 AUTH_USER_MODEL .

什么时候? require_readyFalseget_model() 返回在应用程序注册表完全填充之前可能无法完全正常工作的模型类(例如,反向访问器可能丢失)。因此,最好离开 require_ready 默认值为 True 只要可能。

初始化过程

如何加载应用程序

当Django开始的时候, django.setup() 负责填充应用程序注册表。

setup(set_prefix=True)[源代码]

通过以下方式配置Django:

  • 正在加载设置。

  • 正在设置日志记录。

  • 如果 set_prefix 为true,将URL冲突解决程序脚本前缀设置为 FORCE_SCRIPT_NAME 如果定义,或 / 否则。

  • 正在初始化应用程序注册表。

此函数自动调用:

  • 当通过Django的ASGI或WSGI支持运行HTTP服务器时。

  • 调用管理命令时。

在其他情况下必须显式调用它,例如在纯Python脚本中。

Changed in Django 5.0:

引发了一个 RuntimeWarning 当应用程序在应用程序注册表完全填充之前与数据库交互时。

应用程序注册表分三个阶段初始化。在每个阶段,Django按照以下顺序处理所有应用程序: INSTALLED_APPS .

  1. 第一个django导入 INSTALLED_APPS .

    如果是应用程序配置类,Django将导入应用程序的根包,该根包由 name 属性。如果是Python包,Django在 apps.py 子模块,否则将创建默认应用程序配置。

    在这个阶段,您的代码不应该导入任何模型!

    换句话说,应用程序的根包和定义应用程序配置类的模块不应该导入任何模型,甚至是间接导入。

    严格来说,Django允许在加载应用程序配置后导入模型。但是,为了避免不必要的限制 INSTALLED_APPS ,强烈建议在此阶段不要导入任何模型。

    完成此阶段后,在应用程序配置(如 get_app_config() 变得有用。

  2. 然后Django尝试导入 models 每个应用程序的子模块(如果有)。

    必须定义或导入应用程序中的所有模型 models.pymodels/__init__.py . 否则,此时可能无法完全填充应用程序注册表,这可能导致ORM故障。

    一旦这个阶段完成,在诸如 get_model() 变得有用。

  3. 最后Django运行了 ready() 每个应用程序配置的方法。

故障排除

以下是初始化过程中可能遇到的一些常见问题:

  • AppRegistryNotReady :当导入应用程序配置或模型模块触发依赖于应用程序注册表的代码时,会发生这种情况。

    例如, gettext() 使用应用程序注册表在应用程序中查找翻译目录。要在导入时翻译,您需要 gettext_lazy() 相反。(使用) gettext() 这将是一个错误,因为转换将在导入时发生,而不是在每个请求时发生,具体取决于活动语言。)

    在模型模块中导入时使用ORM执行数据库查询也将触发此异常。在所有模型都可用之前,ORM无法正常工作。

    如果忘记调用 django.setup() 在独立的python脚本中。

  • 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()])
    

    在上面的示例中,来自 Country.objects.all() 在模块导入期间执行,因为 QuerySet 都被迭代了。若要避免该警告,该窗体可以使用 ModelChoiceField 相反::

    class LocationForm(forms.Form):
        country = forms.ModelChoiceField(queryset=Country.objects.all())
    

    为了更容易找到触发此警告的代码,您可以将 treat warnings as errors 要显示堆栈跟踪,请执行以下操作 python -Werror manage.py shell