Mongoengine: mixin Field dereference fail.

Created on 29 Dec 2016  路  3Comments  路  Source: MongoEngine/mongoengine

hi,
I got some trouble in using mixin fields, here is the sample code:

>>> from mongoengine import Document
>>> from mongoengine import MapField, ListField, ReferenceField
>>> class R(Document):
...   pass
...
>>> class T(Document):
...   rs = MapField(field=ListField(ReferenceField(R)))
...
>>> T.objects
[]
>>> t = T()
>>> t.rs
{}
>>> t.rs['rs1'] = [R()]
>>> t.rs
{'rs1': [<R: R object>]}
>>> t.rs['rs1'][0].save()
<R: R object>
>>> t.save()
<T: T object>
>>> t
<T: T object>
>>> T.objects
[<T: T object>]
>>> T.objects[0].rs
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\tmp\venv\lib\site-packages\mongoengine\base\fields.py", line 279, in __get__
    value, max_depth=1, instance=instance, name=self.name
  File "C:\tmp\venv\lib\site-packages\mongoengine\dereference.py", line 76, in __call__
    self.reference_map = self._find_references(items)
  File "C:\tmp\venv\lib\site-packages\mongoengine\dereference.py", line 115, in _find_references
    reference_map.setdefault(item.collection, set()).add(item.id)
TypeError: unhashable type: 'list'

I want to use MapField to mapping key=String, value=List[Ref(Document)], save into mongodb is work, but can't retrieve it.

I tried to trace to figure out something, but I'm failed too. :joy_cat:

Most helpful comment

Until this is resolved, here is a temporary work-around:

class R(Document):
    pass

class TRMap(EmbeddedDocument):
    rl = ListField(ReferenceField(R))

class T(Document):
    rs = MapField(field=EmbeddedDocumentField(TRMap))

t = T()
m = TRMap(rl=[R()])
t.rs['rs1'] = m
t.rs['rs1'].rl[0].save()
t.save()
print T.objects[0].rs['rs1'].rl[0]

All 3 comments

I've confirmed this, but have not yet hunted down the reason. Additional notes:

The problem is also with parent DictField; so this is not a MapField problem per se.
The problem is specifically with a ListField containing ReferenceField. So,

class T(Document):
    rs = MapField(field=ListField(StringField()))

works fine. And

class T(Document):
    rs = MapField(field=ReferenceField(R))

Also works fine.

Until this is resolved, here is a temporary work-around:

class R(Document):
    pass

class TRMap(EmbeddedDocument):
    rl = ListField(ReferenceField(R))

class T(Document):
    rs = MapField(field=EmbeddedDocumentField(TRMap))

t = T()
m = TRMap(rl=[R()])
t.rs['rs1'] = m
t.rs['rs1'].rl[0].save()
t.save()
print T.objects[0].rs['rs1'].rl[0]

Just pushed a PR to fix this, note that the issue was also occuring with DictField and wasn't present if you use ReferenceField(dbref=True). See below:

class Doc(me.Document):
    s = me.StringField()

class Simple(me.Document):
    mapping1 = me.DictField(field=me.ListField(me.ReferenceField(Doc, dbref=True)))
    mapping2 = me.DictField(field=me.ListField(me.ReferenceField(Doc, dbref=False)))

doc = Doc(s='aa').save()
Simple(mapping1={'test': [doc]}, mapping2={'test': [doc]}).save()

s = Simple.objects().first()
s.mapping1    # {'test': [<Doc: Doc object>]}
s.mapping2    # raises TypeError: unhashable type: 'list'
Was this page helpful?
0 / 5 - 0 ratings