应用
Django 包含一个已安装应用配置记录,能够存储配置和内省。它同时还维护一个可用的 模型 列表。
这个注册表被简称为 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.py
, url.py
和 wsgi.py
。项目包通常被扩展成包含固件,CSS 和不与特定应用程序绑定的模板。
一个 项目的根目录 (包含 manage.py
文件的目录) 通常是所有项目应用程序的容器,应用不能独立安装。
术语 application 指的是提供了一些功能的 Python 包。应用 可在多个项目中重用。
应用程序包括模型,视图,模板,模板标签,静态文件,URL,中间件等的一些组合。它们通常使用 INSTALLED_APPS
选项加入到项目中,也可以使用其他机制,如 URLconf, MIDDLEWARE
配置或模板继承。
Django 应用程序只是一组与各个框架组件进行交互的代码。并没有一个 Application
对象。不过,Django 并不怎么需要与已安装的应用程序进行交互,主要用于配置和自检。这就是为什么应用程序注册表在每个已安装的应用程序的 AppConfig
实例中维护元数据的原因。
一个项目包可以自由的作为一个应用程序并包含一些模型等(前提是,需要把它加入 INSTALLED_APPS
)。
配置应用程序
配置一个应用程序,需要继承 AppConfig
,将子类的点式路径加入 INSTALLED_APPS
中。
当 INSTALLED_APPS
简单的包含当前路径到一个应用程序模块的路径时,Django 会检查该模块下的 default_app_config
变量。
如果已经定义,它是当前路径到到达该应用程序子类 AppConfig
的路径。
如果没有 default_app_config
,Django 会使用基类 AppConfig
。
default_app_config
允许 Django 1.7 之前的应用程序(例如 django.contrib.admin
)在不要求用户更新 INSTALLED_APPS
的情况下就可以加入 AppConfig
特性。
新的应用程序应该避免使用 default_app_config
。相反,它们应该在 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'
当 INSTALLED_APPS
仅包含 'rock_n_roll'
会使用 RockNRollConfig
。这允许你充分利用 AppConfig
的功能特性,无需使用你应用人去更新他们的 INSTALLED_APPS
配置。除此之外,最好避免使用 default_app_config
,而是使用后续介绍的在 INSTALLED_APPS
中指定应用配置类。
当然,也可以让你的用户把 'rock_n_roll.apps.RockNRollConfig' 放到他们的 INSTALLED_APPS
配置文件中。你甚至可以提供具有不同行为的 AppConfig
的子类,让用户选择他们所需要的,将其加入到 INSTALLED_APPS
配置中。
惯例是将配置类放在应用程序名为 apps
的子模块中。但是,这不是 Django 强制规定的。
你必须包含 attr:~django.apps.AppConfig.name 属性,Django 用它决定这个配置会应用于哪个应用。你定义任何在 AppConfig
API 参考中记录的属性。
注解
若你在应用的 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',
- # ...
- ]
同样,在名为 app
的子模块中定义项目配置类是一种约定俗成的惯例,但不强求。
应用配置
可配置属性
此属性定义配置适用的应用程序。每个 'django.apps.AppConfig` 子类都必须包含此项。
它必须在整个 Django 项目中唯一。
此属性允许在两个应用标签冲突时重命名其中一个的标签名。默认是 name
的最后一段。必须是一个有效的 Python 标识符。
它必须在整个 Django 项目中唯一。
此属性默认值为 label.title()
。
大多数情况下,Django 能自动检测并设置此属性,但你也能在 AppConfig
子类中申明此属性,显式地重写它。很少情况下要这么做;例如,若应用包是一个拥有多个路径的 命名空间。
只读属性
AppConfig.
module
应用的根模块,如
<module 'django.contrib.admin' from 'django/contrib/admin/init.py'>
。- 包含模型的模块,如
<module 'django.contrib.admin.models' from 'django/contrib/admin/models.py'>
。
应用不包含 models
模块时,可能是 None
。注意,数据库关联的信号,例如 pre_migrate
和 post_migrate
仅在应用有 models
模块时发出。
方法
要求完整填写应用注册信息。
AppConfig.
getmodel
(_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.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()
实现中与数据库交互。这包括了那些会通过 django.db.connection
执行查询 (save()
, delete()
,管理器方法,等等) 和原生查询的模型方法。你的 ready()
方法会在每个管理命令初始化阶段被执行。例如,虽然测试数据库的配置与生成环境配置是分开的, manager.py test
仍会对 生产环境 数据库执行一些查询操作。
注解
在普通的初始化进程中, ready
方法仅被 Django 调用一次。但在一些特殊情况下,特别是针对已安装应用的测试中,可以会多次调用 ready
。这种情况下,在 AppConfig
类中编写幂等方法或放入一个标志,避免那些只需运行一次的代码被多次执行。
命名空间包作为应用程序
Python packages without an init.py
file are known as "namespacepackages" and may be spread across multiple directories at different locationson sys.path
(see PEP 420).
Django应用程序需要一个单一的基本文件系统路径,Django(取决于配置)将在其中搜索模板、静态资产等。因此,只有在以下情况之一为真时,命名空间包才可能是Django应用程序:
- 名称空间包实际上只有一个位置(即不分布在多个目录中)。
- The
AppConfig
class used to configure the applicationhas apath
class attribute, which is theabsolute directory path Django will use as the single base path for theapplication.If neither of these conditions is met, Django will raiseImproperlyConfigured
.
注册应用
apps
应用程序注册表提供以下公共API。以下未列出的方法被视为私有方法,可能会更改,恕不另行通知。
Boolean attribute that is set to
True
after the registry is fullypopulated and allAppConfig.ready()
methods are called.返回一个由django.apps.AppConfig类实例组成的可迭代对象
Returns an
AppConfig
for the application with thegivenapp_label
. RaisesLookupError
if no such applicationexists.Checks whether an application with the given name exists in the registry.
app_name
is the full name of the app, e.g.'django.contrib.admin'
.- Returns the
Model
with the givenapp_label
andmodel_name
. As a shortcut, this method also accepts a singleargument in the formapp_label.model_name
.model_name
iscase-insensitive.
Raises LookupError
if no such application or model exists. RaisesValueError
when called with a single argument that doesn't containexactly one dot.
Requires the app registry to be fully populated unless therequire_ready
argument is set to False
.
Setting require_ready
to False
allows looking up modelswhile the app registry is being populated,specifically during the second phase where it imports models. Thenget_model()
has the same effect as importing the model. The main usecase is to configure model classes with settings, such asAUTH_USER_MODEL
.
When require_ready
is False
, get_model()
returns a model classthat may not be fully functional (reverse accessors may be missing, forexample) until the app registry is fully populated. For this reason, it'sbest to leave require_ready
to the default value of True
wheneverpossible.
初始化进程
应用是如何被加载的
Django 启动后, django.setup()
负责配置应用注册信息。
setup
(set_prefix=True)[源代码]配置 Django:
- 加载配置。
- 设置日志。
- 若
set_prefix
为 True,为 URL 处理器脚本增加前缀FORCE_SCRIPT_NAME
,若未定义此项,则使用/
。 初始化应用注册这个函数会被自动调用。
当通过 Django 的 WSGI 支持运行 HTTP 服务。
- 调用一个管理命令时。在其他情况下,必须显式调用它,例如在纯 Python 脚本中。
应用注册的初始化过程分三个阶段完成。在每个阶段,Django 根据应用在 INSTALLED_APPS
中的顺序依次处理。
- 首先,Django 导入
INSTALLED_APPS
中各项条目。
If it's an application configuration class, Django imports the root packageof the application, defined by its name
attribute. Ifit's a Python package, Django creates a default application configuration.
在这情况下,你的代码不应该导入任何模型!
换句话说,应用程序的根包和定义应用程序配置类的模块不能导入任何模型,即使是间接导入。
严格来说,一旦应用配置完成加载后,Django 是允许导入模型。然而为了避免不必要的 INSTALLED_APPS
, 顺序约束。强烈建议在这个阶段不要导入任何模型。
一旦这个阶段完成,可以使用 API 对应用程序配置(如:meth:〜apps.get_app_config() )进行操作。
- 然后 Django 尝试导入每个存在模型的应用程序中,继承了
models
的所有子模型。
您必须在应用程序的“models.py”或“models / init 。py”中定义或导入所有模型。 否则,此时应用程序注册表可能不会完全加载,这可能会导致ORM出现故障。
此步骤完成后,操作模型的 API,例如 get_model()
,就可以使用了。
- 最后,Django运行每个应用程序配置的:meth:`〜AppConfig.ready()`方法。
错误调试
在初始化期间,这里有一些常见的错误你可能会遇上。
AppRegistryNotReady
: This happens whenimporting an application configuration or a models module triggers code thatdepends on the app registry.
For example, gettext()
uses the appregistry to look up translation catalogs in applications. To translate atimport time, you need gettext_lazy()
instead. (Using gettext()
would be a bug,because the translation would happen at import time, rather than at eachrequest depending on the active language.)
Executing database queries with the ORM at import time in models moduleswill also trigger this exception. The ORM cannot function properly until allmodels are available.
如果你忘记在一个单独的Python脚本中调用函数django.setup()
,也会发生异常。
ImportError: cannot import name …
This happens if the import sequenceends up in a loop.
为了消除这些问题,您应该最大限度地减少模型模块之间的依赖关系,并在导入时尽可能减少复杂度。 为了避免在导入时执行代码,可以将其移入函数并缓存结果。 代码将在您首次需要结果时执行。 这个概念被称为“惰性求值”。
django.contrib.admin
automatically performs autodiscovery ofadmin
modules in installed applications. To prevent it, change yourINSTALLED_APPS
to contain'django.contrib.admin.apps.SimpleAdminConfig'
instead of'django.contrib.admin'
.