|
2 | 2 | import unittest |
3 | 3 |
|
4 | 4 | from bson import DBRef, ObjectId |
| 5 | +from collections import OrderedDict |
5 | 6 |
|
6 | 7 | from mongoengine import * |
7 | 8 | from mongoengine.connection import get_db |
8 | 9 | from mongoengine.context_managers import query_counter |
| 10 | +from mongoengine.python_support import IS_PYMONGO_3 |
| 11 | +from mongoengine.base import TopLevelDocumentMetaclass |
| 12 | +if IS_PYMONGO_3: |
| 13 | + from bson import CodecOptions |
9 | 14 |
|
10 | 15 |
|
11 | 16 | class FieldTest(unittest.TestCase): |
@@ -1287,5 +1292,70 @@ class Playlist(Document): |
1287 | 1292 |
|
1288 | 1293 | self.assertEqual(q, 2) |
1289 | 1294 |
|
| 1295 | + def test_dynamic_field_dereference(self): |
| 1296 | + class Merchandise(Document): |
| 1297 | + name = StringField() |
| 1298 | + price = IntField() |
| 1299 | + |
| 1300 | + class Store(Document): |
| 1301 | + merchandises = DynamicField() |
| 1302 | + |
| 1303 | + Merchandise.drop_collection() |
| 1304 | + Store.drop_collection() |
| 1305 | + |
| 1306 | + merchandises = { |
| 1307 | + '#1': Merchandise(name='foo', price=100).save(), |
| 1308 | + '#2': Merchandise(name='bar', price=120).save(), |
| 1309 | + '#3': Merchandise(name='baz', price=110).save(), |
| 1310 | + } |
| 1311 | + Store(merchandises=merchandises).save() |
| 1312 | + |
| 1313 | + store = Store.objects().first() |
| 1314 | + for obj in store.merchandises.values(): |
| 1315 | + self.assertFalse(isinstance(obj, Merchandise)) |
| 1316 | + |
| 1317 | + store.select_related() |
| 1318 | + for obj in store.merchandises.values(): |
| 1319 | + self.assertTrue(isinstance(obj, Merchandise)) |
| 1320 | + |
| 1321 | + def test_dynamic_field_dereference_with_ordering_guarantee_on_pymongo3(self): |
| 1322 | + # This is because 'codec_options' is supported on pymongo3 or later |
| 1323 | + if IS_PYMONGO_3: |
| 1324 | + class OrderedDocument(Document): |
| 1325 | + my_metaclass = TopLevelDocumentMetaclass |
| 1326 | + __metaclass__ = TopLevelDocumentMetaclass |
| 1327 | + |
| 1328 | + @classmethod |
| 1329 | + def _get_collection(cls): |
| 1330 | + collection = super(OrderedDocument, cls)._get_collection() |
| 1331 | + opts = CodecOptions(document_class=OrderedDict) |
| 1332 | + |
| 1333 | + return collection.with_options(codec_options=opts) |
| 1334 | + |
| 1335 | + class Merchandise(Document): |
| 1336 | + name = StringField() |
| 1337 | + price = IntField() |
| 1338 | + |
| 1339 | + class Store(OrderedDocument): |
| 1340 | + merchandises = DynamicField(container_class=OrderedDict) |
| 1341 | + |
| 1342 | + Merchandise.drop_collection() |
| 1343 | + Store.drop_collection() |
| 1344 | + |
| 1345 | + merchandises = OrderedDict() |
| 1346 | + merchandises['#1'] = Merchandise(name='foo', price=100).save() |
| 1347 | + merchandises['#2'] = Merchandise(name='bar', price=120).save() |
| 1348 | + merchandises['#3'] = Merchandise(name='baz', price=110).save() |
| 1349 | + |
| 1350 | + Store(merchandises=merchandises).save() |
| 1351 | + |
| 1352 | + store = Store.objects().first() |
| 1353 | + |
| 1354 | + store.select_related() |
| 1355 | + |
| 1356 | + # confirms that the load data order is same with the one at storing |
| 1357 | + self.assertTrue(type(store.merchandises), OrderedDict) |
| 1358 | + self.assertEqual(','.join(store.merchandises.keys()), '#1,#2,#3') |
| 1359 | + |
1290 | 1360 | if __name__ == '__main__': |
1291 | 1361 | unittest.main() |
0 commit comments