怪诞与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)评估为字符串产生<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一个额外的类属性的ID的列表。结果显示,创建fooDBRef时存在的模块集合为,与创建裸露的bson.dbref.DBRef类型时存在的模块集合完全不同。其中一个的所有模块ID与另一个的所有模块ID不同。

一些可能相关的因素:

  • 上Apache下出现此错误运行mod_wsgi的服务器。
  • 服务器在wsgi下运行两个不同的Django站点(称它们为site_asite_b)。
  • foo是site_a.foo_app.models定义和酒吧在site_b.bar_app.models定义。
  • site_a设置。py在INSTALLED_APPS中有site_b.bar_app
  • 产生错误的请求由site_a处理。
  • 模块sys.modulesfooDBRef创建时,但没有site_a.*模块。 bson.dbref.DBRef的情况正好相反。
  • httpd reload错误后有时会消失一小会儿,和0-10次尝试内的某个时候返回。

谁能帮我找出是什么原因造成fooDBRefbson.dbref.DBRef不同?

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

这可能是MongoDB的Python客户端模块可能并不在Python子口译正常工作,特别是当模块在同一进程中的一个不同的子解释器使用在相同时间的情况下。

所以,你可以在使用WSGIDaemonProcess/WSGIProcessGroup运行在独立的守护程序组中的每个站点,然后使用WSGIApplicationGroup用“%{}全球”的说法强迫的Python解释器为主的用户。

注意,迫使使用主解释的时候为这两个网站,你可以不再使用嵌入模式,或让他们在同一个后台进程组中的两个运行。因此,为什么你需要强制每个运行在单独的守护进程组中。

+0

我们使用嵌入模式。我切换到守护进程模式,加入WSGIDaemonProcess,WSGIProcessGroup和WSGIApplicationGroup指示到虚拟主机的条目,并且WSGISocketPrefix指令到配置的根级别。它似乎有效!至少,这个错误还没有发生,而且通常情况下它会有这个问题。我将等待一天左右,确保它消失,然后接受答案。谢谢您的帮助! – Alanyst

+0

我很满意它现在的作品。再次感谢。 – Alanyst