应用程序 — Django 文档
应用
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.py
、urls.py
, asgi.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 子类的虚线路径。
对于应用程序用户
如果您在名为 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_migrate 和 post_migrate)仅针对具有models
模块的应用程序发出。
方法
- AppConfig.get_models()
返回此应用程序的 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 类的模块级别导入模型,但您可以在
ready()
中使用import
语句或 导入它们get_model()。如果您正在注册 模型信号 ,您可以通过其字符串标签而不是使用模型类本身来引用发送者。
例子:
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
仍会针对您的 生产 数据库执行一些查询!笔记
在通常的初始化过程中,
ready
方法只会被 Django 调用一次。 但是在某些极端情况下,尤其是在摆弄已安装应用程序的测试中,可能会多次调用ready
。 在这种情况下,要么编写幂等方法,要么在您的AppConfig
类上放置一个标志,以防止重新运行应该只执行一次的代码。
命名空间包作为应用程序
没有 __init__.py
文件的 Python 包被称为“命名空间包”,可能分布在 sys.path
上不同位置的多个目录中(参见 PEP 420 ])。
Django 应用程序需要一个基本文件系统路径,Django(取决于配置)将在其中搜索模板、静态资产等。 因此,如果满足以下任一条件,命名空间包可能只是 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_label
和model_name
的 Model。 作为一种快捷方式,此方法还接受app_label.model_name
形式的单个参数。model_name
不区分大小写。如果不存在此类应用程序或模型,则引发
LookupError
。 当使用不包含一个点的单个参数调用时引发ValueError
。除非
require_ready
参数设置为False
,否则需要完全填充应用程序注册表。将
require_ready
设置为False
允许在填充应用程序注册表 时查找模型 ,特别是在导入模型的第二阶段。 然后get_model()
和导入模型效果一样。 主要用例是使用设置来配置模型类,例如 :setting:`AUTH_USER_MODEL`。当
require_ready
为False
时,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` 的顺序处理所有应用程序。
首先 Django 导入 :setting:`INSTALLED_APPS` 中的每个项目。
如果是应用程序配置类,Django 会导入应用程序的根包,由其 name 属性定义。 如果是 Python 包,Django 会创建一个默认的应用程序配置。
在此阶段,您的代码不应导入任何模型!
换句话说,您的应用程序的根包和定义您的应用程序配置类的模块不应导入任何模型,即使是间接导入。
严格来说,Django 允许在加载应用程序配置后导入模型。 但是,为了避免对:setting:`INSTALLED_APPS`的顺序进行不必要的限制,强烈建议在此阶段不要导入任何模型。
此阶段完成后,在应用程序配置(如 get_app_config())上运行的 API 就可以使用了。
然后 Django 尝试导入每个应用程序的
models
子模块,如果有的话。您必须在应用程序的
models.py
或models/__init__.py
中定义或导入所有模型。 否则,此时应用程序注册表可能未完全填充,这可能导致 ORM 出现故障。此阶段完成后,可在 get_model() 等模型上运行的 API 变得可用。
最后 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'
。