Django 1.7:惰性评估以避免 "App registry isn't ready yet" in models.py

Django 1.7: Lazy evaluation to avoid "App registry isn't ready yet" in models.py

我在模型的方法定义中有一个查询:

'content_type_id': ContentType.objects.get_for_model(model).pk,

https://github.com/seperman/django-tagging/blob/develop/tagging/models.py#L107

它会引发 "App registry is not ready yet"。

将这一行放在另一个独立的函数中也无济于事,因为它仍然会被调用。

Django 文档:

Executing database queries with the ORM at import time in models modules will also trigger this exception. The ORM cannot function properly until all models are available.

如何让它懒惰地评估这一行? 我考虑过通过管理命令将其结果手动缓存在一个文件中。然后读取缓存的结果但是应该有很多better/cleaner解决方案(?)

./manage.py shell
Traceback (most recent call last):
File "./manage.py", line 10, in <module>
  execute_from_command_line(sys.argv)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
  utility.execute()
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 354, in execute
  django.setup()
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/__init__.py", line 21, in setup
  apps.populate(settings.INSTALLED_APPS)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/apps/registry.py", line 108, in populate
  app_config.import_models(all_models)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/apps/config.py", line 202, in import_models
  self.models_module = import_module(models_module_name)
File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
  __import__(name)
File "/var/www/something.com/something/current/apps/riding/models.py", line 32, in <module>
  class RidingSection(CategoryBase):
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/mptt/models.py", line 244, in __new__
  cls = meta.register(cls)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/mptt/models.py", line 325, in register
  obj = getattr(cls, attr)
File "/var/www/something.com/something/current/apps/tagging/fields.py", line 54, in __get__
  return edit_string_for_tags(Tag.objects.usage_for_model(owner))
File "/var/www/something.com/something/current/apps/tagging/models.py", line 148, in usage_for_model
  usage = self.usage_for_queryset(queryset, counts, min_count)
File "/var/www/something.com/something/current/apps/tagging/models.py", line 185, in usage_for_queryset
  return self._get_usage(queryset.model, counts, min_count, extra_joins, extra_criteria, params)
File "/var/www/something.com/something/current/apps/tagging/models.py", line 107, in _get_usage
  'content_type_id': ContentType.objects.get_for_model(model).pk,
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/contrib/contenttypes/models.py", line 50, in get_for_model
  defaults={'name': smart_text(opts.verbose_name_raw)},
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/manager.py", line 92, in manager_method
  return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py", line 422, in get_or_create
  return self.get(**lookup), False
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py", line 345, in get
  clone = self.filter(*args, **kwargs)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py", line 691, in filter
  return self._filter_or_exclude(False, *args, **kwargs)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py", line 709, in _filter_or_exclude
  clone.query.add_q(Q(*args, **kwargs))
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1287, in add_q
  clause, require_inner = self._add_q(where_part, self.used_aliases)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1314, in _add_q
  current_negated=current_negated, connector=connector)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1138, in build_filter
  lookups, parts, reffed_aggregate = self.solve_lookup_type(arg)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1076, in solve_lookup_type
  _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1339, in names_to_path
  field, model, direct, m2m = opts.get_field_by_name(name)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/options.py", line 416, in get_field_by_name
  cache = self.init_name_map()
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/options.py", line 445, in init_name_map
  for f, model in self.get_all_related_m2m_objects_with_model():
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/options.py", line 563, in get_all_related_m2m_objects_with_model
  cache = self._fill_related_many_to_many_cache()
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/db/models/options.py", line 577, in _fill_related_many_to_many_cache
  for klass in self.apps.get_models():
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/utils/lru_cache.py", line 101, in wrapper
  result = user_function(*args, **kwds)
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/apps/registry.py", line 168, in get_models
  self.check_models_ready()
File "/var/www/something.com/something/shared/env/local/lib/python2.7/site-packages/django/apps/registry.py", line 131, in check_models_ready
  raise AppRegistryNotReady("Models aren't loaded yet.")

django.core.exceptions.AppRegistryNotReady: 模型尚未加载。

您在应用程序注册之前调用 _get_usage。无论您在哪里调用它,都需要将其更改为已配置应用程序的位置。基本上,如果您在导入时需要模型,您应该使用 ready().

例如,如果您有一个 signals.py,您可以这样做:

# in apps.py
class SomeAppConfig(AppConfig):
    def ready(self):
        from . import signals

或者您的情况:

# in apps.py
class SomeAppConfig(AppConfig):
    def ready(self):
        mgr = TagManager()
        mgr._get_usage(#some_args)

如文档所述:ready() ... is called as soon as the registry is fully populated

django 标记在这里有点不体贴。

TagField is a descriptor,这意味着如果您访问 YourModel.tags,它会运行执行数据库查询的代码。

碰巧 django-mptt 在启动期间(在加载应用程序缓存之前)遍历了 MPTTModel 类 的所有属性,因此恰好触发了此异常。

我不知道你如何在维护 API 的同时在 django 标记中防止这种情况(YourModel.tags 必须从 某处获取标签 ).像这样的问题可能是 django-tagging 不再维护的原因。

但是,我是 django-mptt 的维护者并且我committed a workaround。如果您升级到 mptt 的主版本,您应该会发现它现在可以工作了。