Приложения

Django содержит реестр установленных приложений, который содержит текущие настройки и предоставляет интроспекцию. Также предоставляет список доступных моделей.

This registry is called apps and it’s available in django.apps:

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

Проекты и приложения

The term project describes a Django web application. The project Python package is defined primarily by a settings module, but it usually contains other things. For example, when you run django-admin startproject mysite you’ll get a mysite project directory that contains a mysite Python package with settings.py, urls.py, asgi.py and wsgi.py. The project package is often extended to include things like fixtures, CSS, and templates which aren’t tied to a particular application.

A project’s root directory (the one that contains manage.py) is usually the container for all of a project’s applications which aren’t installed separately.

The term application describes a Python package that provides some set of features. Applications may be reused in various projects.

Applications include some combination of models, views, templates, template tags, static files, URLs, middleware, etc. They’re generally wired into projects with the INSTALLED_APPS setting and optionally with other mechanisms such as URLconfs, the MIDDLEWARE setting, or template inheritance.

It is important to understand that a Django application is a set of code that interacts with various parts of the framework. There’s no such thing as an Application object. However, there’s a few places where Django needs to interact with installed applications, mainly for configuration and also for introspection. That’s why the application registry maintains metadata in an AppConfig instance for each installed application.

There’s no restriction that a project package can’t also be considered an application and have models, etc. (which would require adding it to INSTALLED_APPS).

Настройка приложений

Для настройки приложения создайте класс наследник AppConfig и укажите путь для его импорта в INSTALLED_APPS.

When INSTALLED_APPS contains the dotted path to an application module, Django checks for a default_app_config variable in that module.

Если она определена, она должна содержать путь для импорта класса наследника AppConfig для этого приложения.

Если default_app_config не существует, Django будет использовать базовый класс AppConfig.

default_app_config позволяет использовать AppConfig для приложений, которые были созданы до Django 1.7, например django.contrib.admin, без изменения INSTALLED_APPS.

Новые приложения не должны использовать default_app_config. Вместо этого они должны требовать указать путь к классу-наследнику AppConfig в INSTALLED_APPS.

Для разработчика приложений

Если вы разрабатываете приложение, которое называется «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"

Вы можете указать Django использовать этот класс по умолчанию следующим образом:

# rock_n_roll/__init__.py

default_app_config = 'rock_n_roll.apps.RockNRollConfig'

That will cause RockNRollConfig to be used when INSTALLED_APPS contains 'rock_n_roll'. This allows you to make use of AppConfig features without requiring your users to update their INSTALLED_APPS setting. Besides this use case, it’s best to avoid using default_app_config and instead specify the app config class in INSTALLED_APPS as described next.

Конечно вы можете попросить пользователей использовать 'rock_n_roll.apps.RockNRollConfig' в INSTALLED_APPS. Вы даже можете предоставить несколько классов наследников AppConfig с различными настройками и позволить пользователям добавить необходимый в INSTALLED_APPS.

Принято все классы настроек добавлять в под-модуль apps приложения. Но Django не заставляет соблюдать это правило.

Необходимо указывать атрибут name, чтобы Django мог определить к какому приложению относится класс конфигурации. Полный список параметров можно найти в описании API AppConfig.

Примечание

Если ваш код импортирует реестр приложений в __init__.py приложения, название apps будет пересекаться с под-модулем apps. Можно вынести этот код в под-модуль приложения и импортировать его в __init__.py. Другой вариант - импортировать реестр под другим названием:

from django.apps import apps as django_apps

Для пользователей приложений

If you’re using «Rock ’n’ roll» in a project called anthology, but you want it to show up as «Jazz Manouche» instead, you can provide your own configuration:

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

Должен быть уникальным для проекта.

AppConfig.label

Короткое название(метка) приложения, например 'admin'

Этот атрибут позволяет поменять метку приложения, если два приложения используют по умолчанию одинаковые метки. По умолчанию метка равна последней части значения name. Метка должна быть правильным идентификатором Python.

Должен быть уникальным для проекта.

AppConfig.verbose_name

Читабельное название приложения, например «Administration».

По умолчанию равен label.title().

AppConfig.path

Filesystem path to the application directory, e.g. '/usr/lib/pythonX.Y/dist-packages/django/contrib/admin'.

В большинстве случае Django может автоматически определить и установить это значение, но вы можете переопределить его в классе наследнике AppConfig. Это может понадобиться в некоторых случаях, например, если пакет приложения является namespace-пакетом и расположен в нескольких каталогах.

Неизменяемые атрибуты

AppConfig.module

Root module for the application, e.g. <module 'django.contrib.admin' from 'django/contrib/admin/__init__.py'>.

AppConfig.models_module

Module containing the models, e.g. <module 'django.contrib.admin.models' from 'django/contrib/admin/models.py'>.

Может быть None, если приложение не содержит модуль models. Обратите внимание, сигналы, связанные с базой данных, такие как pre_migrate и post_migrate, вызываются только для приложений, которые содержат модуль models.

Методы

AppConfig.get_models()

Возвращает итератор по классам Model для текущего приложения.

Requires the app registry to be fully populated.

AppConfig.get_model(model_name, require_ready=True)

Returns the Model with the given model_name. model_name is case-insensitive.

Raises LookupError if no such model exists in this application.

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

Класс наследник может переопределить этот метод, чтобы выполнить инициализацию приложения, например зарегистрировать сигналы. Вызывает, когда реестр приложений будет полностью инициализирован и все приложения будут добавлены.

Although you can’t import models at the module-level where AppConfig classes are defined, you can import them in ready(), using either an import statement or get_model().

If you’re registering model signals, you can refer to the sender by its string label instead of using the model class itself.

Example:

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(). Это включает методы, которые выполняют запросы к базе данных (save(), delete(), методы менеджера и т.д.) и SQL запросы через django.db.connection. Метод ready() будет вызываться при каждом запуске команды Django. Например, хотя настройка тестовой базы данных отделена от рабочих настроек проекта, manage.py test выполнила бы запросы на рабочей базе данных!

Примечание

При обычном процессе инициализации метод ready вызывается Django только один раз. Но в некоторых случаях, в частности при выполнении тестов, ready может вызываться несколько раз. Вам следует писать идемпотентный код(который можно безопасно вызывать несколько раз), или добавить флаг в класс AppConfig и проверять его, чтобы код выполнялся только один раз.

Namespace packages as apps

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 (в зависимости от настроек) будет искать шаблоны, статические файлы, и прочее. Таким образом, namespace-пакеты могут быть приложениями Django только при следующих условиях:

  1. Namespace-пакет содержит один каталог (то есть не разделен на несколько каталогов)
  2. Используется класс AppConfig, указывающий в path один абсолютный путь к каталогу, который Django будет использовать как каталог приложения.

Если ни одно из этих условий не соблюдено, Django вызовет исключение ImproperlyConfigured.

Реестр приложений

apps

Реестр приложений предоставляет следующий публичный API. Методы, которые не описаны здесь, являются приватными и могут изменяться в будущем без предупреждений.

apps.ready

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

apps.get_app_configs()

Возвращает итератор по объектам AppConfig.

apps.get_app_config(app_label)

Возвращает AppConfig приложения для app_label. Вызывает LookupError, если приложение не найдено.

apps.is_installed(app_name)

Проверяет добавлено ли приложение с таким названием в реестр. app_name - полное название приложения, например 'django.contrib.admin'.

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

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.

Вызывает LookupError, если приложение или модель не найдена. Вызывает ValueError, если передан один аргумент неправильного формата.

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.

Процесс инициализации

Как загружаются приложения

Функция django.setup() отвечает за заполнение реестра приложений при запуске Django.

setup(set_prefix=True)

Настраивает Django, выполняя следующие действия:

  • Загрузка настроек.
  • Настройка логирования.
  • If set_prefix is True, setting the URL resolver script prefix to FORCE_SCRIPT_NAME if defined, or / otherwise.
  • Инициализация реестра приложений.

Эта функция вызывается автоматически:

  • При запуске HTTP сервера с Django через WSGI.
  • При выполнении команды Django.

Этот метод необходимо вызывать явно в некоторых случаях, например в Python скрипте.

Реестр приложений инициализируется в три этапа. На каждом этапе Django обрабатывает приложения в порядке, указанном в INSTALLED_APPS.

  1. Первым делом Django импортирует каждый элемент INSTALLED_APPS.

    Если это класс настроек приложения, Django импортирует главный пакет приложения, указанный в атрибуте name. Если это пакет Python, Django создает настройки по умолчанию для приложения.

    На этом этапе ваш код не должен импортировать модели!

    Другими словами, ваш главный пакет и модули, которые содержат классы настроек, не должны импортировать модели, в том числе и неявно.

    По правде говоря, Django позволяет импортировать модели, когда настройки приложения уже загружены. Однако, чтобы избежать проблем и ограничений с порядком приложений в INSTALLED_APPS, мы настоятельно рекомендуем не делать этого на этом этапе.

    После выполнения этого этапа, можно использовать API, который работает с настройками приложения, например get_app_config().

  2. Затем Django пытается импортировать модуль models каждого приложения, если такой существует.

    Вы должны определить или импортировать все модели в models.py или models/__init__.py приложения. Иначе, реестр приложений будет не полностью заполнен, что может привести к неправильной работе ORM.

    После выполнения этого этапа, можно использовать API, который работает с моделями, например get_model().

  3. В конце Django вызывает метод ready() для каждого приложения.

Решение проблем

Вот список некоторых проблем, которые могут возникнуть при инициализации:

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

    Выполнение запросов к базе данных с помощью ORM в процессе импорта модуля моделей вызовет исключение. ORM не может работать, пока все модели не будут загружены.

    Эта ошибка также вызывается, если django.setup() не был вызван в Python скрипте.

  • ImportError: cannot import name .... Ошибка вызывается при циклических импортах.

    Для устранения таких проблем необходимо минимизировать зависимости между модулями моделей и в процессе импорта выполнять минимум действий. Чтобы избежать выполнения кода на этапе импорта, вынесите его в функцию, которая умеет кэшировать результат. Затем вызовите функции при необходимости. Такая концепция называется «ленивое выполнение».

  • django.contrib.admin автоматически выполняет регистрацию модулей admin установленных приложений. Чтобы отключить это, укажите в INSTALLED_APPS 'django.contrib.admin.apps.SimpleAdminConfig' вместо 'django.contrib.admin'.