2016-04-26 52 views
0

我的問題這個職位相匹配的Bountysource:問題與DictField在MongoEngine DynamicDocument

https://www.bountysource.com/issues/32756263-attribute-error-when-building-nested-index-for-dictfield-in-dynamicdocument

我工作的地方已經用PyMongo 2.6.3/MongoEngine 0.8.7一會兒,我們正在考慮升級到PyMongo 3.2.2/MongoEngine 0.10.6。我下載了這些版本並開始測試它們。我開始遇到一個基於使用DictFields的DynamicDocuments的類。

14:20:20 ERROR nas.data.sync       Traceback (most recent call last): 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/nas/data/sync.py", line 1484, in main 
14:20:20 ERROR nas.data.sync        result = funct(external, syncRecord) 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/nas/data/sync.py", line 456, in syncAsset 
14:20:20 ERROR nas.data.sync        success = scheduleUpdate(asset, v) and success 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/nas/data/sync.py", line 299, in scheduleUpdate 
14:20:20 ERROR nas.data.sync        for wu in workunits: 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/auxiliary/__all/MongoEngine-0.10.6/mongoengine/queryset/queryset.py", line 80, in _iter_results 
14:20:20 ERROR nas.data.sync        self._populate_cache() 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/auxiliary/__all/MongoEngine-0.10.6/mongoengine/queryset/queryset.py", line 92, in _populate_cache 
14:20:20 ERROR nas.data.sync        self._result_cache.append(self.next()) 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/auxiliary/__all/MongoEngine-0.10.6/mongoengine/queryset/base.py", line 1407, in next 
14:20:20 ERROR nas.data.sync        raw_doc = self._cursor.next() 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/auxiliary/__all/MongoEngine-0.10.6/mongoengine/queryset/base.py", line 1481, in _cursor 
14:20:20 ERROR nas.data.sync        self._cursor_obj = self._collection.find(self._query, 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/auxiliary/__all/MongoEngine-0.10.6/mongoengine/queryset/base.py", line 1515, in _query 
14:20:20 ERROR nas.data.sync        self._mongo_query = self._query_obj.to_query(self._document) 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/auxiliary/__all/MongoEngine-0.10.6/mongoengine/queryset/visitor.py", line 90, in to_query 
14:20:20 ERROR nas.data.sync        query = query.accept(QueryCompilerVisitor(document)) 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/auxiliary/__all/MongoEngine-0.10.6/mongoengine/queryset/visitor.py", line 155, in accept 
14:20:20 ERROR nas.data.sync        return visitor.visit_query(self) 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/auxiliary/__all/MongoEngine-0.10.6/mongoengine/queryset/visitor.py", line 78, in visit_query 
14:20:20 ERROR nas.data.sync        return transform.query(self.document, **query.query) 
14:20:20 ERROR nas.data.sync        File "E:/alpha_maya_pyside/auxiliary/__all/MongoEngine-0.10.6/mongoengine/queryset/transform.py", line 61, in query 
14:20:20 ERROR nas.data.sync        raise InvalidQueryError(e) 
14:20:20 ERROR nas.data.sync       InvalidQueryError: 'DictField' object has no attribute 'document_type' 
14:20:20 ERROR nas.data.sync       sync failed 

讓類繼承自Document而不是DynamicDocument確實解決了這個問題。我一直在深入研究MongoEngine的源代碼,但沒有真正跳出來對我。我唯一的猜測是,MongoEngine可能無法將DynamicField轉換爲DictField。

我增加了一個print語句MongoEngine-0.10.6/mongoengine /查詢集/ transform.py:

try: 
    fields = _doc_cls._lookup_field(parts) 
    print fields 
except Exception, e: 
    raise InvalidQueryError(e) 

我只是在錯誤消息之前得到這個輸出:

[<mongoengine.fields.DynamicField object at 0x00000000048B48D0>] 
+0

有趣。如果您查看mongoengine/fields.py,您將看到以下字段具有document_type屬性:EmbeddedDocumentField,EmbeddedDocumentListField,CachedReferenceField。 –

+0

函數_lookup_field實際上是BaseDocument(mongoengine/base/document.py)的一部分。它將您的模式(這是一個類)以及您用來執行查詢的任何屬性,並返回包含該字段及其父項的列表(根據註釋)。我認爲在這個功能中有些東西正在崩潰。 –

回答

1

我走進mongoengine/base/document.py並檢出__lookup_field函數。在那個函數內部是一個相當長的if/else語句。這是一段摘錄。

  if field is None: 
       # Look up first field from the document 
       if field_name == 'pk': 
        # Deal with "primary key" alias 
        field_name = cls._meta['id_field'] 
       if field_name in cls._fields: 
        field = cls._fields[field_name] 
       elif cls._dynamic: 
        field = DynamicField(db_field=field_name) 
       elif cls._meta.get("allow_inheritance", False) or cls._meta.get("abstract", False): 
        # 744: in case the field is defined in a subclass 
        for subcls in cls.__subclasses__(): 
         try: 
          field = subcls._lookup_field([field_name])[0] 
         except LookUpError: 
          continue 

         if field is not None: 
          break 
        else: 
         raise LookUpError('Cannot resolve field "%s"' % field_name) 
       else: 
        raise LookUpError('Cannot resolve field "%s"' 
             % field_name) 
      else: 
       ReferenceField = _import_class('ReferenceField') 
       GenericReferenceField = _import_class('GenericReferenceField') 
       if isinstance(field, (ReferenceField, GenericReferenceField)): 
        raise LookUpError('Cannot perform join in mongoDB: %s' % 
             '__'.join(parts)) 
       if hasattr(getattr(field, 'field', None), 'lookup_member'): 
        new_field = field.field.lookup_member(field_name) 
       elif cls._dynamic and (isinstance(field, DynamicField) or 
             getattr(getattr(field, 'document_type'), '_dynamic')): 
        new_field = DynamicField(db_field=field_name) 

如果你運行一個查詢和使用,也就是說,一個StringField,則變量field仍然None。但是,如果使用DictField運行查詢,則field會被設置爲<mongoengine.fields.DictField object at 0x0000000004506CF8>之類的內容。我不知道如何field設置爲該值。無論如何,如果您正在查詢的類恰好是DynamicDocument,那麼您將被髮送到循環的這一部分。

elif cls._dynamic and (isinstance(field, DynamicField) or 
             getattr(getattr(field, 'document_type'), '_dynamic')): 
        new_field = DynamicField(db_field=field_name) 

我通過排除過程得出了這個結論。基本上,我找出了我不去的地方。正如你所看到的,由於DictField不是一個DynamicField,它將被查詢爲document_type屬性。 DictField沒有導致InvalidQueryError的那個屬性。

看起來現在的解決方案是不使用DictFields和DynamicDocuments,或者看看是否可以使用EmbeddedDocumentField,EmbeddedDocumentListField或ReferenceField與DynamicDocument。也許這是開發者的意圖?