427 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			427 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import unittest
 | |
| 
 | |
| import pytest
 | |
| 
 | |
| 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
 | |
| 
 | |
|         assert p.to_mongo() == {"_cls": "Person", "name": "James", "age": 34}
 | |
|         assert p.to_mongo().keys() == ["_cls", "name", "age"]
 | |
|         p.save()
 | |
|         assert p.to_mongo().keys() == ["_id", "_cls", "name", "age"]
 | |
| 
 | |
|         assert self.Person.objects.first().age == 34
 | |
| 
 | |
|         # Confirm no changes to self.Person
 | |
|         assert not 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()
 | |
|         assert 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()
 | |
|         assert p.misc == {"hello": "world"}
 | |
|         collection = self.db[self.Person._get_collection_name()]
 | |
|         obj = collection.find_one()
 | |
|         assert sorted(obj.keys()) == ["_cls", "_id", "misc", "name"]
 | |
| 
 | |
|         del p.misc
 | |
|         p.save()
 | |
| 
 | |
|         p = self.Person.objects.get()
 | |
|         assert not hasattr(p, "misc")
 | |
| 
 | |
|         obj = collection.find_one()
 | |
|         assert 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)
 | |
| 
 | |
|         assert len(p._data) == 3
 | |
|         assert sorted(p._data.keys()) == ["_cls", "id", "name"]
 | |
| 
 | |
|         p.reload()
 | |
|         assert len(p._data) == 4
 | |
|         assert 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)
 | |
|         assert 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)
 | |
|         assert 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)
 | |
|         assert 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)
 | |
|         assert 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()
 | |
| 
 | |
|         assert 1 == self.Person.objects(age=22).count()
 | |
|         p = self.Person.objects(age=22)
 | |
|         p = p.get()
 | |
|         assert 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()
 | |
| 
 | |
|         assert Person.objects(age__icontains="ten").count() == 2
 | |
|         assert 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()
 | |
| 
 | |
|         assert 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"""
 | |
|         self.Person.objects.create(misc={"hello": {"hello2": "world"}})
 | |
|         assert 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")
 | |
|         with pytest.raises(ValidationError):
 | |
|             embedded_doc_2.validate()
 | |
| 
 | |
|         doc.embedded_field_1 = embedded_doc_1
 | |
|         doc.embedded_field_2 = embedded_doc_2
 | |
|         with pytest.raises(ValidationError):
 | |
|             doc.validate()
 | |
| 
 | |
|     def test_inheritance(self):
 | |
|         """Ensure that dynamic document plays nice with inheritance"""
 | |
| 
 | |
|         class Employee(self.Person):
 | |
|             salary = IntField()
 | |
| 
 | |
|         Employee.drop_collection()
 | |
| 
 | |
|         assert "name" in Employee._fields
 | |
|         assert "salary" in Employee._fields
 | |
|         assert 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()
 | |
| 
 | |
|         assert 1 == self.Person.objects(age=20).count()
 | |
|         assert 1 == Employee.objects(age=20).count()
 | |
| 
 | |
|         joe_bloggs = self.Person.objects.first()
 | |
|         assert isinstance(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
 | |
| 
 | |
|         assert 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()
 | |
|         assert doc.embedded_field.__class__ == Embedded
 | |
|         assert doc.embedded_field.string_field == "hello"
 | |
|         assert doc.embedded_field.int_field == 1
 | |
|         assert doc.embedded_field.dict_field == {"hello": "world"}
 | |
|         assert 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
 | |
| 
 | |
|         assert 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()
 | |
|         assert doc.embedded_field.__class__ == Embedded
 | |
|         assert doc.embedded_field.string_field == "hello"
 | |
|         assert doc.embedded_field.int_field == 1
 | |
|         assert doc.embedded_field.dict_field == {"hello": "world"}
 | |
|         assert doc.embedded_field.list_field[0] == "1"
 | |
|         assert doc.embedded_field.list_field[1] == 2
 | |
| 
 | |
|         embedded_field = doc.embedded_field.list_field[2]
 | |
| 
 | |
|         assert embedded_field.__class__ == Embedded
 | |
|         assert embedded_field.string_field == "hello"
 | |
|         assert embedded_field.int_field == 1
 | |
|         assert embedded_field.dict_field == {"hello": "world"}
 | |
|         assert 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()
 | |
| 
 | |
|         assert Person.objects.first().address.city == "Lundenne"
 | |
| 
 | |
|         person = Person.objects.first()
 | |
|         person.address = Address(city="Londinium")
 | |
|         person.save()
 | |
| 
 | |
|         assert Person.objects.first().address.city == "Londinium"
 | |
| 
 | |
|         person = Person.objects.first()
 | |
|         person.age = 35
 | |
|         person.save()
 | |
|         assert 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()
 | |
| 
 | |
|         assert Person.objects.first().address.street_number == "1337"
 | |
|         assert (
 | |
|             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()
 | |
| 
 | |
|         assert Person.objects.first().address.city == "Lundenne"
 | |
| 
 | |
|         assert Person.objects.first().phone == "555-1212"
 | |
| 
 | |
|         person = Person.objects.first()
 | |
|         person.address = Address(city="Londinium")
 | |
|         person.save()
 | |
| 
 | |
|         assert Person.objects.first().address.city == "Londinium"
 | |
| 
 | |
|         person = Person.objects.first()
 | |
|         person["age"] = 35
 | |
|         person.save()
 | |
|         assert Person.objects.first().age == 35
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     unittest.main()
 |