импорт functools
IMPORT SYS
импорт пронизывающий
импорт предупреждения
из коллекций импорта счетчика , defaultdict
из functools импорта частичного
от django.core.exceptions импорта AppRegistryNotReady , ImproperlyConfigured
из .config импортировать AppConfig
class Apps :
"" "
Реестр, в котором хранится конфигурация установленных приложений.
Он также отслеживает модели, например, для обеспечения обратных отношений.
«»»
def __init__ ( self , installed_apps = ()):
# installed_apps установлено значение None при создании главного реестра
#, потому что он не может быть заполнен в этот момент. Другие реестры должны
# предоставить список установленных приложений и сразу же заполнить их.
если значение installed_apps равно None, а hasattr ( sys . modules [ __name__ ], 'apps' ):
поднять RuntimeError ( "Вы должны указать аргумент installed_apps." )
# Сопоставление меток приложений => имена моделей => классы моделей. Каждый раз, когда
модель импортируется, ModelBase .__ new__ вызывает apps.register_model, который
# создает запись в all_models. Все импортированные модели регистрируются,
# независимо от того, определены ли они в установленном приложении
# и заполнен ли реестр. Поскольку невозможно
# безопасно повторно
импортировать
модуль (это может повторно выполнить код инициализации) # all_models никогда не переопределяется и не сбрасывается. я . all_models = defaultdict ( ДИКТ )
# Сопоставление меток с экземплярами AppConfig для установленных приложений.
я . app_configs = {}
# Стек app_configs. Используется для хранения текущего состояния в
# set_available_apps и set_installed_apps.
я . stored_app_configs = []
# Заполнен ли реестр.
я . apps_ready = сам . models_ready = сам . ready = False
# Для автозагрузчика.
я . ready_event = потоки . Событие ()
# Блокировка для потокобезопасного заполнения.
я . _lock = потоки . RLock ()
самостоятельно . loading = False
# Сопоставляет ("app_label", "modelname") кортежи со списками функций, которые
#
будут вызываться, когда соответствующая модель будет готова. Используется методами этого класса # `lazy_model_operation ()` и `do_pending_operations ()`.
я . _pending_operations = defaultdict ( список )
# Заполните приложения и модели, если это не главный реестр.
если значение installed_apps не равно None : self . заполнить ( установленные_приложения )
def populate ( self , installed_apps = None ):
"" "
Загрузить конфигурации и модели приложений.
Импортируйте каждый модуль приложения, а затем каждый модуль модели.
Он потокобезопасный и идемпотентный, но не реентерабельный.
«»»
Если самостоятельно . Готов :
возвращение
# populate () может вызываться двумя потоками параллельно на серверах
#, которые создают потоки перед инициализацией вызываемого WSGI.
с собой . _lock :
если self . готово :
вернуться
# RLock предотвращает вход других потоков в этот раздел. Операция
сравнения и установки # ниже является атомарной.
если сам . loading :
# Предотвратить повторные вызовы, чтобы не запускать
методы
AppConfig.ready () # дважды. поднять RuntimeError ( " populate ( ) не реентерабельно" )
self . loading = True
# Этап 1: инициализация конфигураций приложения и импорт модулей приложения.
для записи в installed_apps :
if isinstance ( entry , AppConfig ):
app_config = entry
else :
app_config = AppConfig . создать ( запись ),
если app_config . ярлык в себе . app_configs :
повышение ImproperlyConfigured (
"ярлыки приложений не являются уникальными,"
"дубликатами: % S " % app_config . этикетка )
я . app_configs [ app_config . label ] = app_config
app_config . apps = self
# Проверить повторяющиеся имена приложений.
counts = Counter (
app_config . name for app_config в self . app_configs . values ())
duplicates = [
name for name , count in counts . most_common () if count > 1 ]
if duplicates :
raise ImproperlyConfigured (
«Имена приложений не уникальны»,
«duplicates: % s » % "," . присоединиться ( дубликаты ))
я . apps_ready = Правда
# Этап 2: импорт модулей моделей.
для app_config в себе . app_configs . values ():
app_config . import_models ()
я . clear_cache ()
я . models_ready = Верно
# Этап 3: запуск методов ready () конфигураций приложений.
для app_config в себе . get_app_configs ():
app_config . готов ()
я . готов = Истинную
себя . ready_event . набор ()
def check_apps_ready ( self ):
"" "
Вызвать исключение, если все приложения еще не импортированы." "" если не self . apps_ready :
от django.conf Импорта Настройки
# Если «не готовы» из - за неконфигурированные настройки доступа
# INSTALLED_APPS поднимает более полезный ImproperlyConfigured
# исключения.
настройки . INSTALLED_APPS
поднять AppRegistryNotReady ( «Приложения еще не загружены» ).
def check_models_ready ( self ):
"" "
Вызвать исключение, если все модели еще не импортированы." "" если не self . models_ready :
поднять AppRegistryNotReady ( «Модели еще не загружены.» )
def get_app_configs ( self ):
"" "Импортировать приложения и возвращать итерацию конфигураций приложений." ""
self . check_apps_ready ()
вернуть себя . app_configs . значения ()
def get_app_config ( self , app_label ):
"" "
Импортировать приложения и возвращает конфигурацию приложения для данной метки.
Поднимите LookupError, если приложение с этой меткой не существует.
"" "
self . check_apps_ready ()
try :
return self . app_configs [ app_label ]
except KeyError :
message = " Не установлено приложение с меткой ' % s '. " % app_label
для app_config в self . get_app_configs ():
if app_config . name = = app_label :
message + = "Возможно, вы имели в виду" % s'? " % app_config . label
break
вызывать LookupError ( сообщение )
# Этот метод критичен к производительности, по крайней мере, для набора тестов Django.
@functools . lru_cache ( maxsize = None )
def get_models ( self , include_auto_created = False , include_swapped = False ):
"" "
Возвращает список всех установленных моделей.
По умолчанию следующие модели не включены:
- автоматически созданные модели для отношений «многие ко многим» без
явной промежуточной таблицы,
- модели, которые были заменены местами.
Задайте для соответствующего аргумента ключевого слова значение True, чтобы включить такие модели.
"" "
self . check_models_ready ()
result = []
для app_config в себе . app_configs . values ():
результат . extend ( app_config . get_models ( include_auto_created , include_swapped ))
вернуть результат
def get_model ( self , app_label , model_name = None , require_ready = True ):
"" "
Возвращает модель, соответствующую указанным app_label и model_name.
В качестве ярлыка app_label может иметь форму <app_label>. <model_name>.
имя_модели не чувствительно к регистру.
Поднимите LookupError, если приложение с этой меткой не
существует
или в приложении не существует модели с таким именем. Поднимите ValueError, если вызывается с одним аргументом, который не содержит ровно одной точки.
"" "
если require_ready :
self . check_models_ready ()
else :
self . check_apps_ready ()
если имя_модели - Нет :
app_label , model_name = app_label . разделить ( '.' )
app_config = себя . get_app_config ( app_label )
если не require_ready и app_config . модели является None :
app_config . import_models ()
вернуть app_config . get_model ( имя_модели , require_ready = require_ready )
def register_model ( self , app_label , model ):
# Поскольку этот метод вызывается при импорте моделей, он не может
# выполнять импорт из-за риска возникновения циклов импорта. Он не должен
# вызывать get_app_config ().
model_name = модель . _meta . имя_модели
app_models = self . all_models [ app_label ]
if model_name в app_models :
if ( model . __name__ == app_models [имя_модели ] . __name__ и
модель . __module__ == app_models [ название_модели ] . __module__ ):
предупреждения . warn (
«Модель ' % s . % s ' уже была зарегистрирована.»
«Перезагрузка моделей не рекомендуется, так как это может привести к несогласованности»
«особенно со связанными моделями.» % ( app_label , model_name ),
RuntimeWarning , stacklevel = 2 )
else :
поднять RuntimeError (
"Конфликт моделей ' % s ' в приложении ' % s ': % s и % s ." %
( Model_name , app_label , app_models [ model_name ], model ))
app_models [ model_name ] = model
self . do_pending_operations ( модель )
self . clear_cache ()
def is_installed ( self , app_name ):
"" "
Проверить, существует ли приложение с этим именем в реестре.
app_name - это полное имя приложения, например django.contrib.admin.
"" "
self . check_apps_ready ()
вернуть любое ( ac . name == app_name для ac в self . app_configs . values ())
def get_contain_app_config ( self , object_name ):
"" "
Найдите конфигурацию приложения, содержащую данный объект.
object_name - это путь Python к объекту, обозначенный точками.
Вернуть конфигурацию приложения для внутреннего приложения в случае вложенности.
Верните None, если объект отсутствует в зарегистрированной конфигурации приложения.
"" "
self . check_apps_ready ()
кандидаты = []
для app_config в self . app_configs . values ():
если имя_объекта . начинается с ( app_config . name ):
subpath = object_name [ len ( app_config . name ):]
if subpath == '' или подпуть [ 0 ] == '.' :
кандидаты . добавить ( app_config ),
если кандидаты :
вернуть отсортированные ( кандидаты , ключ = лямбда ac : - len ( ac . name )) [ 0 ]
def get_registered_model ( self , app_label , model_name ):
"" "
Подобно get_model (), но не требует наличия приложения с
данным app_label.
Этот метод можно безопасно вызывать во время импорта, даже когда реестр
заполняется.
"" "
model = self . all_models [ app_label ] . get ( model_name . lower ())
if model is None :
raise LookupError (
" Модель ' % s . % s ' не зарегистрирована. " % ( app_label , model_name ))
return model
@functools . lru_cache ( maxsize = None )
def get_swappable_settings_name ( self , to_string ):
"" "
Для заданной строки модели (например," auth.User ") вернуть имя
соответствующего имени настройки, если оно относится к заменяемой модели. Если
указанная модель не подлежит замене, вернуть None.
Этот метод украшен lru_cache, потому что он
критически важен для
производительности при миграции. Поскольку заменяемые настройки не меняются после того, как Django загрузил настройки, нет причин получать
соответствующий атрибут настроек снова и снова.
«»»
Для модели в себе . Get_models ( include_swapped = Правда ):
поменять местами = модель . _Meta . Местами
# эта модель выгружена для модели , заданной to_string?
Если поменять местами и местами == to_string:
вернуть модель . _meta . swappable
# Можно ли поменять эту модель и модель, заданную to_string?
если модель . _meta . заменяемый и модель . _meta . label == to_string :
вернуть модель . _meta . заменяемый
возврат Нет
def set_available_apps ( self , available ):
"" "
Ограничить набор установленных приложений, используемых get_app_config [s].
available должен быть повторяющимся именами приложений.
set_available_apps () должен быть сбалансирован с unset_available_apps ().
В основном используется для оптимизации производительности в TransactionTestCase.
Этот метод безопасен в том смысле, что не запускает импорт.
«» «
Доступно = набор ( доступно )
установлено = { app_config . Имя для app_config в себе . Get_app_configs ()}
если не доступны . Issubset ( установлено ):
Raise ValueError (
» Доступные приложения не является подмножество установленных приложений, дополнительных приложения : % s "
% ", " . join ( доступно - установлен )
)
я . сохранено_app_configs . добавить ( self . app_configs )
self . app_configs = {
label : app_config
для ярлыка , app_config в себе . app_configs . items (),
если app_config . имя в наличии
}
я . clear_cache ()
def unset_available_apps ( self ):
"" "Отменить предыдущий вызов set_available_apps ()." ""
self . app_configs = себя . сохранено_app_configs . pop ()
self . clear_cache ()
def set_installed_apps ( self , installed ):
"" "
Включить другой набор установленных приложений для get_app_config [s].
установленный должен быть итеративным в том же формате, что и INSTALLED_APPS.
set_installed_apps () должен быть сбалансирован с unset_installed_apps (),
даже если он завершается с исключением.
В основном используется как приемник сигнала setting_changed в тестах.
Этот метод может запускать новый импорт, который может добавлять новые модели в
реестр всех импортированных моделей. Они останутся в реестре даже
после unset_installed_apps (). Поскольку невозможно
безопасно
воспроизвести импорт (например, это может привести к двойной регистрации слушателей), модели регистрируются при импорте и никогда не удаляются.
"" "
если не self . ready :
поднять AppRegistryNotReady ( " Реестр приложений еще не готов. " )
self . stored_app_configs . append ( self . app_configs )
я . app_configs = {}
сам . apps_ready = сам . models_ready = сам . загрузка = сам . готов = ложных
себя . clear_cache ()
самостоятельно . заполнить ( установлено )
def unset_installed_apps ( self ):
"" "Отменить предыдущий вызов set_installed_apps ()." ""
self . app_configs = себя . сохранено_app_configs . pop ()
self . apps_ready = сам . models_ready = сам . готов = Истинную
себя . clear_cache ()
def clear_cache ( self ):
"" "
Очистить все внутренние кеши для методов, изменяющих реестр приложения.
В основном это используется в тестах.
"" "
# Вызов истекает кеш для каждой модели. Это очистит
# дерево отношений и кеш полей.
Self . Get_models . Cache_clear ()
if self . Ready :
# Обойти self.get_models (), чтобы предотвратить переполнение кеша.
#. это , в частности , что предотвращает пустое значение кэшируется при клонировании
для app_config в собственной . app_configs . значения ():
для модели в app_config . get_models ( include_auto_created= True ):
модель . _meta . _expire_cache ()
def lazy_model_operation ( self , function , * model_keys ):
"" "
Возьмите функцию и несколько кортежей (" app_label "," modelname "), и
когда все соответствующие модели будут импортированы и зарегистрированы,
вызовите функцию с моделью классы в качестве аргументов.
Функция, переданная этому методу, должна принимать ровно n моделей в качестве
аргументов, где n = len (model_keys).
"" "
# Базовый случай: без аргументов, просто выполните функцию.
Если не model_keys :
function ()
# Рекурсивный случай: возьмите заголовок model_keys, дождитесь
# импорта и регистрации соответствующего класса модели, затем примените
# этот аргумент в предоставленную функцию. Передайте полученный частичный
# в lazy_model_operation () вместе с оставшимися аргументами модели и
# повторяйте, пока все модели не будут загружены и все аргументы не будут применены.
else :
next_model , * more_models = model_keys
# Это будет выполнено после того, как класс, соответствующий next_model
#, будет импортирован и зарегистрирован. Атрибут `func` обеспечивает
совместимость # типа утки с партиалами.
def apply_next_model ( модель ):
next_function = partial ( apply_next_model . func , model )
self . lazy_model_operation ( next_function , * more_models )
apply_next_model . func = функция
# Если модель уже была импортирована и зарегистрирована, частично
примените ее к функции сейчас. Если нет, добавьте его в список
ожидающих # операций для модели, где он будет выполняться с
# классом модели в качестве единственного аргумента, как только модель будет готова.
попробуйте :
model_class = self . get_registered_model ( * next_model ),
кроме LookupError :
self . _nding_operations [ следующая_модель ] . append ( apply_next_model )
else :
apply_next_model (model_class )
def do_pending_operations ( self , model ):
"" "
Возьмите только что подготовленную модель и передайте ее каждой ожидающей
ее функции. Это вызывается в самом конце Apps.register_model ().
" ""
key = model . _meta . app_label , модель . _meta . имя_модели
для функции в себе . _pending_operations . pop ( key , []):
функция ( модель )
apps = Приложения ( installed_apps = Нет )