应用¶
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
项目目录,其中包含一个具有settings.py
、urls.py
、asgi.py
和wsgi.py
的mysite
Python 包。项目包通常会扩展为包含诸如与特定应用程序无关的固定装置、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",
...,
]
面向应用使用者¶
如果您在一个名为anthology
的项目中使用“摇滚乐”,但希望它显示为“爵士曼努什”,您可以提供您自己的配置。
# 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 项目中必须唯一。
警告
在应用已应用迁移之后更改此属性会导致项目发生重大更改,或者对于可重用应用程序而言,会导致该应用程序的任何现有安装发生重大更改。这是因为在引用依赖项列表中的应用程序时,
AppConfig.label
用于数据库表和迁移文件中。
- 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
类的模块级别导入模型,但是您可以使用import
语句或get_model()
在ready()
中导入它们。如果您正在注册
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()
实现中与数据库交互。这包括执行查询的模型方法(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_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()
与导入模型具有相同的效果。主要用例是使用设置配置模型类,例如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脚本中,必须显式调用它。
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
:当导入应用程序配置或models模块触发依赖于应用程序注册表的代码时,就会发生这种情况。例如,
gettext()
使用应用程序注册表在应用程序中查找翻译目录。要在导入时进行翻译,你需要使用gettext_lazy()
。(使用gettext()
将是一个错误,因为翻译将在导入时发生,而不是根据活动语言在每个请求时发生。)在models模块中导入时执行ORM的数据库查询也会触发此异常。在所有模型可用之前,ORM无法正常工作。
如果你忘记在独立的Python脚本中调用
django.setup()
,也会发生此异常。ImportError: cannot import name ...
如果导入顺序最终陷入循环,则会发生这种情况。为避免此类问题,应尽量减少模型模块之间的依赖关系,并在导入时尽可能少地执行操作。为了避免在导入时执行代码,可以将其移入函数并缓存其结果。代码将在您第一次需要其结果时执行。此概念称为“延迟求值”。
django.contrib.admin
会自动发现已安装应用程序中的admin
模块。要阻止此行为,请将您的INSTALLED_APPS
更改为包含'django.contrib.admin.apps.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
。