This commit: 1. Formats all of our existing code using `black`. 2. Adds a note about using `black` to `CONTRIBUTING.rst`. 3. Runs `black --check` as part of CI (failing builds that aren't properly formatted).
429 lines
13 KiB
Python
429 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()
|