Repository: MongoEngine/mongoengine

Issue:
Load field which has a value of None and a default value will result with the default value and not None
```
    class User(Document):
        name = StringField()
        height = IntField(default=184)
    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)
```



Discussion:
Duplicate of #734 - Essentially they are the same thing.

Default should be used on the creation of totally new objects only and not from the database.  This would be a breaking change and might get some people relying on the current behaviour (eg to mimic last_updated with a default callable of now).

Closing as duplicate.

I don't think it is the same as #734 because here when set height to None, null is actually written as the value of height in the DB which is cool, but then it is overridden with the default value when it is fetched from the DB


Resolving patch:
diff --git a/mongoengine/base/fields.py b/mongoengine/base/fields.py
--- a/mongoengine/base/fields.py
+++ b/mongoengine/base/fields.py
@@ -37,7 +37,7 @@ class BaseField(object):
     def __init__(self, db_field=None, name=None, required=False, default=None,
                  unique=False, unique_with=None, primary_key=False,
                  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
             (defaults to the name of the field)
@@ -60,6 +60,8 @@ def __init__(self, db_field=None, name=None, required=False, default=None,
             model forms from the document model.
         :param help_text: (optional) The help text for this field and is often
             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'
 
@@ -75,6 +77,7 @@ def __init__(self, db_field=None, name=None, required=False, default=None,
         self.choices = choices
         self.verbose_name = verbose_name
         self.help_text = help_text
+        self.null = null
 
         # Adjust the appropriate creation counter, and save our local copy.
         if self.db_field == '_id':
@@ -100,10 +103,13 @@ def __set__(self, instance, value):
 
         # If setting to None and theres a default
         # Then set the value to the default value
-        if value is None and self.default is not None:
-            value = self.default
-            if callable(value):
-                value = value()
+        if value is None:
+            if self.null:
+                value = None
+            elif self.default is not None:
+                value = self.default
+                if callable(value):
+                    value = value()
 
         if instance._initialised:
             try:


Test patch:
diff --git a/tests/document/instance.py b/tests/document/instance.py
--- a/tests/document/instance.py
+++ b/tests/document/instance.py
@@ -2706,5 +2706,26 @@ class Person(Document):
         self.assertEquals(p4.height, 189)
         self.assertEquals(Person.objects(height=189).count(), 1)
 
+    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__':
     unittest.main()