应用程序¶
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”,其名字会是 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
必须包含配置类的点分隔路径,因为它位于应用程序之外,因此无法自动检测。
应用程序配置¶
可配置属性¶
- AppConfig.name¶
指向此应用程序的完整的 Python 格式的路径,如
'django.contrib.admin'
。此属性定义配置适用的应用程序。每个
AppConfig
子类都必须包含此项。它必须在整个 Django 项目中唯一。
- AppConfig.label¶
应用程序简称,如
'admin'
此属性允许在两个应用标签冲突时重命名其中一个的标签名。默认是
name
的最后一段。必须是一个有效的 Python 标识符。它必须在整个 Django 项目中唯一。
Warning
Changing this attribute after migrations have been applied for an application will result in breaking changes to a project or, in the case of a reusable app, any existing installs of that app. This is because
AppConfig.label
is used in database tables and migration files when referencing an app in the dependencies list.
- 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[source]¶
隐式主键类型,用于添加到本应用中的模型。你可以用它来保持
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)[source]¶
为该应用返回一个可迭代的
Model
类。要求完整填写应用注册表。
- AppConfig.get_model(model_name, require_ready=True)[source]¶
返回给出的
model_name
的Model
。model_name
是大小写敏感的。如果应用程序中不存在此模块,则抛出
LookupError
异常。除
require_ready
参数为False
的情况下,都必须完整设置注册信息。require_ready
行为与apps.get_model()
一致。
- AppConfig.ready()[source]¶
子类可以重写此方法来执行类似注册信号的初始化任务。只要注册表被填满就会调用此方法。
虽然你不能在定义
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")
Warning
尽管可以向上面介绍的那样访问模型类,但是要避免在
ready()
实现中与数据库交互。这包括了那些会通过django.db.connection
执行查询 (save()
,delete()
,管理器方法,等等)和原生查询的模型方法。你的ready()
方法会在每个管理命令初始化阶段被执行。例如,虽然测试数据库的配置与生成环境配置是分开的,manager.py test
仍会对 生产环境 数据库执行一些查询操作。Note
在普通的初始化进程中,
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_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)[source]¶
配置 Django:
加载配置。
设置日志。
若
set_prefix
为 True,为 URL 处理器脚本增加前缀FORCE_SCRIPT_NAME
,若未定义此项,则使用/
。初始化应用程序注册。
这个函数会被自动调用:
当通过Django的ASGI或WSGI运行HTTP服务时。
调用一个管理命令时。
在其他情况下,必须显式调用它,例如在纯 Python 脚本中。
Changed in Django 5.0:在应用程序与数据库交互之前,如果应用程序注册表尚未完全填充,则会引发
RuntimeWarning
。
应用注册的初始化过程分三个阶段完成。在每个阶段,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'
。RuntimeWarning: Accessing the database during app initialization is discouraged.
此警告是在应用程序准备好之前执行的数据库查询触发的,例如在模块导入期间或在AppConfig.ready()
方法中。不鼓励这种过早的数据库查询,因为它们将在每个管理命令的启动期间运行,这会减慢项目的启动速度,可能会缓存过时的数据,甚至可能会在迁移未完成时失败。例如,一个常见的错误是进行数据库查询以填充表单字段的选择项:
class LocationForm(forms.Form): country = forms.ChoiceField(choices=[c.name for c in Country.objects.all()])
在上面的示例中,来自
Country.objects.all()
的查询在模块导入期间执行,因为QuerySet
被迭代。为了避免警告,表单可以使用ModelChoiceField
,如下所示:class LocationForm(forms.Form): country = forms.ModelChoiceField(queryset=Country.objects.all())
为了更容易找到触发此警告的代码,你可以让 Python 将警告视为错误,以显示堆栈跟踪,例如使用
python -Werror manage.py shell
。