Приложения

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 только в том случае, если верно одно из следующих условий:

  1. Пакет пространства имен фактически имеет только одно местоположение (т.е. не распространяется более чем на один каталог).
  2. Класс 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 позволяет искать модели 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.

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

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

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

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

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

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

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

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

    После завершения этого этапа API, оперирующие такими моделями, как get_model(), становятся пригодными для использования.

  3. Наконец, 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.

Вернуться на верх