其他分享
首页 > 其他分享> > 使用mongoengine ReferenceField的怪异

使用mongoengine ReferenceField的怪异

作者:互联网

这是一个令人费解的问题,甚至很难说出来,更不用说描述了.我将从基本事实开始,然后给出可能相关的背景信息.

考虑两个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)评估为字符串会生成<类'bson.dbref.DBRef'>.显然,DBRef没有bar_func属性,但是为什么返回DBRef而不是Bar的实例?

进一步的调试代码显示mongoengine / fields.py中的ReferenceField .__ get__函数中的以下情况失败:

if isinstance(value, (pymongo.dbref.DBRef)):
        value = _get_db().dereference(value)

但是(pymongo.dbref.DBRef)实际上是bson.dbref.DBRef,它似乎与type(foo.bar)相同!为什么实例失败?

这是事情变得很奇怪的地方:

id(type(foo.bar)) == id(bson.dbref.DBRef)  # <-- Evaluates to False!

换句话说,与直接引用bson.dbref.DBRef获得的类型不同,type(foo.bar)是不同的bson.dbref.DBRef.实际上,检查这两种类型的__dict__会显示其功能和属性的不同内存位置.

注意:为了方便起见,我将调用type(foo.bar)fooDBRef返回的类型,以将其与bson.dbref.DBRef引用的类型区分开.

为了进一步调试,我修改了DBRef代码,以添加一个元类,该元类在创建DBRef类型时检查系统模块,并将这些模块的ID列表存储在DBRef的额外类属性中.结果表明,创建fooDBRef时存在的模块集与创建裸bson.dbref.DBRef类型时存在的模块集完全不同.一个模块的所有模块ID与另一个模块的所有模块ID不同.

一些可能相关的因素:

>发生此错误的服务器在Apache下运行mod_wsgi.
>服务器在wsgi下运行两个不同的Django站点(分别称为site_a和site_b).
> Foo在site_a.foo_app.models中定义,而Bar在site_b.bar_app.models中定义.
> site_a settings.py在INSTALLED_APPS中具有site_b.bar_app.
>产生错误的请求由site_a处理.
>创建fooDBRef时,sys.modules中有site_b.*模块,但没有site_a.*模块. bson.dbref.DBRef则相反.
>重新加载httpd后,错误有时会消失一段时间,并在0-10次尝试后的某个时间返回.

谁能帮助我找出导致fooDBRef与bson.dbref.DBRef不同的原因?

解决方法:

您正在使用mod_wsgi的嵌入式模式还是守护程序模式?如果使用mod_wsgi的守护程序模式,是将每个站点委派给不同的守护进程组,然后又迫使该应用程序在主Python解释器中运行?

可能是mongodb Python客户端模块可能无法在Python子解释器中正常工作的情况,尤其是当该模块在同一进程的不同子解释器中同时使用时.

因此,您可能必须使用WSGIDaemonProcess / WSGIProcessGroup在单独的守护进程组中运行每个站点,然后使用带有’%{GLOBAL}’参数的WSGIApplicationGroup强制使用Python主解释器的用户.

请注意,当对两个站点都强制使用主解释器时,您将无法再使用嵌入式模式,也不能使它们都在同一守护进程组中运行.因此,为什么需要强制每个进程在单独的守护进程组中运行.

标签:mod-wsgi,python,django,mongoengine
来源: https://codeday.me/bug/20191101/1987384.html