应用程序 — Django 文档

来自菜鸟教程
Django/docs/2.2.x/ref/applications
跳转至:导航、​搜索

应用程序

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

这个注册表简称为 apps,它在 django.apps 中可用:

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

项目和应用程序

术语 project 描述了 Django Web 应用程序。 项目 Python 包主要由设置模块定义,但它通常包含其他内容。 例如,当您运行 django-admin startproject mysite 时,您将获得一个 mysite 项目目录,其中包含一个 mysite Python 包,其中包含 settings.pyurls.py , 和 wsgi.py。 项目包通常会扩展为包含不绑定到特定应用程序的夹具、CSS 和模板等内容。

项目的根目录 (包含 manage.py 的目录)通常是所有未单独安装的项目应用程序的容器。

术语 application 描述了一个提供一些功能集的 Python 包。 应用程序 可以在各种项目中重复使用

应用程序包括模型、视图、模板、模板标签、静态文件、URL、中间件等的某种组合。 它们通常通过 :setting:`INSTALLED_APPS` 设置和其他机制(如 URLconfs、:setting:`MIDDLEWARE` 设置或模板继承)连接到项目中。

Django 应用程序只是一组与框架的各个部分交互的代码,理解这一点很重要。 没有 Application 对象这样的东西。 但是,Django 需要在一些地方与已安装的应用程序进行交互,主要用于配置和自省。 这就是应用程序注册表为每个已安装应用程序在 AppConfig 实例中维护元数据的原因。

没有限制项目包不能也被视为应用程序并具有模型等。 (这需要将它添加到 :setting:`INSTALLED_APPS`)。


配置应用程序

要配置应用程序,子类 AppConfig 并将该子类的虚线路径放在 :setting:`INSTALLED_APPS` 中。

:setting:`INSTALLED_APPS` 只包含应用程序模块的虚线路径时,Django 会检查该模块中的 default_app_config 变量。

如果已定义,则它是该应用程序的 AppConfig 子类的虚线路径。

如果没有 default_app_config,Django 使用基本的 AppConfig 类。

default_app_config 允许早于 Django 1.7 的应用程序,例如 django.contrib.admin 选择加入 AppConfig 功能,而无需用户更新他们的 :setting:`INSTALLED_APPS` ]。

新应用程序应避免 default_app_config。 相反,他们应该要求在 :setting:`INSTALLED_APPS` 中明确配置适当的 AppConfig 子类的虚线路径。

对于应用程序作者

如果您正在创建一个名为“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"

你可以让你的应用程序默认加载这个 AppConfig 子类,如下所示:

# rock_n_roll/__init__.py

default_app_config = 'rock_n_roll.apps.RockNRollConfig'

:setting:`INSTALLED_APPS` 只包含 'rock_n_roll' 时,这将导致使用 RockNRollConfig。 这允许您使用 AppConfig 功能,而无需您的用户更新他们的 :setting:`INSTALLED_APPS` 设置。 除了这个用例,最好避免使用 default_app_config,而是在 :setting:`INSTALLED_APPS` 中指定应用配置类,如下所述。

当然,您也可以告诉您的用户将 'rock_n_roll.apps.RockNRollConfig' 放在他们的 :setting:`INSTALLED_APPS` 设置中。 您甚至可以提供多个具有不同行为的不同 AppConfig 子类,并允许您的用户通过他们的 :setting:`INSTALLED_APPS` 设置来选择一个。

推荐的约定是将配置类放在名为 apps 的应用程序子模块中。 但是,这不是由 Django 强制执行的。

您必须为 Django 包含 name 属性以确定此配置适用于哪个应用程序。 您可以定义 AppConfig API 参考中记录的任何属性。

笔记

如果您的代码在应用程序的 __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 的子模块中定义特定于项目的配置类是约定,而不是要求。


应用程序配置

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 子类上的类属性。 在某些情况下,这是必需的; 例如,如果应用程序包是具有多个路径的 命名空间包


只读属性

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

返回此应用程序的 Model 类的可迭代对象。

要求完整填写应用注册表。

AppConfig.get_model(model_name, require_ready=True)

返回具有给定 model_nameModelmodel_name 不区分大小写。

如果此应用程序中不存在此类模型,则引发 LookupError

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

AppConfig.ready()

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

尽管您无法在定义了 AppConfig 类的模块级别导入模型,但您可以在 ready() 中使用 import 语句或 导入它们get_model()

如果您正在注册 模型信号 ,您可以通过其字符串标签而不是使用模型类本身来引用发送者。

例子:

from django.db.models.signals import pre_save

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 仍会针对您的 生产 数据库执行一些查询!

笔记

在通常的初始化过程中,ready 方法只会被 Django 调用一次。 但是在某些极端情况下,尤其是在摆弄已安装应用程序的测试中,可能会多次调用 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_name 形式的单个参数。 model_name 不区分大小写。

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

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

require_ready 设置为 False 允许在填充应用程序注册表 时查找模型 ,特别是在导入模型的第二阶段。 然后get_model()和导入模型效果一样。 主要用例是使用设置来配置模型类,例如 :setting:`AUTH_USER_MODEL`

require_readyFalse 时,get_model() 返回的模型类可能无法完全发挥作用(例如,反向访问器可能丢失),直到完全填充应用程序注册表。 因此,最好尽可能将 require_ready 保留为默认值 True


初始化进程

应用程序是如何被加载的

当 Django 启动时,django.setup() 负责填充应用程序注册表。

setup(set_prefix=True)

配置 Django:

  • 加载配置。

  • 设置日志。

  • 如果 set_prefix 为 True,则将 URL 解析器脚本前缀设置为 :setting:`FORCE_SCRIPT_NAME`(如果已定义),否则设置为 /

  • 初始化应用程序注册。

这个函数会被自动调用:

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

  • 调用一个管理命令时。

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

应用程序注册表分三个阶段进行初始化。 在每个阶段,Django 按照 :setting:`INSTALLED_APPS` 的顺序处理所有应用程序。

  1. 首先 Django 导入 :setting:`INSTALLED_APPS` 中的每个项目。

    如果是应用程序配置类,Django 会导入应用程序的根包,由其 name 属性定义。 如果是 Python 包,Django 会创建一个默认的应用程序配置。

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

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

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

    此阶段完成后,在应用程序配置(如 get_app_config())上运行的 API 就可以使用了。

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

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

    此阶段完成后,可在 get_model() 等模型上运行的 API 变得可用。

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


错误调试

在初始化期间,这里有一些常见的错误你可能会遇上。

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

    例如,gettext() 使用应用程序注册表在应用程序中查找翻译目录。 要在导入时进行翻译,您需要改为 gettext_lazy()。 (使用 gettext() 将是一个错误,因为翻译会在导入时发生,而不是根据活动语言在每个请求时发生。)

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

    如果您忘记在独立 Python 脚本中调用 django.setup(),也会发生此异常。

  • ImportError: cannot import name ... 如果导入序列以循环结束,则会发生这种情况。

    为了消除此类问题,您应该尽量减少模型模块之间的依赖关系,并在导入时尽可能少做工作。 为了避免在导入时执行代码,您可以将其移动到函数中并缓存其结果。 代码将在您第一次需要其结果时执行。 这个概念被称为“懒惰评估”。

  • django.contrib.admin 在已安装的应用程序中自动执行 admin 模块的自动发现。 为了防止它,改变你的 :setting:`INSTALLED_APPS` 以包含 'django.contrib.admin.apps.SimpleAdminConfig' 而不是 'django.contrib.admin'