Django包含已安装应用程序的注册表,这些应用程序存储配置并提供自省。它还维护一个可用的列表 models .
这个注册表被称为 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
, urls.py
, asgi.py
和 wsgi.py
. 项目包通常被扩展为包括一些不与特定应用程序绑定的固定装置、CSS和模板。
A 项目的根目录 (包含 manage.py
)通常是一个项目的所有应用程序的容器,这些应用程序不是单独安装的。
术语 应用 描述一个提供一些特性的python包。应用 may be reused 在各种项目中。
应用程序包括一些模型、视图、模板、模板标记、静态文件、URL、中间件等的组合。它们通常与 INSTALLED_APPS
设置并可选使用其他机制,如urlconfs, MIDDLEWARE
设置或模板继承。
了解Django应用程序是与框架的各个部分交互的一组代码,这一点很重要。根本就没有 Application
对象。然而,在一些地方,Django需要与已安装的应用程序进行交互,主要是为了配置和自省。这就是为什么应用程序注册表在 AppConfig
每个已安装应用程序的实例。
没有任何限制,项目包也不能被视为应用程序并具有模型等(这需要将其添加到 INSTALLED_APPS
)
要配置应用程序,请创建 apps.py
模块,然后定义 AppConfig
在那里。
什么时候? INSTALLED_APPS
默认情况下,如果Django只找到一个应用程序模块,则包含指向该模块的虚线路径 AppConfig
中的子类 apps.py
子模块,它将该配置用于应用程序。可以通过设置禁用此行为 AppConfig.default
到 False
.
如果 apps.py
模块包含多个 AppConfig
子类,Django将查找单个 AppConfig.default
是 True
.
如果没有 AppConfig
找到子类,基 AppConfig
将使用类。
或者, INSTALLED_APPS
可能包含指向配置类的虚线路径以显式指定它:
INSTALLED_APPS = [
...,
"polls.apps.PollsAppConfig",
...,
]
如果你在一个叫做 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
必须包含指向配置类的点路径,因为它位于应用程序之外,因此无法自动检测到。
应用程序的完整python路径,例如 'django.contrib.admin'
.
此属性定义配置应用于哪个应用程序。必须全部设置 AppConfig
子类。
它在Django项目中必须是独一无二的。
应用程序的简称,例如 'admin'
此属性允许在两个应用程序具有冲突标签时重新标记应用程序。它默认为的最后一个组件 name
. 它应该是有效的python标识符。
它在Django项目中必须是独一无二的。
应用程序的可读名称,例如“管理”。
此属性默认为 label.title()
.
应用程序目录的文件系统路径,例如 '/usr/lib/pythonX.Y/dist-packages/django/contrib/admin'
.
在大多数情况下,Django可以自动检测和设置该属性,但您也可以在 AppConfig
子类。在一些情况下,这是必需的;例如,如果应用程序包是 namespace package 有多条路径。
将此属性设置为 False
以防止Django自动选择配置类。这在以下情况下很有用 apps.py
只定义一个 AppConfig
子类,但您不希望Django在默认情况下使用它。
将此属性设置为 True
告诉Django自动选择一个配置类。这在以下情况下很有用 apps.py
定义多个 AppConfig
子类,您希望Django在默认情况下使用其中一个。
默认情况下,不设置此属性。
要添加到此应用程序中的模型的隐式主键类型。你可以用这个来保存 AutoField
作为第三方应用程序的主键类型。
默认情况下,这是的值 DEFAULT_AUTO_FIELD
。
应用程序的根模块,例如 <module 'django.contrib.admin' from 'django/contrib/admin/__init__.py'>
.
包含模型的模块,例如 <module 'django.contrib.admin.models' from 'django/contrib/admin/models.py'>
.
它可能是 None
如果应用程序不包含 models
模块。注意与数据库相关的信号,如 pre_migrate
和 post_migrate
仅对具有 models
模块。
返回的iterable Model
此应用程序的类。
需要完全填充应用程序注册表。
返回 Model
用给定的 model_name
. model_name
不区分大小写。
加薪 LookupError
如果此应用程序中不存在此类模型。
需要完全填充应用程序注册表,除非 require_ready
参数设置为 False
. require_ready
行为与 apps.get_model()
.
子类可以重写此方法以执行初始化任务,如注册信号。一旦注册表完全填充,就调用它。
虽然您不能在模块级导入模型,但是 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()
实施。这包括执行查询的模型方法 (save()
, delete()
,管理器方法等),以及通过 django.db.connection
. 你的 ready()
方法将在每个管理命令的启动过程中运行。例如,即使测试数据库配置与生产设置分开, manage.py test
仍然会对您的 生产 数据库!
备注
在通常的初始化过程中, ready
方法只被Django调用一次。但在一些特殊情况下,特别是在摆弄安装的应用程序的测试中, ready
可能会被多次调用。在这种情况下,要么编写幂等方法,要么在 AppConfig
类来防止重新运行应该只执行一次的代码。
没有 __init__.py
文件被称为“名称空间包”,可以分布在多个目录中的不同位置 sys.path
(见 PEP 420 )
Django应用程序需要一个单一的基本文件系统路径,其中Django(取决于配置)将搜索模板、静态资产等。因此,只有在以下情况之一为真时,命名空间包才可能是Django应用程序:
如果这两个条件都不满足,Django将提高 ImproperlyConfigured
.
应用程序注册表提供以下公共API。以下未列出的方法被视为私有方法,可能会更改,恕不另行通知。
设置为的布尔属性 True
在注册表完全填充之后 AppConfig.ready()
方法被调用。
返回一个 AppConfig
对于给定的应用程序 app_label
. 加薪 LookupError
如果不存在此类应用程序。
检查注册表中是否存在具有给定名称的应用程序。 app_name
是应用程序的全名,例如 'django.contrib.admin'
.
返回 Model
用给定的 app_label
和 model_name
. 作为快捷方式,此方法还接受表单中的单个参数 app_label.model_name
. model_name
不区分大小写。
加薪 LookupError
如果不存在此类应用程序或模型。加薪 ValueError
当用不包含一个点的单个参数调用时。
需要完全填充应用程序注册表,除非 require_ready
参数设置为 False
.
设置 require_ready
到 False
允许查找模型 while the app registry is being populated 特别是在导入模型的第二个阶段。然后 get_model()
与导入模型的效果相同。主要的用例是使用设置配置模型类,例如 AUTH_USER_MODEL
.
什么时候? require_ready
是 False
, get_model()
返回在应用程序注册表完全填充之前可能无法完全正常工作的模型类(例如,反向访问器可能丢失)。因此,最好离开 require_ready
默认值为 True
只要可能。
当Django开始的时候, django.setup()
负责填充应用程序注册表。
通过以下方式配置Django:
正在加载设置。
正在设置日志记录。
如果 set_prefix
为true,将URL冲突解决程序脚本前缀设置为 FORCE_SCRIPT_NAME
如果定义,或 /
否则。
正在初始化应用程序注册表。
此函数自动调用:
当通过Django的ASGI或WSGI支持运行HTTP服务器时。
调用管理命令时。
在其他情况下必须显式调用它,例如在纯Python脚本中。
引发了一个 RuntimeWarning
当应用程序在应用程序注册表完全填充之前与数据库交互时。
应用程序注册表分三个阶段初始化。在每个阶段,Django按照以下顺序处理所有应用程序: INSTALLED_APPS
.
第一个django导入 INSTALLED_APPS
.
如果是应用程序配置类,Django将导入应用程序的根包,该根包由 name
属性。如果是Python包,Django在 apps.py
子模块,否则将创建默认应用程序配置。
在这个阶段,您的代码不应该导入任何模型!
换句话说,应用程序的根包和定义应用程序配置类的模块不应该导入任何模型,甚至是间接导入。
严格来说,Django允许在加载应用程序配置后导入模型。但是,为了避免不必要的限制 INSTALLED_APPS
,强烈建议在此阶段不要导入任何模型。
完成此阶段后,在应用程序配置(如 get_app_config()
变得有用。
然后Django尝试导入 models
每个应用程序的子模块(如果有)。
必须定义或导入应用程序中的所有模型 models.py
或 models/__init__.py
. 否则,此时可能无法完全填充应用程序注册表,这可能导致ORM故障。
一旦这个阶段完成,在诸如 get_model()
变得有用。
最后Django运行了 ready()
每个应用程序配置的方法。
以下是初始化过程中可能遇到的一些常见问题:
AppRegistryNotReady
:当导入应用程序配置或模型模块触发依赖于应用程序注册表的代码时,会发生这种情况。
例如, gettext()
使用应用程序注册表在应用程序中查找翻译目录。要在导入时翻译,您需要 gettext_lazy()
相反。(使用) gettext()
这将是一个错误,因为转换将在导入时发生,而不是在每个请求时发生,具体取决于活动语言。)
在模型模块中导入时使用ORM执行数据库查询也将触发此异常。在所有模型都可用之前,ORM无法正常工作。
如果忘记调用 django.setup()
在独立的python脚本中。
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())
为了更容易找到触发此警告的代码,您可以将 treat warnings as errors 要显示堆栈跟踪,请执行以下操作 python -Werror manage.py shell
。
12月 18, 2023