Skip to main content

应用程序

Django包含已安装的应用程序的注册表,用于存储配置并提供自省。它还维护可用 楷模 的列表。

这个注册表简称为 apps,它在 django.apps 中可用:

>>> from django.apps import apps
>>> apps.get_app_config('admin').verbose_name
'Admin'

项目和应用

术语 项目 描述了Django web应用程序。项目Python包主要由设置模块定义,但它通常包含其他内容。例如,当您运行 django-admin startproject mysite 时,您会得到一个 mysite 项目目录,其中包含一个包含 settings.pyurls.pywsgi.pymysite Python包。项目包通常扩展为包括不与特定应用程序绑定的fixture,CSS和模板等。

项目的根目录 (包含 manage.py 的那个)通常是没有单独安装的所有项目应用程序的容器。

术语 应用 描述了提供一些功能的Python包。应用 可以重复使用 在各个项目。

应用程序包括模型,视图,模板,模板标记,静态文件,URL,中间件等的一些组合。它们通常连接到具有 INSTALLED_APPS 设置的项目,并且可选地具有其他机制,例如URLconfs,MIDDLEWARE 设置或模板继承。

重要的是要了解Django应用程序只是一组与框架的各个部分交互的代码。没有像 Application 对象这样的东西。但是,有一些地方Django需要与已安装的应用程序交互,主要用于配置和内省。这就是为什么应用程序注册表为每个已安装的应用程序在 AppConfig 实例中维护元数据。

没有限制,项目包不能被认为是一个应用程序和模型等(这将需要添加到 INSTALLED_APPS)。

配置应用程序

要配置应用程序,请将 AppConfig 子类化,并将虚线路径放在 INSTALLED_APPS 中的该子类中。

INSTALLED_APPS 只包含应用程序模块的虚线路径时,Django会检查该模块中的 default_app_config 变量。

如果它被定义,它是该应用程序的 AppConfig 子类的虚线路径。

如果没有 default_app_config,Django使用基本的 AppConfig 类。

default_app_config 允许早于Django 1.7的应用程序(如 django.contrib.admin)选择启用 AppConfig 功能,而不需要用户更新其 INSTALLED_APPS

新的应用程序应该避免 default_app_config。相反,它们应该需要在 INSTALLED_APPS 中明确配置到适当的 AppConfig 子类的虚线路径。

对于应用程序作者

如果您正在创建一个名为“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"

您可以使您的应用程序默认加载此 AppConfig 子类,如下所示:

# rock_n_roll/__init__.py

default_app_config = 'rock_n_roll.apps.RockNRollConfig'

这将导致在 INSTALLED_APPS 只包含 'rock_n_roll' 时使用 RockNRollConfig。这允许您使用 AppConfig 功能,而不需要您的用户更新其 INSTALLED_APPS 设置。除了这个用例,最好避免使用 default_app_config,而改为在 INSTALLED_APPS 中指定应用程序配置类,如下所述。

当然,你也可以告诉你的用户把 'rock_n_roll.apps.RockNRollConfig' 放在他们的 INSTALLED_APPS 设置。您甚至可以提供具有不同行为的几个不同的 AppConfig 子类,并允许您的用户通过他们的 INSTALLED_APPS 设置选择一个。

推荐的约定是将配置类放在名为 apps 的应用程序的子模块中。但是,这不是由Django强制。

您必须包括Django的 name 属性,以确定此配置适用于哪个应用程序。您可以定义 AppConfig API参考中记录的任何属性。

注解

如果您的代码在应用程序的 __init__.py 中导入应用程序注册表,则 apps 名称将与 apps 子模块发生冲突。最佳实践是将该代码移动到子模块并将其导入。解决方法是以不同的名称导入注册表:

from django.apps import apps as django_apps

应用程序用户

如果你在一个名为 anthology 的项目中使用“Rock’n’roll”,但是希望它显示为“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 的子模块中定义项目特定的配置类是一个约定,而不是一个要求。

应用程序配置

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/python3.4/dist-packages/django/contrib/admin'

在大多数情况下,Django可以自动检测并设置此属性,但是您也可以在 AppConfig 子类上作为类属性提供显式覆盖。在一些情况下,这是必需的;例如,如果应用程序包是具有多个路径的 namespace package

只读属性

AppConfig.module

用于应用程序的根模块,例如 <module 'django.contrib.admin' from 'django/contrib/admin/__init__.pyc'>

AppConfig.models_module

包含模型的模块,例如 <module 'django.contrib.admin.models' from 'django/contrib/admin/models.pyc'>

如果应用程序不包含 models 模块,它可能是 None。请注意,只有具有 models 模块的应用程序才会发出与 pre_migratepost_migrate 等数据库相关的信号。

方法

AppConfig.get_models()[源代码]

返回此应用程序的 Model 类的可迭代。

AppConfig.get_model(model_name)[源代码]

返回具有给定 model_nameModel。如果本应用程序中不存在此类模型,则引发 LookupErrormodel_name 不区分大小写。

AppConfig.ready()[源代码]

子类可以覆盖此方法以执行初始化任务,如注册信号。一旦注册表完全填充,它就会被调用。

虽然您无法在定义 AppConfig 类的模块级别导入模型,但可以使用 import 语句或 get_model() 将其导入 ready()

如果您注册 model signals,您可以通过其字符串标签而不是使用模型类本身来引用发件人。

例:

from django.db.models.signals import pre_save

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 类上放置一个标志,以防止重新运行的代码,应该执行一次。

命名空间包作为应用程序(Python 3.3+)

Python版本3.3和更高版本支持没有 __init__.py 文件的Python包。这些包称为“命名空间包”,可以分布在 sys.path 上不同位置的多个目录(请参阅 PEP 420)。

Django应用程序需要单个基本文件系统路径,其中Django(取决于配置)将搜索模板,静态资产等。因此,如果满足以下条件之一,命名空间包只能是Django应用程序:

  1. 命名空间包实际上只有一个位置(即不会分布在多个目录中)。

  2. 用于配置应用程序的 AppConfig 类具有 path 类属性,这是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)

返回具有给定 app_labelmodel_nameModel。作为一个快捷方式,此方法还接受 app_label.model_name 形式的单个参数。 model_name 不区分大小写。

如果没有此类应用程序或模型,则提高 LookupError。当使用不包含正好一个点的单个参数调用时提升 ValueError

初始化过程

如何加载应用程序

当Django启动时,django.setup() 负责填充应用程序注册表。

setup(set_prefix=True)[源代码]

通过以下方式配置Django:

  • 加载设置。

  • 设置日志记录。

  • 如果 set_prefix 为True,则将URL解析器脚本前缀设置为 FORCE_SCRIPT_NAME (如果已定义),否则为 /

  • 正在初始化应用程序注册表。

Changed in Django 1.10:

设置URL解析器脚本前缀的能力是新的。

自动调用此函数:

  • 当通过Django的WSGI支持运行HTTP服务器时。

  • 调用管理命令时。

它必须在其他情况下显式调用,例如在纯Python脚本中。

应用程序注册表在三个阶段初始化。在每个阶段,Django按 INSTALLED_APPS 的顺序处理所有应用程序。

  1. 首先Django导入 INSTALLED_APPS 中的每个项目。

    如果它是一个应用程序配置类,Django导入应用程序的根包,由其 name 属性定义。如果它是一个Python包,Django创建一个默认的应用程序配置。

    在这个阶段,你的代码不应该导入任何模型!

    换句话说,应用程序的根包和定义应用程序配置类的模块不应该导入任何模型,即使是间接的。

    严格地说,Django允许在加载应用程序配置后导入模型。然而,为了避免对 INSTALLED_APPS 的顺序的不必要的约束,强烈建议在这个阶段不导入任何模型。

    一旦这个阶段完成,对诸如 get_app_config() 的应用配置操作的API变得可用。

  2. 然后Django尝试导入每个应用程序的 models 子模块(如果有的话)。

    您必须在应用程序的 models.pymodels/__init__.py 中定义或导入所有模型。否则,应用程序注册表可能在此时未完全填充,这可能会导致ORM故障。

    一旦这个阶段完成,在诸如 get_model() 的模型上操作的API变得可用。

  3. 最后Django运行每个应用程序配置的 ready() 方法。

故障排除

以下是在初始化过程中可能遇到的一些常见问题:

  • AppRegistryNotReady:当导入应用程序配置或模型模块触发依赖于应用程序注册表的代码时,会发生这种情况。

    例如,ugettext() 使用应用程序注册表来在应用程序中查找翻译目录。要在导入时进行翻译,您需要 ugettext_lazy()。 (使用 ugettext() 将是一个错误,因为翻译将发生在导入时,而不是在每个请求,取决于活动的语言。)

    在模型模块中在导入时使用ORM执行数据库查询也将触发此异常。在所有型号可用之前,ORM无法正常工作。

    另一个常见的罪魁祸首是 django.contrib.auth.get_user_model()。使用 AUTH_USER_MODEL 设置在导入时引用用户模型。

    如果您忘记在独立的Python脚本中调用 django.setup(),也会发生此异常。

  • ImportError: cannot import name ... 如果导入序列在循环中结束,则会发生这种情况。

    要消除这些问题,应该尽量减少模型模块之间的依赖关系,并在导入时尽可能减少工作量。为了避免在导入时执行代码,可以将其移动到函数并缓存其结果。当你第一次需要它的结果时,代码将被执行。这个概念被称为“延迟评估”。

  • django.contrib.admin 在已安装的应用程序中自动执行 admin 模块的自动发现。为了防止它,改变你的 INSTALLED_APPS 包含 'django.contrib.admin.apps.SimpleAdminConfig' 而不是 'django.contrib.admin'