Merge branch 'DavidBord-fix-734'
This commit is contained in:
		| @@ -8,6 +8,7 @@ Changes in 0.9.X - DEV | |||||||
| - OperationError: Shard Keys are immutable. Tried to update id even though the document is not yet saved #771 | - OperationError: Shard Keys are immutable. Tried to update id even though the document is not yet saved #771 | ||||||
| - with_limit_and_skip for count should default like in pymongo #759 | - with_limit_and_skip for count should default like in pymongo #759 | ||||||
| - Fix storing value of precision attribute in DecimalField #787 | - Fix storing value of precision attribute in DecimalField #787 | ||||||
|  | - Set attribute to None does not work (at least for fields with default values) #734 | ||||||
| - Querying by a field defined in a subclass raises InvalidQueryError #744 | - Querying by a field defined in a subclass raises InvalidQueryError #744 | ||||||
| - Add Support For MongoDB 2.6.X's maxTimeMS #778 | - Add Support For MongoDB 2.6.X's maxTimeMS #778 | ||||||
| - abstract shouldn't be inherited in EmbeddedDocument # 789 | - abstract shouldn't be inherited in EmbeddedDocument # 789 | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ class BaseField(object): | |||||||
|     def __init__(self, db_field=None, name=None, required=False, default=None, |     def __init__(self, db_field=None, name=None, required=False, default=None, | ||||||
|                  unique=False, unique_with=None, primary_key=False, |                  unique=False, unique_with=None, primary_key=False, | ||||||
|                  validation=None, choices=None, verbose_name=None, |                  validation=None, choices=None, verbose_name=None, | ||||||
|                  help_text=None): |                  help_text=None, null=False): | ||||||
|         """ |         """ | ||||||
|         :param db_field: The database field to store this field in |         :param db_field: The database field to store this field in | ||||||
|             (defaults to the name of the field) |             (defaults to the name of the field) | ||||||
| @@ -60,6 +60,8 @@ class BaseField(object): | |||||||
|             model forms from the document model. |             model forms from the document model. | ||||||
|         :param help_text: (optional) The help text for this field and is often |         :param help_text: (optional) The help text for this field and is often | ||||||
|             used when generating model forms from the document model. |             used when generating model forms from the document model. | ||||||
|  |         :param null: (optional) Is the field value can be null. If no and there is a default value | ||||||
|  |             then the default value is set | ||||||
|         """ |         """ | ||||||
|         self.db_field = (db_field or name) if not primary_key else '_id' |         self.db_field = (db_field or name) if not primary_key else '_id' | ||||||
|  |  | ||||||
| @@ -75,6 +77,7 @@ class BaseField(object): | |||||||
|         self.choices = choices |         self.choices = choices | ||||||
|         self.verbose_name = verbose_name |         self.verbose_name = verbose_name | ||||||
|         self.help_text = help_text |         self.help_text = help_text | ||||||
|  |         self.null = null | ||||||
|  |  | ||||||
|         # Adjust the appropriate creation counter, and save our local copy. |         # Adjust the appropriate creation counter, and save our local copy. | ||||||
|         if self.db_field == '_id': |         if self.db_field == '_id': | ||||||
| @@ -100,7 +103,10 @@ class BaseField(object): | |||||||
|  |  | ||||||
|         # If setting to None and theres a default |         # If setting to None and theres a default | ||||||
|         # Then set the value to the default value |         # Then set the value to the default value | ||||||
|         if value is None and self.default is not None: |         if value is None: | ||||||
|  |             if self.null: | ||||||
|  |                 value = None | ||||||
|  |             elif self.default is not None: | ||||||
|                 value = self.default |                 value = self.default | ||||||
|                 if callable(value): |                 if callable(value): | ||||||
|                     value = value() |                     value = value() | ||||||
|   | |||||||
| @@ -57,7 +57,9 @@ class InstanceTest(unittest.TestCase): | |||||||
|             self.db.drop_collection(collection) |             self.db.drop_collection(collection) | ||||||
|  |  | ||||||
|     def assertDbEqual(self, docs): |     def assertDbEqual(self, docs): | ||||||
|         self.assertEqual(list(self.Person._get_collection().find().sort("id")), sorted(docs, key=lambda doc: doc["_id"])) |         self.assertEqual( | ||||||
|  |             list(self.Person._get_collection().find().sort("id")), | ||||||
|  |             sorted(docs, key=lambda doc: doc["_id"])) | ||||||
|  |  | ||||||
|     def test_capped_collection(self): |     def test_capped_collection(self): | ||||||
|         """Ensure that capped collections work properly. |         """Ensure that capped collections work properly. | ||||||
| @@ -144,10 +146,18 @@ class InstanceTest(unittest.TestCase): | |||||||
|         """ |         """ | ||||||
|         class Animal(Document): |         class Animal(Document): | ||||||
|             meta = {'allow_inheritance': True} |             meta = {'allow_inheritance': True} | ||||||
|         class Fish(Animal): pass |  | ||||||
|         class Mammal(Animal): pass |         class Fish(Animal): | ||||||
|         class Dog(Mammal): pass |             pass | ||||||
|         class Human(Mammal): pass |  | ||||||
|  |         class Mammal(Animal): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         class Dog(Mammal): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         class Human(Mammal): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|         class Zoo(Document): |         class Zoo(Document): | ||||||
|             animals = ListField(ReferenceField(Animal)) |             animals = ListField(ReferenceField(Animal)) | ||||||
| @@ -459,7 +469,7 @@ class InstanceTest(unittest.TestCase): | |||||||
|             f.reload() |             f.reload() | ||||||
|         except Foo.DoesNotExist: |         except Foo.DoesNotExist: | ||||||
|             pass |             pass | ||||||
|         except Exception as ex: |         except Exception: | ||||||
|             self.assertFalse("Threw wrong exception") |             self.assertFalse("Threw wrong exception") | ||||||
|  |  | ||||||
|         f.save() |         f.save() | ||||||
| @@ -468,7 +478,7 @@ class InstanceTest(unittest.TestCase): | |||||||
|             f.reload() |             f.reload() | ||||||
|         except Foo.DoesNotExist: |         except Foo.DoesNotExist: | ||||||
|             pass |             pass | ||||||
|         except Exception as ex: |         except Exception: | ||||||
|             self.assertFalse("Threw wrong exception") |             self.assertFalse("Threw wrong exception") | ||||||
|  |  | ||||||
|     def test_dictionary_access(self): |     def test_dictionary_access(self): | ||||||
| @@ -503,7 +513,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|  |  | ||||||
|         self.assertEqual(Person(name="Bob", age=35).to_mongo().keys(), |         self.assertEqual(Person(name="Bob", age=35).to_mongo().keys(), | ||||||
|                          ['_cls', 'name', 'age']) |                          ['_cls', 'name', 'age']) | ||||||
|         self.assertEqual(Employee(name="Bob", age=35, salary=0).to_mongo().keys(), |         self.assertEqual( | ||||||
|  |             Employee(name="Bob", age=35, salary=0).to_mongo().keys(), | ||||||
|             ['_cls', 'name', 'age', 'salary']) |             ['_cls', 'name', 'age', 'salary']) | ||||||
|  |  | ||||||
|     def test_embedded_document_to_mongo_id(self): |     def test_embedded_document_to_mongo_id(self): | ||||||
| @@ -641,7 +652,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|  |  | ||||||
|     def test_modify_empty(self): |     def test_modify_empty(self): | ||||||
|         doc = self.Person(name="bob", age=10).save() |         doc = self.Person(name="bob", age=10).save() | ||||||
|         self.assertRaises(InvalidDocumentError, lambda: self.Person().modify(set__age=10)) |         self.assertRaises( | ||||||
|  |             InvalidDocumentError, lambda: self.Person().modify(set__age=10)) | ||||||
|         self.assertDbEqual([dict(doc.to_mongo())]) |         self.assertDbEqual([dict(doc.to_mongo())]) | ||||||
|  |  | ||||||
|     def test_modify_invalid_query(self): |     def test_modify_invalid_query(self): | ||||||
| @@ -649,8 +661,9 @@ class InstanceTest(unittest.TestCase): | |||||||
|         doc2 = self.Person(name="jim", age=20).save() |         doc2 = self.Person(name="jim", age=20).save() | ||||||
|         docs = [dict(doc1.to_mongo()), dict(doc2.to_mongo())] |         docs = [dict(doc1.to_mongo()), dict(doc2.to_mongo())] | ||||||
|  |  | ||||||
|         self.assertRaises(InvalidQueryError, lambda: |         self.assertRaises( | ||||||
|             doc1.modify(dict(id=doc2.id), set__value=20)) |             InvalidQueryError, | ||||||
|  |             lambda: doc1.modify(dict(id=doc2.id), set__value=20)) | ||||||
|  |  | ||||||
|         self.assertDbEqual(docs) |         self.assertDbEqual(docs) | ||||||
|  |  | ||||||
| @@ -674,7 +687,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|  |  | ||||||
|     def test_modify_update(self): |     def test_modify_update(self): | ||||||
|         other_doc = self.Person(name="bob", age=10).save() |         other_doc = self.Person(name="bob", age=10).save() | ||||||
|         doc = self.Person(name="jim", age=20, job=self.Job(name="10gen", years=3)).save() |         doc = self.Person( | ||||||
|  |             name="jim", age=20, job=self.Job(name="10gen", years=3)).save() | ||||||
|  |  | ||||||
|         doc_copy = doc._from_son(doc.to_mongo()) |         doc_copy = doc._from_son(doc.to_mongo()) | ||||||
|  |  | ||||||
| @@ -683,7 +697,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|         doc.job.name = "Google" |         doc.job.name = "Google" | ||||||
|         doc.job.years = 3 |         doc.job.years = 3 | ||||||
|  |  | ||||||
|         assert doc.modify(set__age=21, set__job__name="MongoDB", unset__job__years=True) |         assert doc.modify( | ||||||
|  |             set__age=21, set__job__name="MongoDB", unset__job__years=True) | ||||||
|         doc_copy.age = 21 |         doc_copy.age = 21 | ||||||
|         doc_copy.job.name = "MongoDB" |         doc_copy.job.name = "MongoDB" | ||||||
|         del doc_copy.job.years |         del doc_copy.job.years | ||||||
| @@ -1427,7 +1442,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|         self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4') |         self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4') | ||||||
|  |  | ||||||
|     def test_save_custom_pk(self): |     def test_save_custom_pk(self): | ||||||
|         """Ensure that a document may be saved with a custom _id using pk alias. |         """ | ||||||
|  |         Ensure that a document may be saved with a custom _id using pk alias. | ||||||
|         """ |         """ | ||||||
|         # Create person object and save it to the database |         # Create person object and save it to the database | ||||||
|         person = self.Person(name='Test User', age=30, |         person = self.Person(name='Test User', age=30, | ||||||
| @@ -1513,9 +1529,15 @@ class InstanceTest(unittest.TestCase): | |||||||
|         p4 = Page(comments=[Comment(user=u2, comment="Heavy Metal song")]) |         p4 = Page(comments=[Comment(user=u2, comment="Heavy Metal song")]) | ||||||
|         p4.save() |         p4.save() | ||||||
|  |  | ||||||
|         self.assertEqual([p1, p2], list(Page.objects.filter(comments__user=u1))) |         self.assertEqual( | ||||||
|         self.assertEqual([p1, p2, p4], list(Page.objects.filter(comments__user=u2))) |             [p1, p2], | ||||||
|         self.assertEqual([p1, p3], list(Page.objects.filter(comments__user=u3))) |             list(Page.objects.filter(comments__user=u1))) | ||||||
|  |         self.assertEqual( | ||||||
|  |             [p1, p2, p4], | ||||||
|  |             list(Page.objects.filter(comments__user=u2))) | ||||||
|  |         self.assertEqual( | ||||||
|  |             [p1, p3], | ||||||
|  |             list(Page.objects.filter(comments__user=u3))) | ||||||
|  |  | ||||||
|     def test_save_embedded_document(self): |     def test_save_embedded_document(self): | ||||||
|         """Ensure that a document with an embedded document field may be |         """Ensure that a document with an embedded document field may be | ||||||
| @@ -1590,7 +1612,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|         self.assertEqual(promoted_employee.age, 50) |         self.assertEqual(promoted_employee.age, 50) | ||||||
|  |  | ||||||
|         # Ensure that the 'details' embedded object saved correctly |         # Ensure that the 'details' embedded object saved correctly | ||||||
|         self.assertEqual(promoted_employee.details.position, 'Senior Developer') |         self.assertEqual( | ||||||
|  |             promoted_employee.details.position, 'Senior Developer') | ||||||
|  |  | ||||||
|         # Test removal |         # Test removal | ||||||
|         promoted_employee.details = None |         promoted_employee.details = None | ||||||
| @@ -1726,7 +1749,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|         post.save() |         post.save() | ||||||
|  |  | ||||||
|         reviewer.delete() |         reviewer.delete() | ||||||
|         self.assertEqual(BlogPost.objects.count(), 1)  # No effect on the BlogPost |         # No effect on the BlogPost | ||||||
|  |         self.assertEqual(BlogPost.objects.count(), 1) | ||||||
|         self.assertEqual(BlogPost.objects.get().reviewer, None) |         self.assertEqual(BlogPost.objects.get().reviewer, None) | ||||||
|  |  | ||||||
|         # Delete the Person, which should lead to deletion of the BlogPost, too |         # Delete the Person, which should lead to deletion of the BlogPost, too | ||||||
| @@ -1775,8 +1799,10 @@ class InstanceTest(unittest.TestCase): | |||||||
|  |  | ||||||
|         class BlogPost(Document): |         class BlogPost(Document): | ||||||
|             content = StringField() |             content = StringField() | ||||||
|             authors = ListField(ReferenceField(self.Person, reverse_delete_rule=CASCADE)) |             authors = ListField(ReferenceField( | ||||||
|             reviewers = ListField(ReferenceField(self.Person, reverse_delete_rule=NULLIFY)) |                 self.Person, reverse_delete_rule=CASCADE)) | ||||||
|  |             reviewers = ListField(ReferenceField( | ||||||
|  |                 self.Person, reverse_delete_rule=NULLIFY)) | ||||||
|  |  | ||||||
|         self.Person.drop_collection() |         self.Person.drop_collection() | ||||||
|  |  | ||||||
| @@ -1876,8 +1902,12 @@ class InstanceTest(unittest.TestCase): | |||||||
|         def throw_invalid_document_error(): |         def throw_invalid_document_error(): | ||||||
|             class Blog(Document): |             class Blog(Document): | ||||||
|                 content = StringField() |                 content = StringField() | ||||||
|                 authors = MapField(ReferenceField(self.Person, reverse_delete_rule=CASCADE)) |                 authors = MapField(ReferenceField( | ||||||
|                 reviewers = DictField(field=ReferenceField(self.Person, reverse_delete_rule=NULLIFY)) |                     self.Person, reverse_delete_rule=CASCADE)) | ||||||
|  |                 reviewers = DictField( | ||||||
|  |                     field=ReferenceField( | ||||||
|  |                         self.Person, | ||||||
|  |                         reverse_delete_rule=NULLIFY)) | ||||||
|  |  | ||||||
|         self.assertRaises(InvalidDocumentError, throw_invalid_document_error) |         self.assertRaises(InvalidDocumentError, throw_invalid_document_error) | ||||||
|  |  | ||||||
| @@ -1886,7 +1916,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|                 father = ReferenceField('Person', reverse_delete_rule=DENY) |                 father = ReferenceField('Person', reverse_delete_rule=DENY) | ||||||
|                 mother = ReferenceField('Person', reverse_delete_rule=DENY) |                 mother = ReferenceField('Person', reverse_delete_rule=DENY) | ||||||
|  |  | ||||||
|         self.assertRaises(InvalidDocumentError, throw_invalid_document_error_embedded) |         self.assertRaises( | ||||||
|  |             InvalidDocumentError, throw_invalid_document_error_embedded) | ||||||
|  |  | ||||||
|     def test_reverse_delete_rule_cascade_recurs(self): |     def test_reverse_delete_rule_cascade_recurs(self): | ||||||
|         """Ensure that a chain of documents is also deleted upon cascaded |         """Ensure that a chain of documents is also deleted upon cascaded | ||||||
| @@ -1916,8 +1947,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|         comment.post = post |         comment.post = post | ||||||
|         comment.save() |         comment.save() | ||||||
|  |  | ||||||
|         # Delete the Person, which should lead to deletion of the BlogPost, and, |         # Delete the Person, which should lead to deletion of the BlogPost, | ||||||
|         # recursively to the Comment, too |         # and, recursively to the Comment, too | ||||||
|         author.delete() |         author.delete() | ||||||
|         self.assertEqual(Comment.objects.count(), 0) |         self.assertEqual(Comment.objects.count(), 0) | ||||||
|  |  | ||||||
| @@ -2056,7 +2087,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|  |  | ||||||
|     def test_dynamic_document_pickle(self): |     def test_dynamic_document_pickle(self): | ||||||
|  |  | ||||||
|         pickle_doc = PickleDynamicTest(name="test", number=1, string="One", lists=['1', '2']) |         pickle_doc = PickleDynamicTest( | ||||||
|  |             name="test", number=1, string="One", lists=['1', '2']) | ||||||
|         pickle_doc.embedded = PickleDyanmicEmbedded(foo="Bar") |         pickle_doc.embedded = PickleDyanmicEmbedded(foo="Bar") | ||||||
|         pickled_doc = pickle.dumps(pickle_doc)  # make sure pickling works even before the doc is saved |         pickled_doc = pickle.dumps(pickle_doc)  # make sure pickling works even before the doc is saved | ||||||
|  |  | ||||||
| @@ -2078,7 +2110,8 @@ class InstanceTest(unittest.TestCase): | |||||||
|                          pickle_doc.embedded._dynamic_fields.keys()) |                          pickle_doc.embedded._dynamic_fields.keys()) | ||||||
|  |  | ||||||
|     def test_picklable_on_signals(self): |     def test_picklable_on_signals(self): | ||||||
|         pickle_doc = PickleSignalsTest(number=1, string="One", lists=['1', '2']) |         pickle_doc = PickleSignalsTest( | ||||||
|  |             number=1, string="One", lists=['1', '2']) | ||||||
|         pickle_doc.embedded = PickleEmbedded() |         pickle_doc.embedded = PickleEmbedded() | ||||||
|         pickle_doc.save() |         pickle_doc.save() | ||||||
|         pickle_doc.delete() |         pickle_doc.delete() | ||||||
| @@ -2233,9 +2266,15 @@ class InstanceTest(unittest.TestCase): | |||||||
|         self.assertEqual(AuthorBooks._get_db(), get_db("testdb-3")) |         self.assertEqual(AuthorBooks._get_db(), get_db("testdb-3")) | ||||||
|  |  | ||||||
|         # Collections |         # Collections | ||||||
|         self.assertEqual(User._get_collection(), get_db("testdb-1")[User._get_collection_name()]) |         self.assertEqual( | ||||||
|         self.assertEqual(Book._get_collection(), get_db("testdb-2")[Book._get_collection_name()]) |             User._get_collection(), | ||||||
|         self.assertEqual(AuthorBooks._get_collection(), get_db("testdb-3")[AuthorBooks._get_collection_name()]) |             get_db("testdb-1")[User._get_collection_name()]) | ||||||
|  |         self.assertEqual( | ||||||
|  |             Book._get_collection(), | ||||||
|  |             get_db("testdb-2")[Book._get_collection_name()]) | ||||||
|  |         self.assertEqual( | ||||||
|  |             AuthorBooks._get_collection(), | ||||||
|  |             get_db("testdb-3")[AuthorBooks._get_collection_name()]) | ||||||
|  |  | ||||||
|     def test_db_alias_overrides(self): |     def test_db_alias_overrides(self): | ||||||
|         """db_alias can be overriden |         """db_alias can be overriden | ||||||
| @@ -2613,7 +2652,9 @@ class InstanceTest(unittest.TestCase): | |||||||
|         system.save() |         system.save() | ||||||
|  |  | ||||||
|         system = NodesSystem.objects.first() |         system = NodesSystem.objects.first() | ||||||
|         self.assertEqual("UNDEFINED", system.nodes["node"].parameters["param"].macros["test"].value) |         self.assertEqual( | ||||||
|  |             "UNDEFINED", | ||||||
|  |             system.nodes["node"].parameters["param"].macros["test"].value) | ||||||
|  |  | ||||||
|     def test_embedded_document_equality(self): |     def test_embedded_document_equality(self): | ||||||
|  |  | ||||||
| @@ -2730,5 +2771,26 @@ class InstanceTest(unittest.TestCase): | |||||||
|         self.assertEquals(p.id, None) |         self.assertEquals(p.id, None) | ||||||
|         p.id = "12345"  # in case it is not working: "OperationError: Shard Keys are immutable..." will be raised here |         p.id = "12345"  # in case it is not working: "OperationError: Shard Keys are immutable..." will be raised here | ||||||
|  |  | ||||||
|  |     def test_null_field(self): | ||||||
|  |         # 734 | ||||||
|  |         class User(Document): | ||||||
|  |             name = StringField() | ||||||
|  |             height = IntField(default=184, null=True) | ||||||
|  |         User.objects.delete() | ||||||
|  |         u = User(name='user') | ||||||
|  |         u.save() | ||||||
|  |         u_from_db = User.objects.get(name='user') | ||||||
|  |         u_from_db.height = None | ||||||
|  |         u_from_db.save() | ||||||
|  |         self.assertEquals(u_from_db.height, None) | ||||||
|  |  | ||||||
|  |         # 735 | ||||||
|  |         User.objects.delete() | ||||||
|  |         u = User(name='user') | ||||||
|  |         u.save() | ||||||
|  |         User.objects(name='user').update_one(set__height=None, upsert=True) | ||||||
|  |         u_from_db = User.objects.get(name='user') | ||||||
|  |         self.assertEquals(u_from_db.height, None) | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user