Приложения

Добавлено в Django 1.7.

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

Этот реестр называется просто apps и находится в модуле django.apps:

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

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

Django исторически использует термин проект для установленной версии Django. Проект в первую очередь определяется наличием модуля настроек.

Термин application используется для пакета Python, который предоставляет определенный функционал. Приложения могут повторно использоваться в различных проектах.

Примечание

Сейчас такая терминология вводит в заблуждение т.к. термин “веб-приложение” часто используется для описания проекта Django.

Приложения содержат набор моделей, представлений, шаблонов, шаблонных тегов, статических файлов, URL-ов, мидлваров, и прочее. Они добавляются в проект через настройку INSTALLED_APPS, подключение в URLconfs, настройку MIDDLEWARE_CLASSES, или наследование шаблонов.

Важно помнить, что приложение Django - это просто код, который работает с различными частями фреймверка. Не существует такой вещи, как объект Application. Однако, существуют ситуации, когда Django необходимо работать с установленными приложениями, в основном для конфигурации и интроспекции. Для этого реестр приложений содержит метаданные в объекте AppConfig для каждого экземпляра приложения.

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

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

Если INSTALLED_APPS содержит путь просто к модулю приложения, Django проверяет переменную default_app_config в модуле.

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

Если default_app_config не существует, Django будет использовать базовый класс 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"

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

# rock_n_roll/__init__.py

default_app_config = 'rock_n_roll.apps.RockNRollConfig'

Теперь RockNRollConfig будет использоваться, если в INSTALLED_APPS просто указать 'rock_n_roll'. Теперь пользователи приложения могут использовать настройки из AppConfig без изменения настройки INSTALLED_APPS.

Конечно вы можете попросить пользователей использовать '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

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

Если вы используете приложение с “Rock ’n’ roll” в проекте, который называется anthology, и хотите заменить на “Gypsy jazz”, вы можете добавить свой класс настроек:

# anthology/apps.py

from rock_n_roll.apps import RockNRollConfig

class GypsyJazzConfig(RockNRollConfig):
    verbose_name = "Gypsy jazz"

# anthology/settings.py

INSTALLED_APPS = [
    'anthology.apps.GypsyJazzConfig',
    # ...
]

Опять же, определять классы настроек в под-модуле 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

Путь в файловой системе к каталогу с приложением, например '/usr/lib/python2.7/dist-packages/django/contrib/admin'.

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

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

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

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

Методы

AppConfig.get_models()

Возвращает итератор по классам Model.

AppConfig.get_model(model_name)

Возвращает Model для переданного model_name. Вызывает LookupError, если модель не существует. model_name регистро-независимое значение.

AppConfig.ready()

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

Вы не можете импортировать модели в модуле, который содержит классы настроек, но вы можете использовать метод get_model(), чтобы получить модель по названию:

def ready(self):
    MyModel = self.get_model('MyModel')

Предупреждение

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

Примечание

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

Namespace-пакеты приложений (Python 3.3+)

Python версии 3.3 и выше поддерживает Python пакеты без файла __init__.py. Эти пакеты называют “namespace-пакетами” и могут находится в нескольких каталогах в sys.path (смотрите PEP 420).

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

  1. Namespace-пакет содержит один каталог (то есть не разделен на несколько каталогов)

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

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

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

apps

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

apps.ready

Булев атрибут, который устанавливается в True, когда реестр полностью проинициализирован.

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)

Возвращает Model для app_label и model_name. Для удобства принимает аргумент вида app_label.model_name. model_name - регистро-независимое значение.

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

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

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

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

setup()

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

  • Загрузка настроек.

  • Настройка логирования.

  • Инициализация реестра приложений.

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

  • При запуске 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. Вызывается при импорте настроек приложения или модуля моделей, код которых использует реестр приложений.

    Например, ugettext() использует реестр приложений для поиска каталогов с файлами локализации. Для локализации на этапе импорта используйте ugettext_lazy(). (Использование ugettext() приведет к багу, т.к. перевод будет выполнен на момент импорта, а не для каждого запрос с учетом активной локалид.)

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

    Еще одна причина ошибок - django.contrib.auth.get_user_model(). Используйте настройку AUTH_USER_MODEL для указания ссылки на модель пользователя на этапе импорта.

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

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

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

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