Приложения
Django содержит реестр установленных приложений, который хранит конфигурацию и предоставляет средства интроспекции. Он также поддерживает список доступных моделей.
Этот реестр называется apps и доступен в django.apps:
>>> from django.apps import apps
>>> apps.get_app_config("admin").verbose_name
'Administration'
Проекты и приложения
Термин проект обозначает веб-приложение Django. Пакет проекта на Python определяется главным образом модулем настроек, но обычно содержит и другие элементы. Например, при запуске django-admin startproject mysite вы получите каталог проекта mysite, содержащий пакет Python mysite с 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» в проекте под названием 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.labelиспользуется в таблицах базы данных и файлах миграций при указании приложения в списке зависимостей.
-
AppConfig.verbose_name -
Понятное человеку имя приложения, например «Администрирование».
По умолчанию этому атрибуту присваивается значение
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[исходный код] -
Тип первичного ключа, который неявно добавляется к моделям в этом приложении. Можно использовать этот атрибут, чтобы сохранить
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'>.Его значение может быть
None, если приложение не содержит модульmodels. Обратите внимание, что сигналы, связанные с базой данных, такие как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с указаннымmodel_name. Сравнение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(). Это касается методов моделей, выполняющих запросы (save(),delete(), методы менеджеров и т. д.), а также прямых SQL-запросов черезdjango.db.connection. Методready()будет выполняться при запуске каждой команды управления. Например, несмотря на то что настройки тестовой базы данных отличаются от рабочих настроек,manage.py testвсе равно выполнит некоторые запросы к вашей рабочей базе данных!Примечание
В обычном процессе инициализации Django вызывает метод
readyтолько один раз. Однако в некоторых особых случаях, особенно при тестировании с изменением списка установленных приложений,readyможет вызываться несколько раз. В таком случае либо пишите идемпотентные методы, либо добавьте в классыAppConfigфлаг, который предотвратит повторное выполнение кода, предназначенного для однократного запуска.
Пакеты пространства имен в качестве приложений
Пакеты Python без файла __init__.py называются «пакетами пространства имен» и могут располагаться в нескольких каталогах в разных местах sys.path (см. PEP 420).
Для приложений Django требуется единый базовый путь в файловой системе, по которому Django (в зависимости от конфигурации) будет искать шаблоны, статические ресурсы и т. д. Поэтому пакеты пространства имен могут быть приложениями Django только при выполнении одного из следующих условий:
- Пакет пространства имен фактически имеет только одно расположение (то есть не распределен по нескольким каталогам).
- Класс
AppConfig, используемый для настройки приложения, имеет атрибут классаpath, содержащий абсолютный путь к каталогу, который Django будет использовать в качестве единого базового пути приложения.
Если не выполнено ни одно из этих условий, Django вызовет исключение ImproperlyConfigured.
Реестр приложений
-
apps -
Реестр приложений предоставляет следующий общедоступный API. Методы, не перечисленные ниже, считаются внутренними и могут изменяться без уведомления.
-
apps.ready -
Булев атрибут, которому присваивается значение
Trueпосле полного заполнения реестра и вызова всех методовAppConfig.ready().
-
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) -
Возвращает
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, если он определён, или/в противном случае. - Инициализирует реестр приложений.
Эта функция вызывается автоматически:
- При запуске HTTP-сервера с использованием поддержки ASGI или WSGI в Django.
- При вызове команды управления.
В других случаях, например в обычных скриптах Python, её необходимо вызывать явно.
Реестр приложений инициализируется в три этапа. На каждом этапе Django обрабатывает все приложения в порядке, заданном в INSTALLED_APPS.
-
Сначала Django импортирует каждый элемент из
INSTALLED_APPS.Если это класс конфигурации приложения, Django импортирует корневой пакет приложения, заданный его атрибутом
name. Если это пакет Python, Django ищет конфигурацию приложения в подмодулеapps.py, а если не находит — создаёт конфигурацию приложения по умолчанию.На этом этапе ваш код не должен импортировать никакие модели!
Иными словами, корневые пакеты ваших приложений и модули, в которых определены классы конфигурации приложений, не должны импортировать модели, даже косвенно.
Строго говоря, Django разрешает импортировать модели после загрузки конфигурации приложения. Однако, чтобы избежать ненужных ограничений на порядок элементов в
INSTALLED_APPS, настоятельно рекомендуется не импортировать модели на этом этапе.После завершения этого этапа становятся доступны API для работы с конфигурациями приложений, например
get_app_config(). -
Затем Django пытается импортировать подмодуль
modelsкаждого приложения, если он существует.Вы должны определить или импортировать все модели приложения в его
models.pyилиmodels/__init__.py. Иначе реестр приложений к этому моменту может оказаться заполнен не полностью, что может привести к сбоям в работе ORM.После завершения этого этапа становятся доступны API для работы с моделями, например
get_model(). - Наконец, Django вызывает метод
ready()каждой конфигурации приложения.
Устранение неполадок
Ниже перечислены распространённые проблемы, с которыми можно столкнуться во время инициализации:
-
AppRegistryNotReady: эта ошибка возникает, когда при импорте конфигурации приложения или модуля моделей выполняется код, зависящий от реестра приложений.Например,
gettext()использует реестр приложений для поиска каталогов переводов в приложениях. Для перевода во время импорта необходимо использоватьgettext_lazy(). (Использованиеgettext()было бы ошибкой, поскольку перевод выполнялся бы во время импорта, а не при каждом запросе с учётом активного языка.)Выполнение запросов к базе данных с помощью ORM во время импорта модулей моделей также вызовет это исключение. ORM не может работать корректно, пока не будут доступны все модели.
Это исключение также возникает, если вы забыли вызвать
django.setup()в автономном скрипте Python. -
ImportError: cannot import name ...Это происходит, если последовательность импорта приводит к циклической зависимости.Чтобы избежать подобных проблем, следует свести к минимуму зависимости между модулями моделей и выполнять как можно меньше действий во время импорта. Чтобы не выполнять код во время импорта, можно перенести его в функцию и кэшировать результаты. Код будет выполнен при первой необходимости в его результатах. Эта концепция называется «ленивым вычислением».
-
django.contrib.adminавтоматически выполняет поиск модулейadminв установленных приложениях. Чтобы отключить это, изменитеINSTALLED_APPS: укажите в нём'django.contrib.admin.apps.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.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/ref/applications/