# Django 6.0.7 documentation

# 应用程序

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` 包含一个应用程序模块的路径而不是一个配置类的路径时自动加载它们。

Note

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

    from django.apps import apps as django_apps

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

项目中直接使用 “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 项目中唯一。

  Warning

  在应用的迁移已应用后更改此属性将导致项目的破坏性更改，或者对于可重用应用，将导致该应用的任何现有安装的破坏性更改。这是因为 `AppConfig.label` 在数据库表和迁移文件中用于在依赖项列表中引用应用。

AppConfig.verbose_name

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

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

AppConfig.path

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

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

AppConfig.default

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

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

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

AppConfig.default_auto_field

  The implicit primary key type to add to models within this app.

  Reusable applications that define models must set this attribute to ensure migrations included in the application match the models. Otherwise, migrations will be generated for the application when a user runs `makemigrations` with a different `DEFAULT_AUTO_FIELD`, which may conflict with future migrations shipped by the application.

  For example, to keep using `AutoField`:

      default_auto_field = "django.db.models.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(include_auto_created=False, include_swapped=False)

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

  要求完整填写应用注册表。

AppConfig.get_model(model_name, require_ready=True)

  返回给出的 `model_name` 的 `Model`。 `model_name` 是大小写敏感的。

  如果应用程序中不存在此模块，则抛出 `LookupError` 异常。

  Requires the app registry to be fully populated unless the `require_ready` argument is set to `False`. `require_ready` behaves exactly as in `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")

  Warning

  Although you can access model classes as described above, avoid interacting with the database in your `ready()` implementation. This includes model methods that execute queries (`save()`, `delete()`, manager methods etc.), and also raw SQL queries via `django.db.connection`. Your `ready()` method will run during startup of every management command. For example, even though the test database configuration is separate from the production settings, `manage.py test` would still execute some queries against your production database!

  Note

  在普通的初始化进程中， `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`。

## 初始化进程

### 应用程序是如何被加载的

When Django starts, `django.setup()` is responsible for populating the application registry.

setup(set_prefix=True)

  配置 Django：

  - 加载配置。

  - 设置日志。

  - 若 `set_prefix` 为 True，为 URL 处理器脚本增加前缀 `FORCE_SCRIPT_NAME`，若未定义此项，则使用 `/`。

  - 初始化应用程序注册。

  这个函数会被自动调用：

  - 当通过Django的ASGI或WSGI运行HTTP服务时。

  - 调用一个管理命令时。

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

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

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

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

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

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

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

   Once this stage completes, APIs that operate on application configurations such as `get_app_config()` become usable.

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

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

   Once this stage completes, APIs that operate on models such as `get_model()` become usable.

3. Finally Django runs the `ready()` method of each application configuration.

### 错误调试

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

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

  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.)

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

  This exception also happens if you forget to call `django.setup()` in a standalone Python script.

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

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

- `django.contrib.admin` 会自动在已安装的应用程序中执行 `admin` 模块的自动发现。为了防止这种情况发生，请将你的 `INSTALLED_APPS` 改为包含 `'django.contrib.admin.app.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())

  为了更容易找到触发此警告的代码，你可以让 Python 将警告视为错误，以显示堆栈跟踪，例如使用 `python -Werror manage.py shell`。