# Django 3.2.11.dev 文档

# 应用程序

Django 包含一个已安装应用程序的注册表，能够存储配置和内省。它还维护着一个可用的 模型 列表。

这个注册表叫做 `app`，它在 `django.app` 中可用：

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

## 项目和应用程序

术语 项目 描述了一个 Django 网络应用。项目的 Python 包主要是由一个配置模块定义的，但它通常包含其他东西。例如，当你运行 `django-admin startproject mysite` 时，你会得到一个 `mysite` 项目目录，其中包含一个 `mysite` 的 Python 包，其中有 `settings.py`、`urls.py`、`asgi.py` 和 `wsgi.py`。该项目包经常被扩展到包括像辅助工具、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',
        ...
    ]

### 对于应用程序作者

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

当 `INSTALLED_APPS` 包含 `'rock_n_roll'` 时，`RockNRollConfig` 将自动加载。如果需要防止这种情况发生，可以在类定义中把 `default` 设置为 `False`。

你可以提供多个 `AppConfig` 子类来实现不同的行为。要告诉 Django 默认使用哪一个，可以在定义中把 `default` 设置为 `True`。如果你的用户想选择一个非默认的配置，他们必须在他们的 `INSTALLED_APPS` 配置中用这个类的点分隔路径替换 `'rock_n_roll'`。

`AppConfig.name` 属性告诉 Django 这个配置适用于哪个应用。你可以定义在 `AppConfig` API 参考中描述任何其他属性。

`AppConfig` 子类可以在任何地方定义。`apps.py` 惯例只是允许 Django 在 `INSTALLED_APPS` 包含一个应用程序模块的路径而不是一个配置类的路径时自动加载它们。

注解

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

    from django.apps import apps as django_apps

Changed in Django 3.2:

在以前的版本中，应用模块中的 `default_app_config` 变量用来标识默认的应用程序配置类。

### 对于应用程序使用者

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

这个例子显示了位于一个名为 `apps.py` 的子模块中的特定项目配置类。`AppConfig` 子类可以在任何地方定义。

在这种情况下，`INSTALLED_APPS` 必须包含配置类的点分隔路径，因为它位于应用程序之外，因此无法自动检测。

## 应用程序配置

class `AppConfig`

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

### 可配置属性

`AppConfig.``name`

  指向此应用程序的完整的 Python 格式的路径，如 `'django.contrib.admin'`。

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

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

`AppConfig.``label`

  应用程序简称，如 `'admin'`

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

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

`AppConfig.``verbose_name`

  应用程序容易被人理解的名称，如 “Administration”。

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

`AppConfig.``path`

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

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

`AppConfig.``default`
  New in Django 3.2.

  将此属性设置为 `False` 以防止 Django 自动选择配置类。当 `apps.py` 只定义了一个 `AppConfig` 子类，但你不想让 Django 默认使用它时，这个属性很有用。

  将这个属性设置为 `True` 来告诉 Django 自动选择一个配置类。当 `apps.py` 定义了多个 `AppConfig` 子类，而你希望 Django 默认使用其中一个时，这个属性很有用。

  默认情况下，这个属性没有设置。

`AppConfig.``default_auto_field`
  New in Django 3.2.

  隐式主键类型，用于添加到本应用中的模型。你可以用它来保持 `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`()

  为该应用返回一个可迭代的 `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()`。

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

  注解

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

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

没有 `__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, require_ready=True)

  返回 `Model` 于给定的 `app_label` 和 `model_name`。作为快捷方式，本方法也接受一个单一参数，形式为 `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)

  配置 Django：

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

  这个函数会被自动调用：

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

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

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

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

   如果是应用配置类，Django 导入应用的根包，由其 `name` 属性定义。如果是 Python 包，Django 会在 `apps.py` 子模块中寻找应用配置，否则会创建一个默认的应用配置。

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

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

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

   一旦这个阶段完成，可以使用 API 对应用程序配置（如 `get_app_config()` ）进行操作。

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

   你必须在你的应用程序的 `models.py` 或 `models/__init__.py` 中定义或导入所有模型。否则，应用程序注册表可能在此时没有完全填充，这可能导致 ORM 失灵。

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

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

### 错误调试

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

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

  例如， `gettext()` 使用应用程序注册表来查找应用程序中的翻译目录。要在导入时进行翻译，你需要 `gettext_lazy()` 来代替。（使用 `gettext()` 会是一个 bug，因为翻译会在导入时进行，而不是在每次请求时根据活动语言进行。）

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

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

- `ImportError: cannot import name ...` 如果导入序列陷入循环，就会发生这种情况。

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

- `django.contrib.admin` 会自动在已安装的应用程序中执行 `admin` 模块的自动发现。为了防止这种情况发生，请将你的 `INSTALLED_APPS` 改为包含 `'django.contrib.admin.app.SimpleAdminConfig'` 而不是 `'django.contrib.admin'`。