“Django/docs/3.0.x/ref/applications”的版本间差异
(autoload) |
小 (Page commit) |
||
第1行: | 第1行: | ||
+ | {{DISPLAYTITLE:应用程序 — Django 文档}} | ||
<div id="module-django.apps" class="section"> | <div id="module-django.apps" class="section"> | ||
第4行: | 第5行: | ||
= 应用 = | = 应用 = | ||
− | Django | + | Django 包含一个已安装应用程序的注册表,用于存储配置并提供自省。 它还维护一个可用 [[../../topics/db/models|模型]] 的列表。 |
− | 此注册表称为 | + | 此注册表称为 [[#django.apps.apps|apps]],它在 [[#module-django.apps|django.apps]] 中可用: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第12行: | 第13行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.apps import apps |
− | + | >>> apps.get_app_config('admin').verbose_name | |
− | 'Administration'</ | + | 'Administration'</syntaxhighlight> |
</div> | </div> | ||
第23行: | 第24行: | ||
== 项目和应用 == | == 项目和应用 == | ||
− | 术语 ''' | + | 术语 '''project''' 描述了 Django Web 应用程序。 项目 Python 包主要由设置模块定义,但它通常包含其他内容。 例如,当您运行 <code>django-admin startproject mysite</code> 时,您将获得一个 <code>mysite</code> 项目目录,其中包含一个 <code>mysite</code> Python 包,其中包含 <code>settings.py</code>、<code>urls.py</code> , <code>asgi.py</code> 和 <code>wsgi.py</code>。 项目包通常会扩展为包含不绑定到特定应用程序的夹具、CSS 和模板等内容。 |
− | + | '''项目的根目录''' (包含 <code>manage.py</code> 的目录)通常是所有未单独安装的项目应用程序的容器。 | |
− | 术语 '''application''' | + | 术语 '''application''' 描述了一个提供一些功能集的 Python 包。 应用程序 [[../../intro/reusable-apps|可以在各种项目中重复使用]] 。 |
− | + | 应用程序包括模型、视图、模板、模板标签、静态文件、URL、中间件等的某种组合。 它们通常通过 [[#id1|:setting:`INSTALLED_APPS`]] 设置和其他机制(如 URLconfs、[[#id3|:setting:`MIDDLEWARE`]] 设置或模板继承)连接到项目中。 | |
− | + | Django 应用程序是一组与框架的各个部分交互的代码,理解这一点很重要。 没有 <code>Application</code> 对象这样的东西。 但是,Django 需要在一些地方与已安装的应用程序进行交互,主要用于配置和自省。 这就是应用程序注册表为每个已安装应用程序在 [[#django.apps.AppConfig|AppConfig]] 实例中维护元数据的原因。 | |
− | + | 没有限制项目包不能也被视为应用程序并具有模型等。 (这需要将它添加到 [[#id5|:setting:`INSTALLED_APPS`]])。 | |
第42行: | 第43行: | ||
== 配置应用程序 == | == 配置应用程序 == | ||
− | + | 要配置应用程序,子类 [[#django.apps.AppConfig|AppConfig]] 并将该子类的虚线路径放在 [[#id7|:setting:`INSTALLED_APPS`]] 中。 | |
− | 当 [[ | + | 当 [[#id9|:setting:`INSTALLED_APPS`]] 包含应用程序模块的虚线路径时,Django 会检查该模块中的 <code>default_app_config</code> 变量。 |
− | + | 如果已定义,则它是该应用程序的 [[#django.apps.AppConfig|AppConfig]] 子类的虚线路径。 | |
− | 如果没有 <code>default_app_config</code>,Django | + | 如果没有 <code>default_app_config</code>,Django 使用基本的 [[#django.apps.AppConfig|AppConfig]] 类。 |
− | <code>default_app_config</code> | + | <code>default_app_config</code> 允许早于 Django 1.7 的应用程序,例如 <code>django.contrib.admin</code> 选择加入 [[#django.apps.AppConfig|AppConfig]] 功能,而无需用户更新他们的 [[#id11|:setting:`INSTALLED_APPS`]] ]。 |
− | + | 新应用程序应避免 <code>default_app_config</code>。 相反,他们应该要求在 [[#id13|:setting:`INSTALLED_APPS`]] 中明确配置适当的 [[#django.apps.AppConfig|AppConfig]] 子类的虚线路径。 | |
<div id="for-application-authors" class="section"> | <div id="for-application-authors" class="section"> | ||
− | === | + | === 对于应用程序作者 === |
− | + | 如果您正在创建一个名为“Rock'n'roll”的可插拔应用程序,以下是您为管理员提供正确名称的方法: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第64行: | 第65行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># rock_n_roll/apps.py |
from django.apps import AppConfig | from django.apps import AppConfig | ||
第70行: | 第71行: | ||
class RockNRollConfig(AppConfig): | class RockNRollConfig(AppConfig): | ||
name = 'rock_n_roll' | name = 'rock_n_roll' | ||
− | verbose_name = | + | verbose_name = "Rock ’n’ roll"</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 你可以让你的应用程序默认加载这个 [[#django.apps.AppConfig|AppConfig]] 子类,如下所示: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第81行: | 第82行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># rock_n_roll/__init__.py |
− | default_app_config = 'rock_n_roll.apps.RockNRollConfig'</ | + | default_app_config = 'rock_n_roll.apps.RockNRollConfig'</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | 当 [[ | + | 当 [[#id15|:setting:`INSTALLED_APPS`]] 包含 <code>'rock_n_roll'</code> 时,这将导致使用 <code>RockNRollConfig</code>。 这允许您使用 [[#django.apps.AppConfig|AppConfig]] 功能,而无需您的用户更新他们的 [[#id17|:setting:`INSTALLED_APPS`]] 设置。 除了这个用例,最好避免使用 <code>default_app_config</code>,而是在 [[#id19|:setting:`INSTALLED_APPS`]] 中指定应用配置类,如下所述。 |
− | + | 当然,您也可以告诉您的用户将 <code>'rock_n_roll.apps.RockNRollConfig'</code> 放在他们的 [[#id21|:setting:`INSTALLED_APPS`]] 设置中。 您甚至可以提供多个具有不同行为的不同 [[#django.apps.AppConfig|AppConfig]] 子类,并允许您的用户通过他们的 [[#id23|:setting:`INSTALLED_APPS`]] 设置来选择一个。 | |
− | + | 推荐的约定是将配置类放在名为 <code>apps</code> 的应用程序子模块中。 但是,这不是由 Django 强制执行的。 | |
− | + | 您必须为 Django 包含 [[#django.apps.AppConfig.name|name]] 属性以确定此配置适用于哪个应用程序。 您可以定义 [[#django.apps.AppConfig|AppConfig]] API 参考中记录的任何属性。 | |
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 如果您的代码在应用程序的 <code>__init__.py</code> 中导入应用程序注册表,则名称 <code>apps</code> 将与 <code>apps</code> 子模块冲突。 最佳实践是将该代码移动到子模块并导入它。 解决方法是以不同的名称导入注册表: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第106行: | 第107行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.apps import apps as django_apps</syntaxhighlight> |
</div> | </div> | ||
第117行: | 第118行: | ||
<div id="for-application-users" class="section"> | <div id="for-application-users" class="section"> | ||
− | === | + | === 对于应用程序用户 === |
− | + | 如果您在名为 <code>anthology</code> 的项目中使用“Rock 'n'roll”,但您希望它显示为“Jazz Manouche”,您可以提供您自己的配置: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第125行: | 第126行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># anthology/apps.py |
from rock_n_roll.apps import RockNRollConfig | from rock_n_roll.apps import RockNRollConfig | ||
class JazzManoucheConfig(RockNRollConfig): | class JazzManoucheConfig(RockNRollConfig): | ||
− | verbose_name = | + | verbose_name = "Jazz Manouche" |
# anthology/settings.py | # anthology/settings.py | ||
第137行: | 第138行: | ||
'anthology.apps.JazzManoucheConfig', | 'anthology.apps.JazzManoucheConfig', | ||
# ... | # ... | ||
− | ]</ | + | ]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | 同样,在名为 <code> | + | 同样,在名为 <code>apps</code> 的子模块中定义特定于项目的配置类是约定,而不是要求。 |
第152行: | 第153行: | ||
== 应用配置 == | == 应用配置 == | ||
− | ; ''class | + | ; ''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">AppConfig</span></span> |
− | : | + | : 应用程序配置对象存储应用程序的元数据。 一些属性可以在 [[#django.apps.AppConfig|AppConfig]] 子类中配置。 其他的由 Django 设置并且是只读的。 |
<div id="configurable-attributes" class="section"> | <div id="configurable-attributes" class="section"> | ||
− | === | + | === 可配置的属性 === |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">AppConfig.</span></span><span class="sig-name descname"><span class="pre">name</span></span></dt> |
− | <dd><p> | + | <dd><p>应用程序的完整 Python 路径,例如 <code>'django.contrib.admin'</code>。</p> |
− | <p> | + | <p>此属性定义配置适用于哪个应用程序。 它必须在所有 [[#django.apps.AppConfig|AppConfig]] 子类中设置。</p> |
− | <p> | + | <p>它在 Django 项目中必须是唯一的。</p></dd></dl> |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">AppConfig.</span></span><span class="sig-name descname"><span class="pre">label</span></span></dt> |
− | <dd><p> | + | <dd><p>应用程序的简称,例如 <code>'admin'</code></p> |
− | <p> | + | <p>当两个应用程序具有冲突的标签时,此属性允许重新标记应用程序。 默认为 <code>name</code> 的最后一个组件。 它应该是一个有效的 Python 标识符。</p> |
− | <p> | + | <p>它在 Django 项目中必须是唯一的。</p></dd></dl> |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">AppConfig.</span></span><span class="sig-name descname"><span class="pre">verbose_name</span></span></dt> |
− | <dd><p> | + | <dd><p>应用程序的人类可读名称,例如 “行政”。</p> |
− | <p> | + | <p>该属性默认为 <code>label.title()</code>。</p></dd></dl> |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">AppConfig.</span></span><span class="sig-name descname"><span class="pre">path</span></span></dt> |
− | <dd><p> | + | <dd><p>应用程序目录的文件系统路径,例如 <code>'/usr/lib/pythonX.Y/dist-packages/django/contrib/admin'</code>。</p> |
− | <p> | + | <p>在大多数情况下,Django 可以自动检测并设置它,但您也可以提供显式覆盖作为 [[#django.apps.AppConfig|AppConfig]] 子类上的类属性。 在某些情况下,这是必需的; 例如,如果应用程序包是具有多个路径的 [[#namespace-package|命名空间包]] 。</p></dd></dl> |
第187行: | 第188行: | ||
=== 只读属性 === | === 只读属性 === | ||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">AppConfig.</span></span><span class="sig-name descname"><span class="pre">module</span></span> |
− | : | + | : 应用程序的根模块,例如 <code><module 'django.contrib.admin' from 'django/contrib/admin/__init__.py'></code>。 |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">AppConfig.</span></span><span class="sig-name descname"><span class="pre">models_module</span></span></dt> |
− | <dd><p> | + | <dd><p>包含模型的模块,例如 <code><module 'django.contrib.admin.models' from 'django/contrib/admin/models.py'></code>。</p> |
− | <p> | + | <p>如果应用程序不包含 <code>models</code> 模块,则它可能是 <code>None</code>。 请注意,数据库相关信号(例如 [[../signals#django.db.models.signals|pre_migrate]] 和 [[../signals#django.db.models.signals|post_migrate]])仅针对具有 <code>models</code> 模块的应用程序发出。</p></dd></dl> |
第202行: | 第203行: | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">AppConfig.</span></span><span class="sig-name descname"><span class="pre">get_models</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>返回此应用程序的 [[../models/instances#django.db.models|Model]] 类的可迭代对象。</p> |
− | <p> | + | <p>需要完全填充应用程序注册表。</p></dd></dl> |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">AppConfig.</span></span><span class="sig-name descname"><span class="pre">get_model</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">model_name</span></span>'', ''<span class="n"><span class="pre">require_ready</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>返回具有给定 <code>model_name</code> 的 [[../models/instances#django.db.models|Model]]。 <code>model_name</code> 不区分大小写。</p> |
− | <p> | + | <p>如果此应用程序中不存在此类模型,则引发 <code>LookupError</code>。</p> |
− | <p> | + | <p>除非 <code>require_ready</code> 参数设置为 <code>False</code>,否则需要完全填充应用程序注册表。 <code>require_ready</code> 的行为与 [[#django.apps.apps.get_model|apps.get_model()]] 完全一样。</p></dd></dl> |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">AppConfig.</span></span><span class="sig-name descname"><span class="pre">ready</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>子类可以覆盖此方法来执行初始化任务,例如注册信号。 一旦注册表被完全填充,它就会被调用。</p> |
− | <p> | + | <p>尽管您无法在定义了 [[#django.apps.AppConfig|AppConfig]] 类的模块级别导入模型,但您可以在 <code>ready()</code> 中使用 <code>import</code> 语句或 [[#django.apps.AppConfig.get_model|导入它们get_model()]]。</p> |
− | <p> | + | <p>如果您正在注册 [[../signals#module-django.db.models|模型信号]] ,您可以通过其字符串标签而不是使用模型类本身来引用发送者。</p> |
− | <p> | + | <p>例子:</p> |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.apps import AppConfig |
from django.db.models.signals import pre_save | from django.db.models.signals import pre_save | ||
第235行: | 第236行: | ||
# registering signals with the model's string label | # registering signals with the model's string label | ||
− | pre_save.connect(receiver, sender='app_label.MyModel')</ | + | pre_save.connect(receiver, sender='app_label.MyModel')</syntaxhighlight> |
</div> | </div> | ||
第243行: | 第244行: | ||
<p>警告</p> | <p>警告</p> | ||
− | <p> | + | <p>尽管您可以如上所述访问模型类,但请避免在 [[#django.apps.AppConfig.ready|ready()]] 实现中与数据库交互。 这包括执行查询的模型方法([[../models/instances#django.db.models.Model|save()]]、[[../models/instances#django.db.models.Model|delete()]]、管理器方法等),以及通过 <code>django.db.connection</code> 的原始 SQL 查询。 您的 [[#django.apps.AppConfig.ready|ready()]] 方法将在每个管理命令启动期间运行。 例如,即使测试数据库配置与生产设置分开,<code>manage.py test</code> 仍会针对您的 '''生产''' 数据库执行一些查询!</p> |
</div> | </div> | ||
<div class="admonition note"> | <div class="admonition note"> | ||
− | <p> | + | <p>笔记</p> |
− | <p> | + | <p>在通常的初始化过程中,<code>ready</code> 方法只会被 Django 调用一次。 但是在某些极端情况下,尤其是在摆弄已安装应用程序的测试中,可能会多次调用 <code>ready</code>。 在这种情况下,要么编写幂等方法,要么在您的 <code>AppConfig</code> 类上放置一个标志,以防止重新运行应该只执行一次的代码。</p> |
</div></dd></dl> | </div></dd></dl> | ||
第260行: | 第261行: | ||
=== 命名空间包作为应用程序 === | === 命名空间包作为应用程序 === | ||
− | + | 没有 <code>__init__.py</code> 文件的 Python 包被称为“命名空间包”,可能分布在 <code>sys.path</code> 上不同位置的多个目录中(参见 <span id="index-0" class="target"></span>[https://www.python.org/dev/peps/pep-0420 PEP 420] ])。 | |
− | + | Django 应用程序需要一个基本文件系统路径,Django(取决于配置)将在其中搜索模板、静态资产等。 因此,如果满足以下任一条件,命名空间包可能只是 Django 应用程序: | |
− | # | + | # 命名空间包实际上只有一个位置(即 不分布在多个目录中。) |
− | # | + | # 用于配置应用程序的 [[#django.apps.AppConfig|AppConfig]] 类有一个 [[#django.apps.AppConfig.path|path]] 类属性,这是 Django 将用作应用程序的单个基本路径的绝对目录路径。 |
− | + | 如果这两个条件都不满足,Django 将引发 [[../exceptions#django.core.exceptions|ImproperlyConfigured]]。 | |
− | [[../exceptions#django.core.exceptions| | ||
第276行: | 第276行: | ||
<div id="application-registry" class="section"> | <div id="application-registry" class="section"> | ||
− | == | + | == 应用注册 == |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">apps</span></span> |
− | : | + | : 应用程序注册表提供以下公共 API。 下面未列出的方法被视为私有方法,可能会更改,恕不另行通知。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">apps.</span></span><span class="sig-name descname"><span class="pre">ready</span></span> |
− | + | : 在注册表完全填充并调用所有 [[#django.apps.AppConfig.ready|AppConfig.ready()]] 方法后设置为 <code>True</code> 的布尔属性。 | |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">apps.</span></span><span class="sig-name descname"><span class="pre">get_app_configs</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
− | : | + | : 返回 [[#django.apps.AppConfig|AppConfig]] 实例的迭代。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">apps.</span></span><span class="sig-name descname"><span class="pre">get_app_config</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">app_label</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 返回具有给定 <code>app_label</code> 的应用程序的 [[#django.apps.AppConfig|AppConfig]]。 如果不存在这样的应用程序,则引发 <code>LookupError</code>。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">apps.</span></span><span class="sig-name descname"><span class="pre">is_installed</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">app_name</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 检查注册表中是否存在具有给定名称的应用程序。 <code>app_name</code> 是应用的全名,例如 <code>'django.contrib.admin'</code>。 |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">apps.</span></span><span class="sig-name descname"><span class="pre">get_model</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">app_label</span></span>'', ''<span class="n"><span class="pre">model_name</span></span>'', ''<span class="n"><span class="pre">require_ready</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>返回具有给定 <code>app_label</code> 和 <code>model_name</code> 的 [[../models/instances#django.db.models|Model]]。 作为一种快捷方式,此方法还接受 <code>app_label.model_name</code> 形式的单个参数。 <code>model_name</code> 不区分大小写。</p> |
− | + | <p>如果不存在此类应用程序或模型,则引发 <code>LookupError</code>。 当使用不包含一个点的单个参数调用时引发 <code>ValueError</code>。</p> | |
− | + | <p>除非 <code>require_ready</code> 参数设置为 <code>False</code>,否则需要完全填充应用程序注册表。</p> | |
− | + | <p>将 <code>require_ready</code> 设置为 <code>False</code> 允许在填充应用程序注册表 [[#app-loading-process|时查找模型]] ,特别是在导入模型的第二阶段。 然后<code>get_model()</code>和导入模型效果一样。 主要用例是使用设置来配置模型类,例如 [[#id25|:setting:`AUTH_USER_MODEL`]]。</p> | |
− | <p> | + | <p>当 <code>require_ready</code> 为 <code>False</code> 时,<code>get_model()</code> 返回的模型类可能无法完全发挥作用(例如,反向访问器可能丢失),直到完全填充应用程序注册表。 因此,最好尽可能将 <code>require_ready</code> 保留为默认值 <code>True</code>。</p></dd></dl> |
− | <code>ValueError</code> | ||
− | |||
− | <p> | ||
− | <code>require_ready</code> | ||
− | <p> | ||
− | [[#app-loading-process| | ||
− | |||
− | <code>get_model()</code> | ||
− | |||
− | [[ | ||
− | <p> | ||
− | |||
− | |||
− | |||
− | |||
第321行: | 第306行: | ||
<span id="app-loading-process"></span> | <span id="app-loading-process"></span> | ||
− | == | + | == 初始化过程 == |
<div id="how-applications-are-loaded" class="section"> | <div id="how-applications-are-loaded" class="section"> | ||
− | === | + | === 如何加载应用程序 === |
− | Django | + | 当 Django 启动时,[[#django.setup|django.setup()]] 负责填充应用程序注册表。 |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">setup</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">set_prefix</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>通过以下方式配置 Django:</p> |
<ul> | <ul> | ||
− | <li><p> | + | <li><p>加载设置。</p></li> |
− | <li><p> | + | <li><p>设置日志记录。</p></li> |
− | <li><p> | + | <li><p>如果 <code>set_prefix</code> 为 True,则将 URL 解析器脚本前缀设置为 [[#id27|:setting:`FORCE_SCRIPT_NAME`]](如果已定义),否则设置为 <code>/</code>。</p></li> |
− | <li><p> | + | <li><p>初始化应用程序注册表。</p></li></ul> |
− | <p> | + | <p>这个函数是自动调用的:</p> |
<ul> | <ul> | ||
− | <li><p> | + | <li><p>通过 Django 的 WSGI 支持运行 HTTP 服务器时。</p></li> |
− | <li><p> | + | <li><p>调用管理命令时。</p></li></ul> |
− | <p> | + | <p>在其他情况下必须显式调用它,例如在纯 Python 脚本中。</p></dd></dl> |
− | + | 应用程序注册表分三个阶段进行初始化。 在每个阶段,Django 按照 [[#id29|:setting:`INSTALLED_APPS`]] 的顺序处理所有应用程序。 | |
<ol> | <ol> | ||
− | <li><p> | + | <li><p>首先 Django 导入 [[#id31|:setting:`INSTALLED_APPS`]] 中的每个项目。</p> |
− | <p> | + | <p>如果是应用程序配置类,Django 会导入应用程序的根包,由其 [[#django.apps.AppConfig.name|name]] 属性定义。 如果是 Python 包,Django 会创建一个默认的应用程序配置。</p> |
− | + | <p>''在此阶段,您的代码不应导入任何模型!''</p> | |
− | + | <p>换句话说,您的应用程序的根包和定义您的应用程序配置类的模块不应导入任何模型,即使是间接导入。</p> | |
− | <p>'' | + | <p>严格来说,Django 允许在加载应用程序配置后导入模型。 但是,为了避免对[[#id33|:setting:`INSTALLED_APPS`]]的顺序进行不必要的限制,强烈建议在此阶段不要导入任何模型。</p> |
− | <p> | + | <p>此阶段完成后,在应用程序配置(如 [[#django.apps.apps.get_app_config|get_app_config()]])上运行的 API 就可以使用了。</p></li> |
− | <p> | + | <li><p>然后 Django 尝试导入每个应用程序的 <code>models</code> 子模块,如果有的话。</p> |
− | <p> | + | <p>您必须在应用程序的 <code>models.py</code> 或 <code>models/__init__.py</code> 中定义或导入所有模型。 否则,此时应用程序注册表可能未完全填充,这可能导致 ORM 出现故障。</p> |
− | <li><p>然后 Django | + | <p>此阶段完成后,可在 [[#django.apps.apps.get_model|get_model()]] 等模型上运行的 API 变得可用。</p></li> |
− | <p> | + | <li><p>最后 Django 运行每个应用程序配置的 [[#django.apps.AppConfig.ready|ready()]] 方法。</p></li></ol> |
− | <p> | ||
− | <li><p> | ||
第366行: | 第349行: | ||
<span id="applications-troubleshooting"></span> | <span id="applications-troubleshooting"></span> | ||
− | === | + | === 故障排除 === |
− | + | 以下是您在初始化过程中可能遇到的一些常见问题: | |
<ul> | <ul> | ||
− | <li><p>[[../exceptions#django.core.exceptions| | + | <li><p>[[../exceptions#django.core.exceptions|AppRegistryNotReady]]:当导入应用程序配置或模型模块触发依赖于应用程序注册表的代码时会发生这种情况。</p> |
− | + | <p>例如,[[../utils#django.utils.translation|gettext()]] 使用应用程序注册表在应用程序中查找翻译目录。 要在导入时进行翻译,您需要改为 [[../utils#django.utils.translation|gettext_lazy()]]。 (使用 [[../utils#django.utils.translation|gettext()]] 将是一个错误,因为翻译会在导入时发生,而不是根据活动语言在每个请求时发生。)</p> | |
− | + | <p>在模型模块中导入时使用 ORM 执行数据库查询也会触发此异常。 在所有模型都可用之前,ORM 无法正常运行。</p> | |
− | <p> | + | <p>如果您忘记在独立 Python 脚本中调用 [[#django.setup|django.setup()]],也会发生此异常。</p></li> |
− | + | <li><p><code>ImportError: cannot import name ...</code> 如果导入序列以循环结束,则会发生这种情况。</p> | |
− | + | <p>为了消除此类问题,您应该尽量减少模型模块之间的依赖关系,并在导入时尽可能少做工作。 为了避免在导入时执行代码,您可以将其移动到函数中并缓存其结果。 代码将在您第一次需要其结果时执行。 这个概念被称为“懒惰评估”。</p></li> | |
− | + | <li><p><code>django.contrib.admin</code> 在已安装的应用程序中自动执行 <code>admin</code> 模块的自动发现。 为了防止它,改变你的 [[#id35|:setting:`INSTALLED_APPS`]] 以包含 <code>'django.contrib.admin.apps.SimpleAdminConfig'</code> 而不是 <code>'django.contrib.admin'</code>。</p></li></ul> | |
− | |||
− | |||
− | <p> | ||
− | |||
− | |||
− | <p> | ||
− | <li><p><code>ImportError: cannot import name ...</code> | ||
− | |||
− | <p> | ||
− | <li><p><code>django.contrib.admin</code> | ||
− | |||
− | [[ | ||
− | <code>'django.contrib.admin.apps.SimpleAdminConfig'</code> | ||
− | <code>'django.contrib.admin'</code> | ||
第399行: | 第368行: | ||
</div> | </div> | ||
+ | <div class="clearer"> | ||
− | [[Category:Django 3.0.x | + | |
+ | |||
+ | </div> | ||
+ | |||
+ | [[Category:Django 3.0.x 文档]] |
2021年10月31日 (日) 04:09的最新版本
应用
Django 包含一个已安装应用程序的注册表,用于存储配置并提供自省。 它还维护一个可用 模型 的列表。
此注册表称为 apps,它在 django.apps 中可用:
项目和应用
术语 project 描述了 Django Web 应用程序。 项目 Python 包主要由设置模块定义,但它通常包含其他内容。 例如,当您运行 django-admin startproject mysite
时,您将获得一个 mysite
项目目录,其中包含一个 mysite
Python 包,其中包含 settings.py
、urls.py
, asgi.py
和 wsgi.py
。 项目包通常会扩展为包含不绑定到特定应用程序的夹具、CSS 和模板等内容。
项目的根目录 (包含 manage.py
的目录)通常是所有未单独安装的项目应用程序的容器。
术语 application 描述了一个提供一些功能集的 Python 包。 应用程序 可以在各种项目中重复使用 。
应用程序包括模型、视图、模板、模板标签、静态文件、URL、中间件等的某种组合。 它们通常通过 :setting:`INSTALLED_APPS` 设置和其他机制(如 URLconfs、:setting:`MIDDLEWARE` 设置或模板继承)连接到项目中。
Django 应用程序是一组与框架的各个部分交互的代码,理解这一点很重要。 没有 Application
对象这样的东西。 但是,Django 需要在一些地方与已安装的应用程序进行交互,主要用于配置和自省。 这就是应用程序注册表为每个已安装应用程序在 AppConfig 实例中维护元数据的原因。
没有限制项目包不能也被视为应用程序并具有模型等。 (这需要将它添加到 :setting:`INSTALLED_APPS`)。
配置应用程序
要配置应用程序,子类 AppConfig 并将该子类的虚线路径放在 :setting:`INSTALLED_APPS` 中。
当 :setting:`INSTALLED_APPS` 包含应用程序模块的虚线路径时,Django 会检查该模块中的 default_app_config
变量。
如果已定义,则它是该应用程序的 AppConfig 子类的虚线路径。
如果没有 default_app_config
,Django 使用基本的 AppConfig 类。
default_app_config
允许早于 Django 1.7 的应用程序,例如 django.contrib.admin
选择加入 AppConfig 功能,而无需用户更新他们的 :setting:`INSTALLED_APPS` ]。
新应用程序应避免 default_app_config
。 相反,他们应该要求在 :setting:`INSTALLED_APPS` 中明确配置适当的 AppConfig 子类的虚线路径。
对于应用程序用户
如果您在名为 anthology
的项目中使用“Rock 'n'roll”,但您希望它显示为“Jazz Manouche”,您可以提供您自己的配置:
同样,在名为 apps
的子模块中定义特定于项目的配置类是约定,而不是要求。
应用配置
- class AppConfig
- 应用程序配置对象存储应用程序的元数据。 一些属性可以在 AppConfig 子类中配置。 其他的由 Django 设置并且是只读的。
可配置的属性
- AppConfig.name
应用程序的完整 Python 路径,例如
'django.contrib.admin'
。此属性定义配置适用于哪个应用程序。 它必须在所有 AppConfig 子类中设置。
它在 Django 项目中必须是唯一的。
- AppConfig.label
应用程序的简称,例如
'admin'
当两个应用程序具有冲突的标签时,此属性允许重新标记应用程序。 默认为
name
的最后一个组件。 它应该是一个有效的 Python 标识符。它在 Django 项目中必须是唯一的。
- AppConfig.verbose_name
应用程序的人类可读名称,例如 “行政”。
该属性默认为
label.title()
。
- AppConfig.path
应用程序目录的文件系统路径,例如
'/usr/lib/pythonX.Y/dist-packages/django/contrib/admin'
。在大多数情况下,Django 可以自动检测并设置它,但您也可以提供显式覆盖作为 AppConfig 子类上的类属性。 在某些情况下,这是必需的; 例如,如果应用程序包是具有多个路径的 命名空间包 。
只读属性
- 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()。如果您正在注册 模型信号 ,您可以通过其字符串标签而不是使用模型类本身来引用发送者。
例子:
警告
尽管您可以如上所述访问模型类,但请避免在 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_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)
返回具有给定
app_label
和model_name
的 Model。 作为一种快捷方式,此方法还接受app_label.model_name
形式的单个参数。model_name
不区分大小写。如果不存在此类应用程序或模型,则引发
LookupError
。 当使用不包含一个点的单个参数调用时引发ValueError
。除非
require_ready
参数设置为False
,否则需要完全填充应用程序注册表。将
require_ready
设置为False
允许在填充应用程序注册表 时查找模型 ,特别是在导入模型的第二阶段。 然后get_model()
和导入模型效果一样。 主要用例是使用设置来配置模型类,例如 :setting:`AUTH_USER_MODEL`。当
require_ready
为False
时,get_model()
返回的模型类可能无法完全发挥作用(例如,反向访问器可能丢失),直到完全填充应用程序注册表。 因此,最好尽可能将require_ready
保留为默认值True
。
初始化过程
如何加载应用程序
当 Django 启动时,django.setup() 负责填充应用程序注册表。
- setup(set_prefix=True)
通过以下方式配置 Django:
加载设置。
设置日志记录。
如果
set_prefix
为 True,则将 URL 解析器脚本前缀设置为 :setting:`FORCE_SCRIPT_NAME`(如果已定义),否则设置为/
。初始化应用程序注册表。
这个函数是自动调用的:
通过 Django 的 WSGI 支持运行 HTTP 服务器时。
调用管理命令时。
在其他情况下必须显式调用它,例如在纯 Python 脚本中。
应用程序注册表分三个阶段进行初始化。 在每个阶段,Django 按照 :setting:`INSTALLED_APPS` 的顺序处理所有应用程序。
首先 Django 导入 :setting:`INSTALLED_APPS` 中的每个项目。
如果是应用程序配置类,Django 会导入应用程序的根包,由其 name 属性定义。 如果是 Python 包,Django 会创建一个默认的应用程序配置。
在此阶段,您的代码不应导入任何模型!
换句话说,您的应用程序的根包和定义您的应用程序配置类的模块不应导入任何模型,即使是间接导入。
严格来说,Django 允许在加载应用程序配置后导入模型。 但是,为了避免对:setting:`INSTALLED_APPS`的顺序进行不必要的限制,强烈建议在此阶段不要导入任何模型。
此阶段完成后,在应用程序配置(如 get_app_config())上运行的 API 就可以使用了。
然后 Django 尝试导入每个应用程序的
models
子模块,如果有的话。您必须在应用程序的
models.py
或models/__init__.py
中定义或导入所有模型。 否则,此时应用程序注册表可能未完全填充,这可能导致 ORM 出现故障。此阶段完成后,可在 get_model() 等模型上运行的 API 变得可用。
最后 Django 运行每个应用程序配置的 ready() 方法。
故障排除
以下是您在初始化过程中可能遇到的一些常见问题:
AppRegistryNotReady:当导入应用程序配置或模型模块触发依赖于应用程序注册表的代码时会发生这种情况。
例如,gettext() 使用应用程序注册表在应用程序中查找翻译目录。 要在导入时进行翻译,您需要改为 gettext_lazy()。 (使用 gettext() 将是一个错误,因为翻译会在导入时发生,而不是根据活动语言在每个请求时发生。)
在模型模块中导入时使用 ORM 执行数据库查询也会触发此异常。 在所有模型都可用之前,ORM 无法正常运行。
如果您忘记在独立 Python 脚本中调用 django.setup(),也会发生此异常。
ImportError: cannot import name ...
如果导入序列以循环结束,则会发生这种情况。为了消除此类问题,您应该尽量减少模型模块之间的依赖关系,并在导入时尽可能少做工作。 为了避免在导入时执行代码,您可以将其移动到函数中并缓存其结果。 代码将在您第一次需要其结果时执行。 这个概念被称为“懒惰评估”。
django.contrib.admin
在已安装的应用程序中自动执行admin
模块的自动发现。 为了防止它,改变你的 :setting:`INSTALLED_APPS` 以包含'django.contrib.admin.apps.SimpleAdminConfig'
而不是'django.contrib.admin'
。