Приложения¶
Django содержит реестр установленных приложений, который хранит конфигурацию и обеспечивает интроспекцию. Он также поддерживает список доступных models.
Этот реестр называется 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, предоставляющий некоторый набор функций. Приложения may be reused в различных проектах.
Приложения включают некоторую комбинацию моделей, представлений, шаблонов, тегов шаблонов, статических файлов, URL-адресов, промежуточного программного обеспечения и т. д. Они обычно подключаются к проектам с помощью настройки INSTALLED_APPS
и, по желанию, с помощью других механизмов, таких как URLconfs, настройки MIDDLEWARE
или наследование шаблонов.
Важно понимать, что приложение Django - это набор кода, который взаимодействует с различными частями фреймворка. Не существует такой вещи, как объект Application
. Однако есть несколько мест, где Django необходимо взаимодействовать с установленными приложениями, в основном для конфигурации, а также для интроспекции. Поэтому реестр приложений хранит метаданные в экземпляре AppConfig
для каждого установленного приложения.
Нет никаких ограничений на то, что пакет проекта не может также считаться приложением и иметь модели и т.д. (что потребовало бы добавить его в INSTALLED_APPS
).
Настройка приложений¶
Чтобы настроить приложение, создайте модуль apps.py
внутри приложения, затем определите в нем подкласс AppConfig
.
Когда INSTALLED_APPS
содержит точечный путь к модулю приложения, по умолчанию, если Django находит ровно один подкласс AppConfig
в подмодуле apps.py
, он использует эту конфигурацию для приложения. Это поведение можно отключить, установив 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"
RockNRollConfig
будет загружаться автоматически, если INSTALLED_APPS
содержит 'rock_n_roll'
. Если вам нужно предотвратить это, установите default
в False
в определении класса.
Вы можете предоставить несколько подклассов AppConfig
с различным поведением. Чтобы указать Django, какой из них использовать по умолчанию, установите default
на True
в его определении. Если ваши пользователи хотят выбрать конфигурацию не по умолчанию, они должны заменить 'rock_n_roll'
на точечный путь к этому конкретному классу в настройках INSTALLED_APPS
.
Атрибут AppConfig.name
сообщает Django, к какому приложению относится данная конфигурация. Вы можете определить любой другой атрибут, документированный в справочнике API AppConfig
.
Подклассы AppConfig
могут быть определены где угодно. Соглашение apps.py
просто позволяет Django автоматически загружать их, когда INSTALLED_APPS
содержит путь к модулю приложения, а не путь к конфигурационному классу.
Примечание
Если ваш код импортирует реестр приложения в __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.
-
AppConfig.
verbose_name
¶ Человекочитаемое имя приложения, например, «Администрирование».
По умолчанию этот атрибут имеет значение
label.title()
.
-
AppConfig.
path
¶ Путь к директории приложения в файловой системе, например,
'/usr/lib/pythonX.Y/dist-packages/django/contrib/admin'
.В большинстве случаев Django может автоматически определить и установить это, но вы также можете предоставить явное переопределение как атрибут класса вашего подкласса
AppConfig
. В некоторых ситуациях это необходимо; например, если пакет приложения представляет собой namespace package с несколькими путями.
-
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
, подаются только для приложений, имеющих модульpost_migrate
.
Методы¶
-
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
все равно будет выполнять некоторые запросы к вашей производственной базе данных!Примечание
В обычном процессе инициализации метод
ready
вызывается Django только один раз. Но в некоторых угловых случаях, особенно в тестах, которые работают с установленными приложениями,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_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
позволяет искать модели while the app registry is being populated, особенно во время второй фазы, когда он импортирует модели. Затем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 resolver вFORCE_SCRIPT_NAME
, если он определен, или/
в противном случае. - Инициализация реестра приложения.
Эта функция вызывается автоматически:
- При запуске HTTP-сервера через поддержку ASGI или WSGI в Django.
- При вызове команды управления.
В других случаях, например, в обычных скриптах Python, его нужно вызывать явно.
Changed in Django 5.0:Вызывает ошибку
RuntimeWarning
, когда приложения взаимодействуют с базой данных до того, как реестр приложений будет полностью заполнен.
Реестр приложений инициализируется в три этапа. На каждом этапе 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 treat warnings as errors для раскрытия трассировки стека, например, с помощью
python -Werror manage.py shell
.