【问题标题】:Weirdness with mongoengine ReferenceFieldmongoengine ReferenceField 的奇怪之处
【发布时间】:2011-12-07 23:16:45
【问题描述】:

这是一个令人费解的问题,甚至难以命名,更不用说描述了。我将从基本事实开始,然后提供可能相关的背景信息。

考虑两个 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) 作为字符串评估会产生&lt;class 'bson.dbref.DBRef'&gt;。显然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)一样!为什么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 类型时检查系统模块,并将这些模块的ID 列表存储在一个额外的类属性中DBRef。结果表明,创建fooDBRef 时存在的模块集与创建裸bson.dbref.DBRef 类型时存在的模块集完全不同。一个的所有模块 ID 都不同于另一个的所有模块 ID。

一些可能相关的因素:

  • 出现此错误的服务器在 Apache 下运行 mod_wsgi。
  • 服务器在 wsgi 下运行两个不同的 Django 站点(称它们为 site_asite_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 reload 之后,错误有时会消失一段时间,然后在 0-10 次尝试后返回。

谁能帮我找出导致fooDBRefbson.dbref.DBRef 不同的原因?

【问题讨论】:

    标签: python django mod-wsgi mongoengine


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 我们使用的是嵌入式模式。我切换到守护程序模式,将 WSGIDaemonProcess、WSGIProcessGroup 和 WSGIApplicationGroup 指令添加到 VirtualHost 条目,并将 WSGISocketPrefix 指令添加到配置的根级别。它似乎奏效了!至少,这个错误还没有发生,而且通常它会在这一点上发生。我会等一天左右,以确保它已经消失,然后接受答案。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2012-02-29
    • 1970-01-01
    • 2014-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多