commit
02a557aa67
@ -2,6 +2,10 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
Changes in 0.15.4
|
||||||
|
=================
|
||||||
|
- Added `DateField` #513
|
||||||
|
|
||||||
Changes in 0.15.3
|
Changes in 0.15.3
|
||||||
=================
|
=================
|
||||||
- Subfield resolve error in generic_emdedded_document query #1651 #1652
|
- Subfield resolve error in generic_emdedded_document query #1651 #1652
|
||||||
|
@ -43,7 +43,7 @@ except ImportError:
|
|||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'StringField', 'URLField', 'EmailField', 'IntField', 'LongField',
|
'StringField', 'URLField', 'EmailField', 'IntField', 'LongField',
|
||||||
'FloatField', 'DecimalField', 'BooleanField', 'DateTimeField',
|
'FloatField', 'DecimalField', 'BooleanField', 'DateTimeField', 'DateField',
|
||||||
'ComplexDateTimeField', 'EmbeddedDocumentField', 'ObjectIdField',
|
'ComplexDateTimeField', 'EmbeddedDocumentField', 'ObjectIdField',
|
||||||
'GenericEmbeddedDocumentField', 'DynamicField', 'ListField',
|
'GenericEmbeddedDocumentField', 'DynamicField', 'ListField',
|
||||||
'SortedListField', 'EmbeddedDocumentListField', 'DictField',
|
'SortedListField', 'EmbeddedDocumentListField', 'DictField',
|
||||||
@ -525,6 +525,22 @@ class DateTimeField(BaseField):
|
|||||||
return super(DateTimeField, self).prepare_query_value(op, self.to_mongo(value))
|
return super(DateTimeField, self).prepare_query_value(op, self.to_mongo(value))
|
||||||
|
|
||||||
|
|
||||||
|
class DateField(DateTimeField):
|
||||||
|
def to_mongo(self, value):
|
||||||
|
value = super(DateField, self).to_mongo(value)
|
||||||
|
# drop hours, minutes, seconds
|
||||||
|
if isinstance(value, datetime.datetime):
|
||||||
|
value = datetime.datetime(value.year, value.month, value.day)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
value = super(DateField, self).to_python(value)
|
||||||
|
# convert datetime to date
|
||||||
|
if isinstance(value, datetime.datetime):
|
||||||
|
value = datetime.date(value.year, value.month, value.day)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class ComplexDateTimeField(StringField):
|
class ComplexDateTimeField(StringField):
|
||||||
"""
|
"""
|
||||||
ComplexDateTimeField handles microseconds exactly instead of rounding
|
ComplexDateTimeField handles microseconds exactly instead of rounding
|
||||||
|
@ -46,6 +46,17 @@ class FieldTest(MongoDBTestCase):
|
|||||||
md = MyDoc(dt='')
|
md = MyDoc(dt='')
|
||||||
self.assertRaises(ValidationError, md.save)
|
self.assertRaises(ValidationError, md.save)
|
||||||
|
|
||||||
|
def test_date_from_empty_string(self):
|
||||||
|
"""
|
||||||
|
Ensure an exception is raised when trying to
|
||||||
|
cast an empty string to datetime.
|
||||||
|
"""
|
||||||
|
class MyDoc(Document):
|
||||||
|
dt = DateField()
|
||||||
|
|
||||||
|
md = MyDoc(dt='')
|
||||||
|
self.assertRaises(ValidationError, md.save)
|
||||||
|
|
||||||
def test_datetime_from_whitespace_string(self):
|
def test_datetime_from_whitespace_string(self):
|
||||||
"""
|
"""
|
||||||
Ensure an exception is raised when trying to
|
Ensure an exception is raised when trying to
|
||||||
@ -57,6 +68,17 @@ class FieldTest(MongoDBTestCase):
|
|||||||
md = MyDoc(dt=' ')
|
md = MyDoc(dt=' ')
|
||||||
self.assertRaises(ValidationError, md.save)
|
self.assertRaises(ValidationError, md.save)
|
||||||
|
|
||||||
|
def test_date_from_whitespace_string(self):
|
||||||
|
"""
|
||||||
|
Ensure an exception is raised when trying to
|
||||||
|
cast a whitespace-only string to datetime.
|
||||||
|
"""
|
||||||
|
class MyDoc(Document):
|
||||||
|
dt = DateField()
|
||||||
|
|
||||||
|
md = MyDoc(dt=' ')
|
||||||
|
self.assertRaises(ValidationError, md.save)
|
||||||
|
|
||||||
def test_default_values_nothing_set(self):
|
def test_default_values_nothing_set(self):
|
||||||
"""Ensure that default field values are used when creating
|
"""Ensure that default field values are used when creating
|
||||||
a document.
|
a document.
|
||||||
@ -66,13 +88,14 @@ class FieldTest(MongoDBTestCase):
|
|||||||
age = IntField(default=30, required=False)
|
age = IntField(default=30, required=False)
|
||||||
userid = StringField(default=lambda: 'test', required=True)
|
userid = StringField(default=lambda: 'test', required=True)
|
||||||
created = DateTimeField(default=datetime.datetime.utcnow)
|
created = DateTimeField(default=datetime.datetime.utcnow)
|
||||||
|
day = DateField(default=datetime.date.today)
|
||||||
|
|
||||||
person = Person(name="Ross")
|
person = Person(name="Ross")
|
||||||
|
|
||||||
# Confirm saving now would store values
|
# Confirm saving now would store values
|
||||||
data_to_be_saved = sorted(person.to_mongo().keys())
|
data_to_be_saved = sorted(person.to_mongo().keys())
|
||||||
self.assertEqual(data_to_be_saved,
|
self.assertEqual(data_to_be_saved,
|
||||||
['age', 'created', 'name', 'userid']
|
['age', 'created', 'day', 'name', 'userid']
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertTrue(person.validate() is None)
|
self.assertTrue(person.validate() is None)
|
||||||
@ -81,16 +104,18 @@ class FieldTest(MongoDBTestCase):
|
|||||||
self.assertEqual(person.age, person.age)
|
self.assertEqual(person.age, person.age)
|
||||||
self.assertEqual(person.userid, person.userid)
|
self.assertEqual(person.userid, person.userid)
|
||||||
self.assertEqual(person.created, person.created)
|
self.assertEqual(person.created, person.created)
|
||||||
|
self.assertEqual(person.day, person.day)
|
||||||
|
|
||||||
self.assertEqual(person._data['name'], person.name)
|
self.assertEqual(person._data['name'], person.name)
|
||||||
self.assertEqual(person._data['age'], person.age)
|
self.assertEqual(person._data['age'], person.age)
|
||||||
self.assertEqual(person._data['userid'], person.userid)
|
self.assertEqual(person._data['userid'], person.userid)
|
||||||
self.assertEqual(person._data['created'], person.created)
|
self.assertEqual(person._data['created'], person.created)
|
||||||
|
self.assertEqual(person._data['day'], person.day)
|
||||||
|
|
||||||
# Confirm introspection changes nothing
|
# Confirm introspection changes nothing
|
||||||
data_to_be_saved = sorted(person.to_mongo().keys())
|
data_to_be_saved = sorted(person.to_mongo().keys())
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
data_to_be_saved, ['age', 'created', 'name', 'userid'])
|
data_to_be_saved, ['age', 'created', 'day', 'name', 'userid'])
|
||||||
|
|
||||||
def test_default_values_set_to_None(self):
|
def test_default_values_set_to_None(self):
|
||||||
"""Ensure that default field values are used even when
|
"""Ensure that default field values are used even when
|
||||||
@ -662,6 +687,32 @@ class FieldTest(MongoDBTestCase):
|
|||||||
log.time = 'ABC'
|
log.time = 'ABC'
|
||||||
self.assertRaises(ValidationError, log.validate)
|
self.assertRaises(ValidationError, log.validate)
|
||||||
|
|
||||||
|
def test_date_validation(self):
|
||||||
|
"""Ensure that invalid values cannot be assigned to datetime
|
||||||
|
fields.
|
||||||
|
"""
|
||||||
|
class LogEntry(Document):
|
||||||
|
time = DateField()
|
||||||
|
|
||||||
|
log = LogEntry()
|
||||||
|
log.time = datetime.datetime.now()
|
||||||
|
log.validate()
|
||||||
|
|
||||||
|
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 = 'ABC'
|
||||||
|
self.assertRaises(ValidationError, log.validate)
|
||||||
|
|
||||||
def test_datetime_tz_aware_mark_as_changed(self):
|
def test_datetime_tz_aware_mark_as_changed(self):
|
||||||
from mongoengine import connection
|
from mongoengine import connection
|
||||||
|
|
||||||
@ -733,6 +784,51 @@ class FieldTest(MongoDBTestCase):
|
|||||||
self.assertNotEqual(log.date, d1)
|
self.assertNotEqual(log.date, d1)
|
||||||
self.assertEqual(log.date, d2)
|
self.assertEqual(log.date, d2)
|
||||||
|
|
||||||
|
def test_date(self):
|
||||||
|
"""Tests showing pymongo date fields
|
||||||
|
|
||||||
|
See: http://api.mongodb.org/python/current/api/bson/son.html#dt
|
||||||
|
"""
|
||||||
|
class LogEntry(Document):
|
||||||
|
date = DateField()
|
||||||
|
|
||||||
|
LogEntry.drop_collection()
|
||||||
|
|
||||||
|
# Test can save dates
|
||||||
|
log = LogEntry()
|
||||||
|
log.date = datetime.date.today()
|
||||||
|
log.save()
|
||||||
|
log.reload()
|
||||||
|
self.assertEqual(log.date, datetime.date.today())
|
||||||
|
|
||||||
|
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 999)
|
||||||
|
d2 = datetime.datetime(1970, 1, 1, 0, 0, 1)
|
||||||
|
log = LogEntry()
|
||||||
|
log.date = d1
|
||||||
|
log.save()
|
||||||
|
log.reload()
|
||||||
|
self.assertEqual(log.date, d1.date())
|
||||||
|
self.assertEqual(log.date, d2.date())
|
||||||
|
|
||||||
|
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 9999)
|
||||||
|
d2 = datetime.datetime(1970, 1, 1, 0, 0, 1, 9000)
|
||||||
|
log.date = d1
|
||||||
|
log.save()
|
||||||
|
log.reload()
|
||||||
|
self.assertEqual(log.date, d1.date())
|
||||||
|
self.assertEqual(log.date, d2.date())
|
||||||
|
|
||||||
|
if not six.PY3:
|
||||||
|
# Pre UTC dates microseconds below 1000 are dropped
|
||||||
|
# This does not seem to be true in PY3
|
||||||
|
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, 999)
|
||||||
|
d2 = datetime.datetime(1969, 12, 31, 23, 59, 59)
|
||||||
|
log.date = d1
|
||||||
|
log.save()
|
||||||
|
log.reload()
|
||||||
|
self.assertEqual(log.date, d1.date())
|
||||||
|
self.assertEqual(log.date, d2.date())
|
||||||
|
|
||||||
def test_datetime_usage(self):
|
def test_datetime_usage(self):
|
||||||
"""Tests for regular datetime fields"""
|
"""Tests for regular datetime fields"""
|
||||||
class LogEntry(Document):
|
class LogEntry(Document):
|
||||||
@ -787,6 +883,51 @@ class FieldTest(MongoDBTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(logs.count(), 5)
|
self.assertEqual(logs.count(), 5)
|
||||||
|
|
||||||
|
def test_date_usage(self):
|
||||||
|
"""Tests for regular datetime fields"""
|
||||||
|
class LogEntry(Document):
|
||||||
|
date = DateField()
|
||||||
|
|
||||||
|
LogEntry.drop_collection()
|
||||||
|
|
||||||
|
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1)
|
||||||
|
log = LogEntry()
|
||||||
|
log.date = d1
|
||||||
|
log.validate()
|
||||||
|
log.save()
|
||||||
|
|
||||||
|
for query in (d1, d1.isoformat(' ')):
|
||||||
|
log1 = LogEntry.objects.get(date=query)
|
||||||
|
self.assertEqual(log, log1)
|
||||||
|
|
||||||
|
if dateutil:
|
||||||
|
log1 = LogEntry.objects.get(date=d1.isoformat('T'))
|
||||||
|
self.assertEqual(log, log1)
|
||||||
|
|
||||||
|
# create additional 19 log entries for a total of 20
|
||||||
|
for i in range(1971, 1990):
|
||||||
|
d = datetime.datetime(i, 1, 1, 0, 0, 1)
|
||||||
|
LogEntry(date=d).save()
|
||||||
|
|
||||||
|
self.assertEqual(LogEntry.objects.count(), 20)
|
||||||
|
|
||||||
|
# Test ordering
|
||||||
|
logs = LogEntry.objects.order_by("date")
|
||||||
|
i = 0
|
||||||
|
while i < 19:
|
||||||
|
self.assertTrue(logs[i].date <= logs[i + 1].date)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
logs = LogEntry.objects.order_by("-date")
|
||||||
|
i = 0
|
||||||
|
while i < 19:
|
||||||
|
self.assertTrue(logs[i].date >= logs[i + 1].date)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# Test searching
|
||||||
|
logs = LogEntry.objects.filter(date__gte=datetime.datetime(1980, 1, 1))
|
||||||
|
self.assertEqual(logs.count(), 10)
|
||||||
|
|
||||||
def test_complexdatetime_storage(self):
|
def test_complexdatetime_storage(self):
|
||||||
"""Tests for complex datetime fields - which can handle
|
"""Tests for complex datetime fields - which can handle
|
||||||
microseconds without rounding.
|
microseconds without rounding.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user