Repository: MongoEngine/mongoengine

Issue:
ComplexDateTimeField fails unexpectedly if an invalid string is set
```
In [1]: from mongoengine import *

In [2]: connect()
Out[2]: MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True, read_preference=Primary())

In [3]: class Visit(Document):
   ...:     timestamp = ComplexDateTimeField()
   ...:     

In [4]: Visit(timestamp='yolo')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-51965e337718> in <module>()
----> 1 Visit(timestamp='yolo').save()

/home/bgerard/dev/mongoengine/mongoengine/base/document.py in __init__(self, *args, **values)
    137                         if field and not isinstance(field, FileField):
    138                             value = field.to_python(value)
--> 139                     setattr(self, key, value)
    140                 else:
    141                     self._data[key] = value

/home/bgerard/dev/mongoengine/mongoengine/base/document.py in __setattr__(self, name, value)
    213             super(BaseDocument, self).__setattr__("_created", False)
    214 
--> 215         super(BaseDocument, self).__setattr__(name, value)
    216 
    217     def __getstate__(self):

/home/bgerard/dev/mongoengine/mongoengine/fields.py in __set__(self, instance, value)
    684         value = instance._data[self.name]
    685         if value is not None:
--> 686             instance._data[self.name] = self._convert_from_datetime(value)
    687 
    688     def validate(self, value):

/home/bgerard/dev/mongoengine/mongoengine/fields.py in _convert_from_datetime(self, val)
    655         '2011,06,08,20,26,24,092284'
    656         """
--> 657         return val.strftime(self.format)
    658 
    659     def _convert_from_string(self, data):

AttributeError: 'str' object has no attribute 'strftime'
```
To mimic the behavior of other fields, it should fail only when .save/validate are called


Discussion:

Resolving patch:
diff --git a/mongoengine/fields.py b/mongoengine/fields.py
--- a/mongoengine/fields.py
+++ b/mongoengine/fields.py
@@ -677,7 +677,10 @@ def __set__(self, instance, value):
         super().__set__(instance, value)
         value = instance._data[self.name]
         if value is not None:
-            instance._data[self.name] = self._convert_from_datetime(value)
+            if isinstance(value, datetime.datetime):
+                instance._data[self.name] = self._convert_from_datetime(value)
+            else:
+                instance._data[self.name] = value
 
     def validate(self, value):
         value = self.to_python(value)


Test patch:
diff --git a/tests/fields/test_complex_datetime_field.py b/tests/fields/test_complex_datetime_field.py
--- a/tests/fields/test_complex_datetime_field.py
+++ b/tests/fields/test_complex_datetime_field.py
@@ -4,6 +4,8 @@
 import math
 import re
 
+import pytest
+
 from mongoengine import *
 
 from tests.utils import MongoDBTestCase
@@ -191,3 +193,18 @@ class Log(Document):
 
         fetched_log = Log.objects.with_id(log.id)
         assert fetched_log.timestamp >= NOW
+
+    def test_setting_bad_value_does_not_raise_unless_validate_is_called(self):
+        # test regression of #2253
+
+        class Log(Document):
+            timestamp = ComplexDateTimeField()
+
+        Log.drop_collection()
+
+        log = Log(timestamp="garbage")
+        with pytest.raises(ValidationError):
+            log.validate()
+
+        with pytest.raises(ValidationError):
+            log.save()