From 5447c6e947fb6cf16c3995cd24fe7618e0707855 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 4 Jun 2013 09:08:13 +0000 Subject: [PATCH] DateTimeField now auto converts valid datetime isostrings into dates (#343) --- docs/changelog.rst | 2 ++ mongoengine/fields.py | 25 +++++++++++++++++++------ setup.py | 2 +- tests/fields/fields.py | 12 ++++++++++-- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 6a4dab60..6b666aa4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,8 @@ Changelog Changes in 0.8.2 ================ +- DateTimeField now auto converts valid datetime isostrings into dates (#343) +- DateTimeField now uses dateutil for parsing if available (#343) - Fixed Doc.objects(read_preference=X) not setting read preference (#352) - Django session ttl index expiry fixed (#329) - Fixed pickle.loads (#342) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 8ea48c25..2b0e3951 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -7,6 +7,7 @@ import urllib2 import uuid import warnings from operator import itemgetter + try: import dateutil except ImportError: @@ -353,6 +354,11 @@ class BooleanField(BaseField): class DateTimeField(BaseField): """A datetime field. + Uses the python-dateutil library if available alternatively use time.strptime + to parse the dates. Note: python-dateutil's parser is fully featured and when + installed you can utilise it to convert varing types of date formats into valid + python datetime objects. + Note: Microseconds are rounded to the nearest millisecond. Pre UTC microsecond support is effecively broken. Use :class:`~mongoengine.fields.ComplexDateTimeField` if you @@ -360,13 +366,11 @@ class DateTimeField(BaseField): """ def validate(self, value): - if not isinstance(value, (datetime.datetime, datetime.date)): + new_value = self.to_mongo(value) + if not isinstance(new_value, (datetime.datetime, datetime.date)): self.error(u'cannot parse date "%s"' % value) def to_mongo(self, value): - return self.prepare_query_value(None, value) - - def prepare_query_value(self, op, value): if value is None: return value if isinstance(value, datetime.datetime): @@ -376,10 +380,16 @@ class DateTimeField(BaseField): if callable(value): return value() + if not isinstance(value, basestring): + return None + # Attempt to parse a datetime: if dateutil: - return dateutil.parser.parse(value) - # value = smart_str(value) + try: + return dateutil.parser.parse(value) + except ValueError: + return None + # split usecs, because they are not recognized by strptime. if '.' in value: try: @@ -404,6 +414,9 @@ class DateTimeField(BaseField): except ValueError: return None + def prepare_query_value(self, op, value): + return self.to_mongo(value) + class ComplexDateTimeField(StringField): """ diff --git a/setup.py b/setup.py index 365791fc..1888828f 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ if sys.version_info[0] == 3: extra_opts['packages'].append("tests") extra_opts['package_data'] = {"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]} else: - extra_opts['tests_require'] = ['nose', 'coverage', 'blinker', 'django>=1.4.2', 'PIL', 'jinja2==2.6'] + extra_opts['tests_require'] = ['nose', 'coverage', 'blinker', 'django>=1.4.2', 'PIL', 'jinja2==2.6', 'python-dateutil==1.5'] extra_opts['packages'] = find_packages(exclude=('tests',)) setup(name='mongoengine', diff --git a/tests/fields/fields.py b/tests/fields/fields.py index 6c3f49f7..00a4bd78 100644 --- a/tests/fields/fields.py +++ b/tests/fields/fields.py @@ -408,9 +408,16 @@ class FieldTest(unittest.TestCase): log.time = datetime.date.today() log.validate() + log.time = datetime.datetime.now().isoformat(' ') + log.validate() + + if dateutil: + log.time = datetime.datetime.now().isoformat('T') + log.validate() + log.time = -1 self.assertRaises(ValidationError, log.validate) - log.time = '1pm' + log.time = 'ABC' self.assertRaises(ValidationError, log.validate) def test_datetime_tz_aware_mark_as_changed(self): @@ -497,6 +504,7 @@ class FieldTest(unittest.TestCase): d1 = datetime.datetime(1970, 01, 01, 00, 00, 01) log = LogEntry() log.date = d1 + log.validate() log.save() for query in (d1, d1.isoformat(' ')): @@ -1993,7 +2001,7 @@ class FieldTest(unittest.TestCase): self.db['mongoengine.counters'].drop() self.assertEqual(Person.id.get_next_value(), '1') - + def test_sequence_field_sequence_name(self): class Person(Document): id = SequenceField(primary_key=True, sequence_name='jelly')