Это загадочная проблема, которую трудно даже назвать, не говоря уже о том, чтобы ее описать. Я начну с основных фактов, а затем приведу справочную информацию.
Рассмотрим две модели документов mongoengine:
class Bar(Document):
# ...
# field definitions
# ...
def bar_func(self):
pass # ...or some arbitrary code
class Foo(Document):
bar = ReferenceField(Bar)
Следующее непоследовательно создает AttributeError
на нашем производственном сервере:
# Assume foo_id references a valid Foo document in Mongo
# and that its 'bar' reference is to a valid Bar document.
foo = Foo.objects.with_id(foo_id)
foo.bar.bar_func() # <-- AttributeError on 'bar_func'
Если я размещу код отладки прямо перед местом ошибки, оценка type(foo.bar)
как строки приводит к <class 'bson.dbref.DBRef'>
. Очевидно, что DBRef
не имеет атрибута bar_func
, но почему возвращается DBRef
вместо экземпляра Bar
?
Дальнейший код отладки показывает, что в функции ReferenceField.__get__
в mongoengine/fields.py
не выполняется следующее условие:
if isinstance(value, (pymongo.dbref.DBRef)):
value = _get_db().dereference(value)
Но (pymongo.dbref.DBRef)
на самом деле bson.dbref.DBRef
, что похоже на type(foo.bar)
! Почему isinstance
терпит неудачу?
Вот где все становится действительно странно:
id(type(foo.bar)) == id(bson.dbref.DBRef) # <-- Evaluates to False!
Другими словами, type(foo.bar)
- это , отличное bson.dbref.DBRef
от того, которое получено путем прямой ссылки на bson.dbref.DBRef
. Фактически, проверка __dict__
этих двух типов показывает различные области памяти для их функций и свойств.
Примечание: Для удобства ниже я назову тип, возвращаемый type(foo.bar)
fooDBRef
, чтобы отличить его от типа, на который ссылается bson.dbref.DBRef
.
Для дальнейшей отладки я изменил код DBRef
, добавив метакласс, который проверяет системные модули во время создания типа DBRef
и сохраняет список идентификаторов этих модулей в дополнительном атрибуте класса: DBRef
. Результаты показывают, что набор модулей, существующих при создании fooDBRef
, полностью отличается от набора модулей, существующих при создании типа bson.dbref.DBRef
. Все идентификаторы модулей для одного отличаются от всех идентификаторов модулей для другого.
Некоторые, возможно, соответствующие факторы:
- Сервер, на котором возникает эта ошибка, запускает mod_wsgi под Apache.
- Сервер запускает два разных сайта Django под wsgi (назовите их
site_a
и site_b
).
- Foo определено в
site_a.foo_app.models
, а Bar определено в site_b.bar_app.models
.
site_a
settings.py имеет site_b.bar_app
в INSTALLED_APPS
.
- Запросы, которые вызывают ошибку, обрабатываются
site_a
.
- При создании
fooDBRef
в sys.modules
было site_b.*
модулей, но нет site_a.*
модулей. Обратное верно для bson.dbref.DBRef
.
- После
httpd reload
ошибка иногда исчезает на некоторое время и возвращается через 0-10 попыток.
Может кто-нибудь помочь мне выяснить, чем причина fooDBRef
отличается от bson.dbref.DBRef
?