hmarr/mongoengine — pull request #466 by aparrish (2012-03-21 18:13:32+00:00)
Title: Fix for prepare_query_value in ListField

The `prepare_query_value` method in the `ListField` class checks to see if the value has an `__iter__` attribute, then uses a list comprehension to make a list from that value. This is broken—embedded documents have an `__iter__` attribute (for iterating over keys), but should NOT be turned into lists when updating values in Mongo. This patch prevents this behavior for anything that is an instance of `BaseDocument`.

Here's a test script so you can see the bug and how the patch fixes it:

```python

from mongoengine import *

class Author(EmbeddedDocument):
name = StringField()
age = IntField()

class Message(Document):
title = StringField()
authors = ListField(EmbeddedDocumentField(Author))

if __name__ == '__main__':
connect('testy')

message = Message(title="hello", authors=[Author(name="Bob", age=12)])
message.save()

Message.objects(authors__name="Bob").update_one(
set__authors__S=Author(name="Horace", age="13"))

message.reload()

print message.title
print message.authors[0].name
```

Without the patch, you get a nasty error (because what should be an embedded document in the `Message` object was saved as a list):

```
Traceback (most recent call last):
File "testy2.py", line 20, in
message.reload()
File "/home/aparrish/mongoengine/mongoengine/document.py", line 298, in reload
**{id_field: self[id_field]}
File "/home/aparrish/mongoengine/mongoengine/queryset.py", line 822, in first
result = self[0]
File "/home/aparrish/mongoengine/mongoengine/queryset.py", line 1095, in __getitem__
return self._document._from_son(self._cursor[key])
File "/home/aparrish/mongoengine/mongoengine/base.py", line 961, in _from_son
else field.to_python(value))
File "/home/aparrish/mongoengine/mongoengine/base.py", line 291, in to_python
value_dict = dict([(key, self.field.to_python(item)) for key, item in value.items()])
File "/home/aparrish/mongoengine/mongoengine/fields.py", line 414, in to_python
return self.document_type._from_son(value)
File "/home/aparrish/mongoengine/mongoengine/base.py", line 943, in _from_son
class_name = son.get(u'_cls', cls._class_name)
AttributeError: 'list' object has no attribute 'get'
```

With the patch, we get the expected results:

```
hello
Horace
```