应用程序
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
)。
配置应用程序
To configure an application, create an apps.py
module inside the application, then define a subclass of AppConfig
there.
When INSTALLED_APPS
contains the dotted path to an application module, by default, if Django finds exactly one AppConfig
subclass in the apps.py
submodule, it uses that configuration for the application. This behavior may be disabled by setting AppConfig.default
to False
.
If the apps.py
module contains more than one AppConfig
subclass, Django will look for a single one where AppConfig.default
is True
.
If no AppConfig
subclass is found, the base AppConfig
class will be used.
Alternatively, INSTALLED_APPS
may contain the dotted path to a configuration class to specify it explicitly:
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"
RockNRollConfig
will be loaded automatically when INSTALLED_APPS
contains 'rock_n_roll'
. If you need to prevent this, set default
to False
in the class definition.
You can provide several AppConfig
subclasses with different behaviors. To tell Django which one to use by default, set default
to True
in its definition. If your users want to pick a non-default configuration, they must replace 'rock_n_roll'
with the dotted path to that specific class in their INSTALLED_APPS
setting.
The AppConfig.name
attribute tells Django which application this configuration applies to. You can define any other attribute documented in the AppConfig
API reference.
AppConfig
subclasses may be defined anywhere. The apps.py
convention merely allows Django to load them automatically when INSTALLED_APPS
contains the path to an application module rather than the path to a configuration class.
注解
若你在应用的 __init__.py
中导入了应用注册信息,名称 apps
会与子模块 apps
冲突。最好的办法是将此段带入移入子模块,再导入它。折中方案是导入后取个别名:
from django.apps import apps as django_apps
Changed in Django Development version:
In previous versions, a default_app_config
variable in the application module was used to identify the default application configuration class.
对于应用程序使用者
项目中直接使用 “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',
# ...
]
This example shows project-specific configuration classes located in a submodule called apps.py
. This is a convention, not a requirement. AppConfig
subclasses may be defined anywhere.
In this situation, INSTALLED_APPS
must contain the dotted path to the configuration class because it lives outside of an application and thus cannot be automatically detected.
应用程序配置
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
New in Django Development version.
Set this attribute to False
to prevent Django from selecting a configuration class automatically. This is useful when apps.py
defines only one AppConfig
subclass but you don’t want Django to use it by default.
Set this attribute to True
to tell Django to select a configuration class automatically. This is useful when apps.py
defines more than one AppConfig
subclass and you want Django to use one of them by default.
By default, this attribute isn’t set.
AppConfig.``default_auto_field
New in Django Development version.
The implicit primary key type to add to models within this app. You can use this to keep AutoField
as the primary key type for third party applications.
By default, this is the value of 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
()
为该应用返回一个可迭代的 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 的 WSGI 支持运行 HTTP 服务。
- 调用一个管理命令时。
在其他情况下,必须显式调用它,例如在纯 Python 脚本中。
应用注册的初始化过程分三个阶段完成。在每个阶段,Django 根据应用在 INSTALLED_APPS
中的顺序依次处理。
首先,Django 导入
INSTALLED_APPS
中各项条目。If it’s an application configuration class, Django imports the root package of the application, defined by its
name
attribute. If it’s a Python package, Django looks for an application configuration in anapps.py
submodule, or else creates a default application configuration.在这情况下,你的代码不应该导入任何模型!
换句话说,应用程序的根包和定义应用程序配置类的模块不能导入任何模型,即使是间接导入。
严格来说,一旦应用配置完成加载后,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'
。