440 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			440 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import unittest
 | |
| 
 | |
| from mongoengine import *
 | |
| from tests.utils import MongoDBTestCase
 | |
| 
 | |
| __all__ = ("TestDynamicDocument", )
 | |
| 
 | |
| 
 | |
| class TestDynamicDocument(MongoDBTestCase):
 | |
| 
 | |
|     def setUp(self):
 | |
|         super(TestDynamicDocument, self).setUp()
 | |
| 
 | |
|         class Person(DynamicDocument):
 | |
|             name = StringField()
 | |
|             meta = {'allow_inheritance': True}
 | |
| 
 | |
|         Person.drop_collection()
 | |
| 
 | |
|         self.Person = Person
 | |
| 
 | |
|     def test_simple_dynamic_document(self):
 | |
|         """Ensures simple dynamic documents are saved correctly"""
 | |
| 
 | |
|         p = self.Person()
 | |
|         p.name = "James"
 | |
|         p.age = 34
 | |
| 
 | |
|         self.assertEqual(p.to_mongo(), {"_cls": "Person", "name": "James",
 | |
|                                         "age": 34})
 | |
|         self.assertEqual(p.to_mongo().keys(), ["_cls", "name", "age"])
 | |
|         p.save()
 | |
|         self.assertEqual(p.to_mongo().keys(), ["_id", "_cls", "name", "age"])
 | |
| 
 | |
|         self.assertEqual(self.Person.objects.first().age, 34)
 | |
| 
 | |
|         # Confirm no changes to self.Person
 | |
|         self.assertFalse(hasattr(self.Person, 'age'))
 | |
| 
 | |
|     def test_change_scope_of_variable(self):
 | |
|         """Test changing the scope of a dynamic field has no adverse effects"""
 | |
|         p = self.Person()
 | |
|         p.name = "Dean"
 | |
|         p.misc = 22
 | |
|         p.save()
 | |
| 
 | |
|         p = self.Person.objects.get()
 | |
|         p.misc = {'hello': 'world'}
 | |
|         p.save()
 | |
| 
 | |
|         p = self.Person.objects.get()
 | |
|         self.assertEqual(p.misc, {'hello': 'world'})
 | |
| 
 | |
|     def test_delete_dynamic_field(self):
 | |
|         """Test deleting a dynamic field works"""
 | |
|         self.Person.drop_collection()
 | |
|         p = self.Person()
 | |
|         p.name = "Dean"
 | |
|         p.misc = 22
 | |
|         p.save()
 | |
| 
 | |
|         p = self.Person.objects.get()
 | |
|         p.misc = {'hello': 'world'}
 | |
|         p.save()
 | |
| 
 | |
|         p = self.Person.objects.get()
 | |
|         self.assertEqual(p.misc, {'hello': 'world'})
 | |
|         collection = self.db[self.Person._get_collection_name()]
 | |
|         obj = collection.find_one()
 | |
|         self.assertEqual(sorted(obj.keys()), ['_cls', '_id', 'misc', 'name'])
 | |
| 
 | |
|         del p.misc
 | |
|         p.save()
 | |
| 
 | |
|         p = self.Person.objects.get()
 | |
|         self.assertFalse(hasattr(p, 'misc'))
 | |
| 
 | |
|         obj = collection.find_one()
 | |
|         self.assertEqual(sorted(obj.keys()), ['_cls', '_id', 'name'])
 | |
| 
 | |
|     def test_reload_after_unsetting(self):
 | |
|         p = self.Person()
 | |
|         p.misc = 22
 | |
|         p.save()
 | |
|         p.update(unset__misc=1)
 | |
|         p.reload()
 | |
| 
 | |
|     def test_reload_dynamic_field(self):
 | |
|         self.Person.objects.delete()
 | |
|         p = self.Person.objects.create()
 | |
|         p.update(age=1)
 | |
| 
 | |
|         self.assertEqual(len(p._data), 3)
 | |
|         self.assertEqual(sorted(p._data.keys()), ['_cls', 'id', 'name'])
 | |
| 
 | |
|         p.reload()
 | |
|         self.assertEqual(len(p._data), 4)
 | |
|         self.assertEqual(sorted(p._data.keys()), ['_cls', 'age', 'id', 'name'])
 | |
| 
 | |
|     def test_fields_without_underscore(self):
 | |
|         """Ensure we can query dynamic fields"""
 | |
|         Person = self.Person
 | |
| 
 | |
|         p = self.Person(name='Dean')
 | |
|         p.save()
 | |
| 
 | |
|         raw_p = Person.objects.as_pymongo().get(id=p.id)
 | |
|         self.assertEqual(
 | |
|             raw_p,
 | |
|             {
 | |
|                 '_cls': u'Person',
 | |
|                 '_id': p.id,
 | |
|                 'name': u'Dean'
 | |
|              }
 | |
|         )
 | |
| 
 | |
|         p.name = 'OldDean'
 | |
|         p.newattr = 'garbage'
 | |
|         p.save()
 | |
|         raw_p = Person.objects.as_pymongo().get(id=p.id)
 | |
|         self.assertEqual(
 | |
|             raw_p,
 | |
|             {
 | |
|                 '_cls': u'Person',
 | |
|                 '_id': p.id,
 | |
|                 'name': 'OldDean',
 | |
|                 'newattr': u'garbage'
 | |
|             }
 | |
|         )
 | |
| 
 | |
|     def test_fields_containing_underscore(self):
 | |
|         """Ensure we can query dynamic fields"""
 | |
|         class WeirdPerson(DynamicDocument):
 | |
|             name = StringField()
 | |
|             _name = StringField()
 | |
| 
 | |
|         WeirdPerson.drop_collection()
 | |
| 
 | |
|         p = WeirdPerson(name='Dean', _name='Dean')
 | |
|         p.save()
 | |
| 
 | |
|         raw_p = WeirdPerson.objects.as_pymongo().get(id=p.id)
 | |
|         self.assertEqual(
 | |
|             raw_p,
 | |
|             {
 | |
|                 '_id': p.id,
 | |
|                 '_name': u'Dean',
 | |
|                 'name': u'Dean'
 | |
|             }
 | |
|         )
 | |
| 
 | |
|         p.name = 'OldDean'
 | |
|         p._name = 'NewDean'
 | |
|         p._newattr1 = 'garbage'    # Unknown fields won't be added
 | |
|         p.save()
 | |
|         raw_p = WeirdPerson.objects.as_pymongo().get(id=p.id)
 | |
|         self.assertEqual(
 | |
|             raw_p,
 | |
|             {
 | |
|                 '_id': p.id,
 | |
|                 '_name': u'NewDean',
 | |
|                 'name': u'OldDean',
 | |
|             }
 | |
|         )
 | |
| 
 | |
|     def test_dynamic_document_queries(self):
 | |
|         """Ensure we can query dynamic fields"""
 | |
|         p = self.Person()
 | |
|         p.name = "Dean"
 | |
|         p.age = 22
 | |
|         p.save()
 | |
| 
 | |
|         self.assertEqual(1, self.Person.objects(age=22).count())
 | |
|         p = self.Person.objects(age=22)
 | |
|         p = p.get()
 | |
|         self.assertEqual(22, p.age)
 | |
| 
 | |
|     def test_complex_dynamic_document_queries(self):
 | |
|         class Person(DynamicDocument):
 | |
|             name = StringField()
 | |
| 
 | |
|         Person.drop_collection()
 | |
| 
 | |
|         p = Person(name="test")
 | |
|         p.age = "ten"
 | |
|         p.save()
 | |
| 
 | |
|         p1 = Person(name="test1")
 | |
|         p1.age = "less then ten and a half"
 | |
|         p1.save()
 | |
| 
 | |
|         p2 = Person(name="test2")
 | |
|         p2.age = 10
 | |
|         p2.save()
 | |
| 
 | |
|         self.assertEqual(Person.objects(age__icontains='ten').count(), 2)
 | |
|         self.assertEqual(Person.objects(age__gte=10).count(), 1)
 | |
| 
 | |
|     def test_complex_data_lookups(self):
 | |
|         """Ensure you can query dynamic document dynamic fields"""
 | |
|         p = self.Person()
 | |
|         p.misc = {'hello': 'world'}
 | |
|         p.save()
 | |
| 
 | |
|         self.assertEqual(1, self.Person.objects(misc__hello='world').count())
 | |
| 
 | |
|     def test_three_level_complex_data_lookups(self):
 | |
|         """Ensure you can query three level document dynamic fields"""
 | |
|         p = self.Person.objects.create(
 | |
|             misc={'hello': {'hello2': 'world'}}
 | |
|         )
 | |
|         self.assertEqual(1, self.Person.objects(misc__hello__hello2='world').count())
 | |
| 
 | |
|     def test_complex_embedded_document_validation(self):
 | |
|         """Ensure embedded dynamic documents may be validated"""
 | |
|         class Embedded(DynamicEmbeddedDocument):
 | |
|             content = URLField()
 | |
| 
 | |
|         class Doc(DynamicDocument):
 | |
|             pass
 | |
| 
 | |
|         Doc.drop_collection()
 | |
|         doc = Doc()
 | |
| 
 | |
|         embedded_doc_1 = Embedded(content='http://mongoengine.org')
 | |
|         embedded_doc_1.validate()
 | |
| 
 | |
|         embedded_doc_2 = Embedded(content='this is not a url')
 | |
|         self.assertRaises(ValidationError, embedded_doc_2.validate)
 | |
| 
 | |
|         doc.embedded_field_1 = embedded_doc_1
 | |
|         doc.embedded_field_2 = embedded_doc_2
 | |
|         self.assertRaises(ValidationError, doc.validate)
 | |
| 
 | |
|     def test_inheritance(self):
 | |
|         """Ensure that dynamic document plays nice with inheritance"""
 | |
|         class Employee(self.Person):
 | |
|             salary = IntField()
 | |
| 
 | |
|         Employee.drop_collection()
 | |
| 
 | |
|         self.assertIn('name', Employee._fields)
 | |
|         self.assertIn('salary', Employee._fields)
 | |
|         self.assertEqual(Employee._get_collection_name(),
 | |
|                          self.Person._get_collection_name())
 | |
| 
 | |
|         joe_bloggs = Employee()
 | |
|         joe_bloggs.name = "Joe Bloggs"
 | |
|         joe_bloggs.salary = 10
 | |
|         joe_bloggs.age = 20
 | |
|         joe_bloggs.save()
 | |
| 
 | |
|         self.assertEqual(1, self.Person.objects(age=20).count())
 | |
|         self.assertEqual(1, Employee.objects(age=20).count())
 | |
| 
 | |
|         joe_bloggs = self.Person.objects.first()
 | |
|         self.assertIsInstance(joe_bloggs, Employee)
 | |
| 
 | |
|     def test_embedded_dynamic_document(self):
 | |
|         """Test dynamic embedded documents"""
 | |
|         class Embedded(DynamicEmbeddedDocument):
 | |
|             pass
 | |
| 
 | |
|         class Doc(DynamicDocument):
 | |
|             pass
 | |
| 
 | |
|         Doc.drop_collection()
 | |
|         doc = Doc()
 | |
| 
 | |
|         embedded_1 = Embedded()
 | |
|         embedded_1.string_field = 'hello'
 | |
|         embedded_1.int_field = 1
 | |
|         embedded_1.dict_field = {'hello': 'world'}
 | |
|         embedded_1.list_field = ['1', 2, {'hello': 'world'}]
 | |
|         doc.embedded_field = embedded_1
 | |
| 
 | |
|         self.assertEqual(doc.to_mongo(), {
 | |
|             "embedded_field": {
 | |
|                 "_cls": "Embedded",
 | |
|                 "string_field": "hello",
 | |
|                 "int_field": 1,
 | |
|                 "dict_field": {"hello": "world"},
 | |
|                 "list_field": ['1', 2, {'hello': 'world'}]
 | |
|             }
 | |
|         })
 | |
|         doc.save()
 | |
| 
 | |
|         doc = Doc.objects.first()
 | |
|         self.assertEqual(doc.embedded_field.__class__, Embedded)
 | |
|         self.assertEqual(doc.embedded_field.string_field, "hello")
 | |
|         self.assertEqual(doc.embedded_field.int_field, 1)
 | |
|         self.assertEqual(doc.embedded_field.dict_field, {'hello': 'world'})
 | |
|         self.assertEqual(doc.embedded_field.list_field,
 | |
|                             ['1', 2, {'hello': 'world'}])
 | |
| 
 | |
|     def test_complex_embedded_documents(self):
 | |
|         """Test complex dynamic embedded documents setups"""
 | |
|         class Embedded(DynamicEmbeddedDocument):
 | |
|             pass
 | |
| 
 | |
|         class Doc(DynamicDocument):
 | |
|             pass
 | |
| 
 | |
|         Doc.drop_collection()
 | |
|         doc = Doc()
 | |
| 
 | |
|         embedded_1 = Embedded()
 | |
|         embedded_1.string_field = 'hello'
 | |
|         embedded_1.int_field = 1
 | |
|         embedded_1.dict_field = {'hello': 'world'}
 | |
| 
 | |
|         embedded_2 = Embedded()
 | |
|         embedded_2.string_field = 'hello'
 | |
|         embedded_2.int_field = 1
 | |
|         embedded_2.dict_field = {'hello': 'world'}
 | |
|         embedded_2.list_field = ['1', 2, {'hello': 'world'}]
 | |
| 
 | |
|         embedded_1.list_field = ['1', 2, embedded_2]
 | |
|         doc.embedded_field = embedded_1
 | |
| 
 | |
|         self.assertEqual(doc.to_mongo(), {
 | |
|             "embedded_field": {
 | |
|                 "_cls": "Embedded",
 | |
|                 "string_field": "hello",
 | |
|                 "int_field": 1,
 | |
|                 "dict_field": {"hello": "world"},
 | |
|                 "list_field": ['1', 2,
 | |
|                     {"_cls": "Embedded",
 | |
|                     "string_field": "hello",
 | |
|                     "int_field": 1,
 | |
|                     "dict_field": {"hello": "world"},
 | |
|                     "list_field": ['1', 2, {'hello': 'world'}]}
 | |
|                 ]
 | |
|             }
 | |
|         })
 | |
|         doc.save()
 | |
|         doc = Doc.objects.first()
 | |
|         self.assertEqual(doc.embedded_field.__class__, Embedded)
 | |
|         self.assertEqual(doc.embedded_field.string_field, "hello")
 | |
|         self.assertEqual(doc.embedded_field.int_field, 1)
 | |
|         self.assertEqual(doc.embedded_field.dict_field, {'hello': 'world'})
 | |
|         self.assertEqual(doc.embedded_field.list_field[0], '1')
 | |
|         self.assertEqual(doc.embedded_field.list_field[1], 2)
 | |
| 
 | |
|         embedded_field = doc.embedded_field.list_field[2]
 | |
| 
 | |
|         self.assertEqual(embedded_field.__class__, Embedded)
 | |
|         self.assertEqual(embedded_field.string_field, "hello")
 | |
|         self.assertEqual(embedded_field.int_field, 1)
 | |
|         self.assertEqual(embedded_field.dict_field, {'hello': 'world'})
 | |
|         self.assertEqual(embedded_field.list_field, ['1', 2,
 | |
|                                                         {'hello': 'world'}])
 | |
| 
 | |
|     def test_dynamic_and_embedded(self):
 | |
|         """Ensure embedded documents play nicely"""
 | |
| 
 | |
|         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.address.city = "Lundenne"
 | |
|         person.save()
 | |
| 
 | |
|         self.assertEqual(Person.objects.first().address.city, "Lundenne")
 | |
| 
 | |
|         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)
 | |
| 
 | |
|     def test_dynamic_embedded_works_with_only(self):
 | |
|         """Ensure custom fieldnames on a dynamic embedded document are found by qs.only()"""
 | |
| 
 | |
|         class Address(DynamicEmbeddedDocument):
 | |
|             city = StringField()
 | |
| 
 | |
|         class Person(DynamicDocument):
 | |
|             address = EmbeddedDocumentField(Address)
 | |
| 
 | |
|         Person.drop_collection()
 | |
| 
 | |
|         Person(name="Eric", address=Address(city="San Francisco", street_number="1337")).save()
 | |
| 
 | |
|         self.assertEqual(Person.objects.first().address.street_number, '1337')
 | |
|         self.assertEqual(Person.objects.only('address__street_number').first().address.street_number, '1337')
 | |
| 
 | |
|     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()
 |