应用程序
Django 包含一个已安装应用程序的注册表,能够存储配置和内省。它还维护着一个可用的 模型 列表。
这个注册表叫做 app
,它在 django.app
中可用:
>>> from django.apps import apps
>>> apps.get_app_config("admin").verbose_name
'Administration'
项目和应用程序
术语 项目 描述了一个 Django 网络应用。项目的 Python 包主要是由一个配置模块定义的,但它通常包含其他东西。例如,当你运行 django-admin startproject mysite
时,你会得到一个 mysite
项目目录,其中包含一个 mysite
的 Python 包,其中有 settings.py
、urls.py
、asgi.py
和 wsgi.py
。该项目包经常被扩展到包括像辅助工具、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",
...,
]
对于应用程序作者
如果你正在创建一个名为 “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
对于应用程序使用者
项目中直接使用 “Rock ’n’ roll”,其名字会是 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
应用程序容易被人理解的名称,如 “Administration”。
此属性默认值为 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 类的模型层导入模型,但可以在 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() 实现中与数据库交互。这包括了那些会通过 django.db.connection
执行查询 (save(), delete(),管理器方法,等等)和原生查询的模型方法。你的 ready() 方法会在每个管理命令初始化阶段被执行。例如,虽然测试数据库的配置与生成环境配置是分开的, manager.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)
返回 Model 于给定的 app_label
和 model_name
。作为快捷方式,本方法也接受一个单一参数,形式为 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 根据应用在 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() 会是一个 bug,因为翻译会在导入时进行,而不是在每次请求时根据活动语言进行。)
在模型模块中导入时用 ORM 执行数据库查询也会触发此异常。在所有模型都可用之前,ORM 无法正常工作。
如果你忘记在一个单独的 Python 脚本中调用函数 django.setup(),也会发生异常。
ImportError: cannot import name ...
如果导入序列陷入循环,就会发生这种情况。为了消除这些问题,你应该最大限度地减少模型模块之间的依赖关系,并在导入时尽可能减少复杂度。 为了避免在导入时执行代码,可以将其移入函数并缓存结果。 代码将在你首次需要结果时执行。 这个概念被称为“惰性求值”。
django.contrib.admin
会自动在已安装的应用程序中执行admin
模块的自动发现。为了防止这种情况发生,请将你的 INSTALLED_APPS 改为包含'django.contrib.admin.app.SimpleAdminConfig'
而不是'django.contrib.admin'
。