DateTimeField now auto converts valid datetime isostrings into dates (#343)

This commit is contained in:
Ross Lawley 2013-06-04 09:08:13 +00:00
parent f1b97fbc8b
commit 5447c6e947
4 changed files with 32 additions and 9 deletions

View File

@ -5,6 +5,8 @@ Changelog
Changes in 0.8.2 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) - Fixed Doc.objects(read_preference=X) not setting read preference (#352)
- Django session ttl index expiry fixed (#329) - Django session ttl index expiry fixed (#329)
- Fixed pickle.loads (#342) - Fixed pickle.loads (#342)

View File

@ -7,6 +7,7 @@ import urllib2
import uuid import uuid
import warnings import warnings
from operator import itemgetter from operator import itemgetter
try: try:
import dateutil import dateutil
except ImportError: except ImportError:
@ -353,6 +354,11 @@ class BooleanField(BaseField):
class DateTimeField(BaseField): class DateTimeField(BaseField):
"""A datetime field. """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. Note: Microseconds are rounded to the nearest millisecond.
Pre UTC microsecond support is effecively broken. Pre UTC microsecond support is effecively broken.
Use :class:`~mongoengine.fields.ComplexDateTimeField` if you Use :class:`~mongoengine.fields.ComplexDateTimeField` if you
@ -360,13 +366,11 @@ class DateTimeField(BaseField):
""" """
def validate(self, value): 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) self.error(u'cannot parse date "%s"' % value)
def to_mongo(self, value): def to_mongo(self, value):
return self.prepare_query_value(None, value)
def prepare_query_value(self, op, value):
if value is None: if value is None:
return value return value
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
@ -376,10 +380,16 @@ class DateTimeField(BaseField):
if callable(value): if callable(value):
return value() return value()
if not isinstance(value, basestring):
return None
# Attempt to parse a datetime: # Attempt to parse a datetime:
if dateutil: if dateutil:
return dateutil.parser.parse(value) try:
# value = smart_str(value) return dateutil.parser.parse(value)
except ValueError:
return None
# split usecs, because they are not recognized by strptime. # split usecs, because they are not recognized by strptime.
if '.' in value: if '.' in value:
try: try:
@ -404,6 +414,9 @@ class DateTimeField(BaseField):
except ValueError: except ValueError:
return None return None
def prepare_query_value(self, op, value):
return self.to_mongo(value)
class ComplexDateTimeField(StringField): class ComplexDateTimeField(StringField):
""" """

View File

@ -57,7 +57,7 @@ if sys.version_info[0] == 3:
extra_opts['packages'].append("tests") extra_opts['packages'].append("tests")
extra_opts['package_data'] = {"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]} extra_opts['package_data'] = {"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]}
else: 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',)) extra_opts['packages'] = find_packages(exclude=('tests',))
setup(name='mongoengine', setup(name='mongoengine',

View File

@ -408,9 +408,16 @@ class FieldTest(unittest.TestCase):
log.time = datetime.date.today() log.time = datetime.date.today()
log.validate() log.validate()
log.time = datetime.datetime.now().isoformat(' ')
log.validate()
if dateutil:
log.time = datetime.datetime.now().isoformat('T')
log.validate()
log.time = -1 log.time = -1
self.assertRaises(ValidationError, log.validate) self.assertRaises(ValidationError, log.validate)
log.time = '1pm' log.time = 'ABC'
self.assertRaises(ValidationError, log.validate) self.assertRaises(ValidationError, log.validate)
def test_datetime_tz_aware_mark_as_changed(self): 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) d1 = datetime.datetime(1970, 01, 01, 00, 00, 01)
log = LogEntry() log = LogEntry()
log.date = d1 log.date = d1
log.validate()
log.save() log.save()
for query in (d1, d1.isoformat(' ')): for query in (d1, d1.isoformat(' ')):
@ -1993,7 +2001,7 @@ class FieldTest(unittest.TestCase):
self.db['mongoengine.counters'].drop() self.db['mongoengine.counters'].drop()
self.assertEqual(Person.id.get_next_value(), '1') self.assertEqual(Person.id.get_next_value(), '1')
def test_sequence_field_sequence_name(self): def test_sequence_field_sequence_name(self):
class Person(Document): class Person(Document):
id = SequenceField(primary_key=True, sequence_name='jelly') id = SequenceField(primary_key=True, sequence_name='jelly')