Приложения

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

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

Чтобы сконфигурировать приложение, подкласс AppConfig и поместите точечный путь к этому подклассу в INSTALLED_APPS.

Когда INSTALLED_APPS содержит точечный путь к модулю приложения, Django проверяет наличие переменной default_app_config в этом модуле.

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

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

default_app_config позволяет приложениям, предшествующим Django 1.7, таким как django.contrib.admin, отказаться от использования AppConfig функций, не требуя от пользователей обновления 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"

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

# rock_n_roll/__init__.py

default_app_config = 'rock_n_roll.apps.RockNRollConfig'

Это приведет к тому, что RockNRollConfig будет использоваться, когда INSTALLED_APPS содержит 'rock_n_roll'. Это позволит вам использовать возможности AppConfig, не требуя от ваших пользователей обновлять настройки INSTALLED_APPS. Кроме этого случая использования, лучше избегать использования default_app_config и вместо этого указать класс конфигурации приложения в INSTALLED_APPS, как описано далее.

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

Рекомендуется помещать класс конфигурации в подмодуль приложения под названием apps. Однако Django этого не делает.

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

Примечание

Если ваш код импортирует реестр приложения в __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 - это соглашение, а не требование.

Конфигурация приложения

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.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()[исходный код]

Возвращает итерабельность 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-сервера через поддержку 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``q вашего приложения. В противном случае реестр приложения может быть не полностью заполнен в этот момент, что может привести к сбою в работе 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'.

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