应用P

Django 包含一个已安装应用配置记录,能够存储配置和内省。它同时还维护一个可用的 模型 列表。

这个注册表被简称为 apps 并且它可用于 django.apps:

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

项目和应用P

术语 项目 描述了一个 Django Web 应用程序。项目 Python 包主要由设置模块定义,但通常包含其他内容。例如,当你运行 django-admin startproject mysite 时,你会得到一个 mysite 工程目录,其中包含一个 mysite Python包,里面有 settings.pyurl.pywsgi.py。项目包通常被扩展成包含固件,CSS 和不与特定应用程序绑定的模板。

一个 项目的根目录 (包含 manage.py 文件的目录) 通常是所有项目应用程序的容器,应用不能独立安装。

术语 application 指的是提供了一些功能的 Python 包。应用 可在多个项目中重用

应用程序包括模型,视图,模板,模板标签,静态文件,URL,中间件等的一些组合。它们通常使用 INSTALLED_APPS 选项加入到项目中,也可以使用其他机制,如 URLconf, MIDDLEWARE 配置或模板继承。

Django 应用程序只是一组与各个框架组件进行交互的代码。并没有一个 Application 对象。不过,Django 并不怎么需要与已安装的应用程序进行交互,主要用于配置和自检。这就是为什么应用程序注册表在每个已安装的应用程序的 AppConfig 实例中维护元数据的原因。

一个项目包可以自由的作为一个应用程序并包含一些模型等(前提是,需要把它加入 INSTALLED_APPS)。

配置应用程序P

配置一个应用程序,需要继承 AppConfig,将子类的点式路径加入 INSTALLED_APPS 中。

INSTALLED_APPS 简单的包含当前路径到一个应用程序模块的路径时,Django 会检查该模块下的 default_app_config 变量。

如果已经定义,它是当前路径到到达该应用程序子类 AppConfig 的路径。

如果没有 default_app_config,Django 会使用基类 AppConfig

default_app_config 允许 Django 1.7 之前的应用程序(例如 django.contrib.admin)在不要求用户更新 INSTALLED_APPS 的情况下就可以加入 AppConfig 特性。

新的应用程序应该避免使用 default_app_config。相反,它们应该在 INSTALLED_APPS 中显式地配置对应 AppConfig 子类的完整点式路径。

对于应用作者P

如果您正在创建一个名为 “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 强制规定的。

你必须包含 attr:~django.apps.AppConfig.name 属性,Django 用它决定这个配置会应用于哪个应用。你定义任何在 AppConfig API 参考中记录的属性。

注解

若你在应用的 __init__.py 中导入了应用注册信息,名称 apps 会与子模块 apps 冲突。最好的办法是将此段带入移入子模块,再导入它。折中方案是导入后取个别名:

from django.apps import apps as django_apps

对于应用使用者P

项目中直接使用 "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',
    # ...
]

同样,在名为 app 的子模块中定义项目配置类是一种约定俗成的惯例,但不强求。

应用配置P

class AppConfig[源代码]P

应用程序配置对象存储了应用的元数据。某些属性可以在 AppConfig 的子类中配置。而其它 Django 设置好的配置是只读的。

可配置属性P

AppConfig.nameP

指向此应用的完整的 Python 格式的路径,如 'django.contrib.admin'

此属性定义配置适用的应用程序。每个 'django.apps.AppConfig` 子类都必须包含此项。

它必须在整个 Django 项目中唯一。

AppConfig.labelP

应用简称,如 'admin'

此属性允许在两个应用标签冲突时重命名其中一个的标签名。默认是 name 的最后一段。必须是一个有效的 Python 标识符。

它必须在整个 Django 项目中唯一。

AppConfig.verbose_nameP

应用容易被人理解的名称,如 "Administration"。

此属性默认值为 label.title()

AppConfig.pathP

应用目录的文件系统路径,如 '/usr/lib/pythonX.Y/dist-packages/django/contrib/admin'

大多数情况下,Django 能自动检测并设置此属性,但你也能在 AppConfig 子类中申明此属性,显式地重写它。很少情况下要这么做;例如,若应用包是一个拥有多个路径的 命名空间

只读属性P

AppConfig.moduleP

应用的根模块,如 <module 'django.contrib.admin' from 'django/contrib/admin/__init__.py'>

AppConfig.models_moduleP

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

应用不包含 models 模块时,可能是 None。注意,数据库关联的信号,例如 pre_migratepost_migrate 仅在应用有 models 模块时发出。

方法P

AppConfig.get_models()[源代码]P

为该应用返回一个可迭代的 Model 类。

要求完整填写应用注册信息。

AppConfig.get_model(model_name, require_ready=True)[源代码]P

返回指定 model_nameModelmodel_name 是大小写敏感的。

如果应用中不存在此模块,则抛出 LookupError 异常。

require_ready 参数为 False 的情况下,都必须完整设置注册信息。 require_ready 行为与 apps.get_model() 一致。

AppConfig.ready()[源代码]P

子类可以重写此方法来执行类似注册信号的初始化任务。只要注册表被填满就会调用此方法。

虽然你不能在定义 AppConfig 类的模型层导入模型,但可以在 ready() 中导入,通过 import 语句或 get_model()

若你正在注册 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() 实现中与数据库交互。这包括了那些会通过 django.db.connection 执行查询 (save()delete(),管理器方法,等等) 和原生查询的模型方法。你的 ready() 方法会在每个管理命令初始化阶段被执行。例如,虽然测试数据库的配置与生成环境配置是分开的, manager.py test 仍会对 生产环境 数据库执行一些查询操作。

注解

在普通的初始化进程中, ready 方法仅被 Django 调用一次。但在一些特殊情况下,特别是针对已安装应用的测试中,可以会多次调用 ready。这种情况下,在 AppConfig 类中编写幂等方法或放入一个标志,避免那些只需运行一次的代码被多次执行。

命名空间包作为应用程序P

Python packages without an __init__.py file are known as "namespace packages" and may be spread across multiple directories at different locations on sys.path (see PEP 420).

Django应用程序需要一个单一的基本文件系统路径,Django(取决于配置)将在其中搜索模板、静态资产等。因此,只有在以下情况之一为真时,命名空间包才可能是Django应用程序:

  1. 名称空间包实际上只有一个位置(即不分布在多个目录中)。
  2. The AppConfig class used to configure the application has a path class attribute, which is the absolute directory path Django will use as the single base path for the application.

If neither of these conditions is met, Django will raise ImproperlyConfigured.

注册应用P

appsP

应用程序注册表提供以下公共API。以下未列出的方法被视为私有方法,可能会更改,恕不另行通知。

apps.readyP

Boolean attribute that is set to True after the registry is fully populated and all AppConfig.ready() methods are called.

apps.get_app_configs()P

返回一个由django.apps.AppConfig类实例组成的可迭代对象

apps.get_app_config(app_label)P

Returns an AppConfig for the application with the given app_label. Raises LookupError if no such application exists.

apps.is_installed(app_name)P

Checks whether an application with the given name exists in the registry. app_name is the full name of the app, e.g. 'django.contrib.admin'.

apps.get_model(app_label, model_name, require_ready=True)P

Returns the Model with the given app_label and model_name. As a shortcut, this method also accepts a single argument in the form app_label.model_name. model_name is case-insensitive.

Raises LookupError if no such application or model exists. Raises ValueError when called with a single argument that doesn't contain exactly one dot.

Requires the app registry to be fully populated unless the require_ready argument is set to False.

Setting require_ready to False allows looking up models while the app registry is being populated, specifically during the second phase where it imports models. Then get_model() has the same effect as importing the model. The main use case is to configure model classes with settings, such as AUTH_USER_MODEL.

When require_ready is False, get_model() returns a model class that may not be fully functional (reverse accessors may be missing, for example) until the app registry is fully populated. For this reason, it's best to leave require_ready to the default value of True whenever possible.

初始化进程P

应用是如何被加载的P

Django 启动后, django.setup() 负责配置应用注册信息。

setup(set_prefix=True)[源代码]P

配置 Django:

  • 加载配置。
  • 设置日志。
  • set_prefix 为 True,为 URL 处理器脚本增加前缀 FORCE_SCRIPT_NAME,若未定义此项,则使用 /
  • 初始化应用注册

这个函数会被自动调用。

  • 当通过 Django 的 WSGI 支持运行 HTTP 服务。
  • 调用一个管理命令时。

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

应用注册的初始化过程分三个阶段完成。在每个阶段,Django 根据应用在 INSTALLED_APPS 中的顺序依次处理。

  1. 首先,Django 导入 INSTALLED_APPS 中各项条目。

    If it's an application configuration class, Django imports the root package of the application, defined by its name attribute. If it's a Python package, Django creates a default application configuration.

    在这情况下,你的代码不应该导入任何模型!

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

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

    一旦这个阶段完成,可以使用 API 对应用程序配置(如:meth:apps.get_app_config() )进行操作。

  2. 然后 Django 尝试导入每个存在模型的应用程序中,继承了``models``的所有子模型。

    您必须在应用程序的“models.py”或“models / __ init __。py”中定义或导入所有模型。 否则,此时应用程序注册表可能不会完全加载,这可能会导致ORM出现故障。

    此步骤完成后,操作模型的 API,例如 get_model(),就可以使用了。

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

错误调试P

在初始化期间,这里有一些常见的错误你可能会遇上。

  • AppRegistryNotReady: This happens when importing an application configuration or a models module triggers code that depends on the app registry.

    For example, gettext() uses the app registry to look up translation catalogs in applications. To translate at import time, you need gettext_lazy() instead. (Using gettext() would be a bug, because the translation would happen at import time, rather than at each request depending on the active language.)

    Executing database queries with the ORM at import time in models modules will also trigger this exception. The ORM cannot function properly until all models are available.

    如果你忘记在一个单独的Python脚本中调用函数`django.setup()`,也会发生异常。

  • ImportError: cannot import name ... This happens if the import sequence ends up in a loop.

    为了消除这些问题,您应该最大限度地减少模型模块之间的依赖关系,并在导入时尽可能减少复杂度。 为了避免在导入时执行代码,可以将其移入函数并缓存结果。 代码将在您首次需要结果时执行。 这个概念被称为“惰性求值”。

  • django.contrib.admin automatically performs autodiscovery of admin modules in installed applications. To prevent it, change your INSTALLED_APPS to contain 'django.contrib.admin.apps.SimpleAdminConfig' instead of 'django.contrib.admin'.