Repository: MongoEngine/mongoengine

Issue:
DynamicEmbeddedDocuments KeyError in bracket form
While working with DynamicEmbeddedDocuments, I ran into an issue where [] notation results in KeyError where dot notation does not.  e.g.

I think `__setitem__` should be 

```
Python 2.7.6 (default, Dec  3 2013, 22:01:37)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from mongoengine import *
>>>
>>> class Page(DynamicDocument):
...     title = StringField(max_length=200, required=True)
...
>>>
>>> page = Page(title='Using MongoEngine')
>>> k = 'foo'
>>> page[k] = 'bar'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "mongoengine/base/document.py", line 186, in __setitem__
    raise KeyError(name)
KeyError: 'foo'
>>>
>>> page.foo = 'bar'
```

The above is from master, I also tested in 0.8.4:

```
File "/opt/pyenv/local/lib/python2.7/site-packages/mongoengine/base/document.py", line 185, in __setitem__
```

DynamicEmbeddedDocuments KeyError in bracket form
While working with DynamicEmbeddedDocuments, I ran into an issue where [] notation results in KeyError where dot notation does not.  e.g.

I think `__setitem__` should be 

```
Python 2.7.6 (default, Dec  3 2013, 22:01:37)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from mongoengine import *
>>>
>>> class Page(DynamicDocument):
...     title = StringField(max_length=200, required=True)
...
>>>
>>> page = Page(title='Using MongoEngine')
>>> k = 'foo'
>>> page[k] = 'bar'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "mongoengine/base/document.py", line 186, in __setitem__
    raise KeyError(name)
KeyError: 'foo'
>>>
>>> page.foo = 'bar'
```

The above is from master, I also tested in 0.8.4:

```
File "/opt/pyenv/local/lib/python2.7/site-packages/mongoengine/base/document.py", line 185, in __setitem__
```



Discussion:

Resolving patch:
diff --git a/mongoengine/base/document.py b/mongoengine/base/document.py
--- a/mongoengine/base/document.py
+++ b/mongoengine/base/document.py
@@ -182,7 +182,7 @@ def __setitem__(self, name, value):
         """Dictionary-style field access, set a field's value.
         """
         # Ensure that the field exists before settings its value
-        if name not in self._fields:
+        if not self._dynamic and name not in self._fields:
             raise KeyError(name)
         return setattr(self, name, value)
 


Test patch:
diff --git a/tests/document/dynamic.py b/tests/document/dynamic.py
--- a/tests/document/dynamic.py
+++ b/tests/document/dynamic.py
@@ -292,6 +292,44 @@ class Person(DynamicDocument):
         person.save()
         self.assertEqual(Person.objects.first().age, 35)
 
+    def test_dynamic_and_embedded_dict_access(self):
+        """Ensure embedded dynamic documents work with dict[] style access"""
+
+        class Address(EmbeddedDocument):
+            city = StringField()
+
+        class Person(DynamicDocument):
+            name = StringField()
+
+        Person.drop_collection()
+
+        Person(name="Ross", address=Address(city="London")).save()
+
+        person = Person.objects.first()
+        person.attrval = "This works"
+
+        person["phone"] = "555-1212" # but this should too
+
+        # Same thing two levels deep
+        person["address"]["city"] = "Lundenne"
+        person.save()
+
+        self.assertEqual(Person.objects.first().address.city, "Lundenne")
+
+        self.assertEqual(Person.objects.first().phone, "555-1212")
+
+        person = Person.objects.first()
+        person.address = Address(city="Londinium")
+        person.save()
+
+        self.assertEqual(Person.objects.first().address.city, "Londinium")
+
+
+        person = Person.objects.first()
+        person["age"] = 35
+        person.save()
+        self.assertEqual(Person.objects.first().age, 35)
+
 
 if __name__ == '__main__':
     unittest.main()