From 32d5c0c946f68a9328e23c688ad37b4472033e1c Mon Sep 17 00:00:00 2001 From: Alice Bevan-McGregor Date: Wed, 3 Apr 2013 15:00:34 -0400 Subject: [PATCH 1/3] Store ordered list of field names, and return the ordered list when iterating a document instance. --- mongoengine/base.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mongoengine/base.py b/mongoengine/base.py index f73af4cc..2f956ccf 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -560,8 +560,11 @@ class DocumentMetaclass(type): # Set _fields and db_field maps attrs['_fields'] = doc_fields - attrs['_db_field_map'] = dict([(k, getattr(v, 'db_field', k)) - for k, v in doc_fields.iteritems()]) + attrs['_fields_ordered'] = tuple(i[1] + for i in sorted((v.creation_counter, v.name) + for v in doc_fields.itervalues())) + attrs['_db_field_map'] = dict((k, getattr(v, 'db_field', k)) + for k, v in doc_fields.iteritems()) attrs['_reverse_db_field_map'] = dict( (v, k) for k, v in attrs['_db_field_map'].iteritems()) @@ -1302,7 +1305,10 @@ class BaseDocument(object): return value def __iter__(self): - return iter(self._fields) + if 'id' in self._fields and 'id' not in self._fields_ordered: + return iter(('id', ) + self._fields_ordered) + + return iter(self._fields_ordered) def __getitem__(self, name): """Dictionary-style field access, return a field's value if present. From fc1ce6d39bd5dac86111276eae26de407a194ce6 Mon Sep 17 00:00:00 2001 From: Alice Bevan-McGregor Date: Wed, 3 Apr 2013 15:00:51 -0400 Subject: [PATCH 2/3] Allow construction of document instances using positional arguments. --- mongoengine/base.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mongoengine/base.py b/mongoengine/base.py index 2f956ccf..36d7c295 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -907,7 +907,17 @@ class BaseDocument(object): _dynamic_lock = True _initialised = False - def __init__(self, **values): + def __init__(self, *args, **values): + if args: + # Combine positional arguments with named arguments. + # We only want named arguments. + field = iter(self._fields_ordered) + for value in args: + name = next(field) + if name in values: + raise TypeError("Multiple values for keyword argument '" + name + "'") + values[name] = value + signals.pre_init.send(self.__class__, document=self, values=values) self._data = {} From 07d3e52e6a461eeca1251911e5440486e0832c2a Mon Sep 17 00:00:00 2001 From: Alice Bevan-McGregor Date: Wed, 3 Apr 2013 15:03:33 -0400 Subject: [PATCH 3/3] Tests for construction using positional parameters. --- tests/test_document.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_document.py b/tests/test_document.py index 3e8d8134..00059fa0 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -1386,6 +1386,28 @@ class DocumentTest(unittest.TestCase): person = self.Person(name="Test User", age=30) self.assertEqual(person.name, "Test User") self.assertEqual(person.age, 30) + + def test_positional_creation(self): + """Ensure that document may be created using positional arguments. + """ + person = self.Person("Test User", 42) + self.assertEqual(person.name, "Test User") + self.assertEqual(person.age, 42) + + def test_mixed_creation(self): + """Ensure that document may be created using mixed arguments. + """ + person = self.Person("Test User", age=42) + self.assertEqual(person.name, "Test User") + self.assertEqual(person.age, 42) + + def test_bad_mixed_creation(self): + """Ensure that document gives correct error when duplicating arguments + """ + def construct_bad_instance(): + return self.Person("Test User", 42, name="Bad User") + + self.assertRaises(TypeError, construct_bad_instance) def test_to_dbref(self): """Ensure that you can get a dbref of a document"""