fix conflict
This commit is contained in:
commit
c8df3fd2a7
@ -7,7 +7,9 @@ Development
|
|||||||
- (Fill this out as you fix issues and develop your features).
|
- (Fill this out as you fix issues and develop your features).
|
||||||
- Fix .only() working improperly after using .count() of the same instance of QuerySet
|
- Fix .only() working improperly after using .count() of the same instance of QuerySet
|
||||||
- POTENTIAL BREAKING CHANGE: All result fields are now passed, including internal fields (_cls, _id) when using `QuerySet.as_pymongo` #1976
|
- POTENTIAL BREAKING CHANGE: All result fields are now passed, including internal fields (_cls, _id) when using `QuerySet.as_pymongo` #1976
|
||||||
|
- Document a BREAKING CHANGE introduced in 0.15.3 and not reported at that time (#1995)
|
||||||
- Fix InvalidStringData error when using modify on a BinaryField #1127
|
- Fix InvalidStringData error when using modify on a BinaryField #1127
|
||||||
|
- DEPRECATION: `EmbeddedDocument.save` & `.reload` are marked as deprecated and will be removed in a next version of mongoengine #1552
|
||||||
|
|
||||||
=================
|
=================
|
||||||
Changes in 0.16.3
|
Changes in 0.16.3
|
||||||
@ -65,6 +67,7 @@ Changes in 0.16.0
|
|||||||
|
|
||||||
Changes in 0.15.3
|
Changes in 0.15.3
|
||||||
=================
|
=================
|
||||||
|
- BREAKING CHANGES: `Queryset.update/update_one` methods now returns an UpdateResult when `full_result=True` is provided and no longer a dict (relates to #1491)
|
||||||
- Subfield resolve error in generic_emdedded_document query #1651 #1652
|
- Subfield resolve error in generic_emdedded_document query #1651 #1652
|
||||||
- use each modifier only with $position #1673 #1675
|
- use each modifier only with $position #1673 #1675
|
||||||
- Improve LazyReferenceField and GenericLazyReferenceField with nested fields #1704
|
- Improve LazyReferenceField and GenericLazyReferenceField with nested fields #1704
|
||||||
|
@ -91,9 +91,15 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
warnings.warn("EmbeddedDocument.save is deprecated and will be removed in a next version of mongoengine."
|
||||||
|
"Use the parent document's .save() or ._instance.save()",
|
||||||
|
DeprecationWarning, stacklevel=2)
|
||||||
self._instance.save(*args, **kwargs)
|
self._instance.save(*args, **kwargs)
|
||||||
|
|
||||||
def reload(self, *args, **kwargs):
|
def reload(self, *args, **kwargs):
|
||||||
|
warnings.warn("EmbeddedDocument.reload is deprecated and will be removed in a next version of mongoengine."
|
||||||
|
"Use the parent document's .reload() or ._instance.reload()",
|
||||||
|
DeprecationWarning, stacklevel=2)
|
||||||
self._instance.reload(*args, **kwargs)
|
self._instance.reload(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -499,11 +499,12 @@ class BaseQuerySet(object):
|
|||||||
``save(..., write_concern={w: 2, fsync: True}, ...)`` will
|
``save(..., write_concern={w: 2, fsync: True}, ...)`` will
|
||||||
wait until at least two servers have recorded the write and
|
wait until at least two servers have recorded the write and
|
||||||
will force an fsync on the primary server.
|
will force an fsync on the primary server.
|
||||||
:param full_result: Return the full result dictionary rather than just the number
|
:param full_result: Return the associated ``pymongo.UpdateResult`` rather than just the number
|
||||||
updated, e.g. return
|
updated items
|
||||||
``{'n': 2, 'nModified': 2, 'ok': 1.0, 'updatedExisting': True}``.
|
|
||||||
:param update: Django-style update keyword arguments
|
:param update: Django-style update keyword arguments
|
||||||
|
|
||||||
|
:returns the number of updated documents (unless ``full_result`` is True)
|
||||||
|
|
||||||
.. versionadded:: 0.2
|
.. versionadded:: 0.2
|
||||||
"""
|
"""
|
||||||
if not update and not upsert:
|
if not update and not upsert:
|
||||||
@ -567,7 +568,7 @@ class BaseQuerySet(object):
|
|||||||
document = self._document.objects.with_id(atomic_update.upserted_id)
|
document = self._document.objects.with_id(atomic_update.upserted_id)
|
||||||
return document
|
return document
|
||||||
|
|
||||||
def update_one(self, upsert=False, write_concern=None, **update):
|
def update_one(self, upsert=False, write_concern=None, full_result=False, **update):
|
||||||
"""Perform an atomic update on the fields of the first document
|
"""Perform an atomic update on the fields of the first document
|
||||||
matched by the query.
|
matched by the query.
|
||||||
|
|
||||||
@ -578,12 +579,19 @@ class BaseQuerySet(object):
|
|||||||
``save(..., write_concern={w: 2, fsync: True}, ...)`` will
|
``save(..., write_concern={w: 2, fsync: True}, ...)`` will
|
||||||
wait until at least two servers have recorded the write and
|
wait until at least two servers have recorded the write and
|
||||||
will force an fsync on the primary server.
|
will force an fsync on the primary server.
|
||||||
|
:param full_result: Return the associated ``pymongo.UpdateResult`` rather than just the number
|
||||||
|
updated items
|
||||||
:param update: Django-style update keyword arguments
|
:param update: Django-style update keyword arguments
|
||||||
|
full_result
|
||||||
|
:returns the number of updated documents (unless ``full_result`` is True)
|
||||||
.. versionadded:: 0.2
|
.. versionadded:: 0.2
|
||||||
"""
|
"""
|
||||||
return self.update(
|
return self.update(
|
||||||
upsert=upsert, multi=False, write_concern=write_concern, **update)
|
upsert=upsert,
|
||||||
|
multi=False,
|
||||||
|
write_concern=write_concern,
|
||||||
|
full_result=full_result,
|
||||||
|
**update)
|
||||||
|
|
||||||
def modify(self, upsert=False, full_response=False, remove=False, new=False, **update):
|
def modify(self, upsert=False, full_response=False, remove=False, new=False, **update):
|
||||||
"""Update and return the updated document.
|
"""Update and return the updated document.
|
||||||
|
@ -4,6 +4,7 @@ import os
|
|||||||
import pickle
|
import pickle
|
||||||
import unittest
|
import unittest
|
||||||
import uuid
|
import uuid
|
||||||
|
import warnings
|
||||||
import weakref
|
import weakref
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@ -3086,6 +3087,24 @@ class InstanceTest(MongoDBTestCase):
|
|||||||
"UNDEFINED",
|
"UNDEFINED",
|
||||||
system.nodes["node"].parameters["param"].macros["test"].value)
|
system.nodes["node"].parameters["param"].macros["test"].value)
|
||||||
|
|
||||||
|
def test_embedded_document_save_reload_warning(self):
|
||||||
|
"""Relates to #1570"""
|
||||||
|
class Embedded(EmbeddedDocument):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Doc(Document):
|
||||||
|
emb = EmbeddedDocumentField(Embedded)
|
||||||
|
|
||||||
|
doc = Doc(emb=Embedded()).save()
|
||||||
|
doc.emb.save() # Make sure its still working
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("error", DeprecationWarning)
|
||||||
|
with self.assertRaises(DeprecationWarning):
|
||||||
|
doc.emb.save()
|
||||||
|
|
||||||
|
with self.assertRaises(DeprecationWarning):
|
||||||
|
doc.emb.reload()
|
||||||
|
|
||||||
def test_embedded_document_equality(self):
|
def test_embedded_document_equality(self):
|
||||||
class Test(Document):
|
class Test(Document):
|
||||||
field = StringField(required=True)
|
field = StringField(required=True)
|
||||||
|
@ -10,11 +10,6 @@ import sys
|
|||||||
from nose.plugins.skip import SkipTest
|
from nose.plugins.skip import SkipTest
|
||||||
import six
|
import six
|
||||||
|
|
||||||
try:
|
|
||||||
import dateutil
|
|
||||||
except ImportError:
|
|
||||||
dateutil = None
|
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from bson import DBRef, ObjectId, SON
|
from bson import DBRef, ObjectId, SON
|
||||||
@ -30,55 +25,9 @@ from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList,
|
|||||||
|
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
__all__ = ("FieldTest", "EmbeddedDocumentListFieldTestCase")
|
|
||||||
|
|
||||||
|
|
||||||
class FieldTest(MongoDBTestCase):
|
class FieldTest(MongoDBTestCase):
|
||||||
|
|
||||||
def test_datetime_from_empty_string(self):
|
|
||||||
"""
|
|
||||||
Ensure an exception is raised when trying to
|
|
||||||
cast an empty string to datetime.
|
|
||||||
"""
|
|
||||||
class MyDoc(Document):
|
|
||||||
dt = DateTimeField()
|
|
||||||
|
|
||||||
md = MyDoc(dt='')
|
|
||||||
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):
|
|
||||||
"""
|
|
||||||
Ensure an exception is raised when trying to
|
|
||||||
cast a whitespace-only string to datetime.
|
|
||||||
"""
|
|
||||||
class MyDoc(Document):
|
|
||||||
dt = DateTimeField()
|
|
||||||
|
|
||||||
md = MyDoc(dt=' ')
|
|
||||||
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.
|
||||||
@ -695,273 +644,6 @@ class FieldTest(MongoDBTestCase):
|
|||||||
person.api_key = api_key
|
person.api_key = api_key
|
||||||
self.assertRaises(ValidationError, person.validate)
|
self.assertRaises(ValidationError, person.validate)
|
||||||
|
|
||||||
def test_datetime_validation(self):
|
|
||||||
"""Ensure that invalid values cannot be assigned to datetime
|
|
||||||
fields.
|
|
||||||
"""
|
|
||||||
class LogEntry(Document):
|
|
||||||
time = DateTimeField()
|
|
||||||
|
|
||||||
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_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):
|
|
||||||
from mongoengine import connection
|
|
||||||
|
|
||||||
# Reset the connections
|
|
||||||
connection._connection_settings = {}
|
|
||||||
connection._connections = {}
|
|
||||||
connection._dbs = {}
|
|
||||||
|
|
||||||
connect(db='mongoenginetest', tz_aware=True)
|
|
||||||
|
|
||||||
class LogEntry(Document):
|
|
||||||
time = DateTimeField()
|
|
||||||
|
|
||||||
LogEntry.drop_collection()
|
|
||||||
|
|
||||||
LogEntry(time=datetime.datetime(2013, 1, 1, 0, 0, 0)).save()
|
|
||||||
|
|
||||||
log = LogEntry.objects.first()
|
|
||||||
log.time = datetime.datetime(2013, 1, 1, 0, 0, 0)
|
|
||||||
self.assertEqual(['time'], log._changed_fields)
|
|
||||||
|
|
||||||
def test_datetime(self):
|
|
||||||
"""Tests showing pymongo datetime fields handling of microseconds.
|
|
||||||
Microseconds are rounded to the nearest millisecond and pre UTC
|
|
||||||
handling is wonky.
|
|
||||||
|
|
||||||
See: http://api.mongodb.org/python/current/api/bson/son.html#dt
|
|
||||||
"""
|
|
||||||
class LogEntry(Document):
|
|
||||||
date = DateTimeField()
|
|
||||||
|
|
||||||
LogEntry.drop_collection()
|
|
||||||
|
|
||||||
# Test can save dates
|
|
||||||
log = LogEntry()
|
|
||||||
log.date = datetime.date.today()
|
|
||||||
log.save()
|
|
||||||
log.reload()
|
|
||||||
self.assertEqual(log.date.date(), datetime.date.today())
|
|
||||||
|
|
||||||
# Post UTC - microseconds are rounded (down) nearest millisecond and
|
|
||||||
# dropped
|
|
||||||
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.assertNotEqual(log.date, d1)
|
|
||||||
self.assertEqual(log.date, d2)
|
|
||||||
|
|
||||||
# Post UTC - microseconds are rounded (down) nearest millisecond
|
|
||||||
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.assertNotEqual(log.date, d1)
|
|
||||||
self.assertEqual(log.date, d2)
|
|
||||||
|
|
||||||
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.assertNotEqual(log.date, d1)
|
|
||||||
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):
|
|
||||||
"""Tests for regular datetime fields"""
|
|
||||||
class LogEntry(Document):
|
|
||||||
date = DateTimeField()
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(date__lte=datetime.datetime(1980, 1, 1))
|
|
||||||
self.assertEqual(logs.count(), 10)
|
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(
|
|
||||||
date__lte=datetime.datetime(1980, 1, 1),
|
|
||||||
date__gte=datetime.datetime(1975, 1, 1),
|
|
||||||
)
|
|
||||||
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_list_validation(self):
|
def test_list_validation(self):
|
||||||
"""Ensure that a list field only accepts lists with valid elements."""
|
"""Ensure that a list field only accepts lists with valid elements."""
|
||||||
AccessLevelChoices = (
|
AccessLevelChoices = (
|
||||||
@ -5328,180 +5010,5 @@ class GenericLazyReferenceFieldTest(MongoDBTestCase):
|
|||||||
check_fields_type(occ)
|
check_fields_type(occ)
|
||||||
|
|
||||||
|
|
||||||
class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|
||||||
def test_complexdatetime_storage(self):
|
|
||||||
"""Tests for complex datetime fields - which can handle
|
|
||||||
microseconds without rounding.
|
|
||||||
"""
|
|
||||||
class LogEntry(Document):
|
|
||||||
date = ComplexDateTimeField()
|
|
||||||
date_with_dots = ComplexDateTimeField(separator='.')
|
|
||||||
|
|
||||||
LogEntry.drop_collection()
|
|
||||||
|
|
||||||
# Post UTC - microseconds are rounded (down) nearest millisecond and
|
|
||||||
# dropped - with default datetimefields
|
|
||||||
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 999)
|
|
||||||
log = LogEntry()
|
|
||||||
log.date = d1
|
|
||||||
log.save()
|
|
||||||
log.reload()
|
|
||||||
self.assertEqual(log.date, d1)
|
|
||||||
|
|
||||||
# Post UTC - microseconds are rounded (down) nearest millisecond - with
|
|
||||||
# default datetimefields
|
|
||||||
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 9999)
|
|
||||||
log.date = d1
|
|
||||||
log.save()
|
|
||||||
log.reload()
|
|
||||||
self.assertEqual(log.date, d1)
|
|
||||||
|
|
||||||
# Pre UTC dates microseconds below 1000 are dropped - with default
|
|
||||||
# datetimefields
|
|
||||||
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, 999)
|
|
||||||
log.date = d1
|
|
||||||
log.save()
|
|
||||||
log.reload()
|
|
||||||
self.assertEqual(log.date, d1)
|
|
||||||
|
|
||||||
# Pre UTC microseconds above 1000 is wonky - with default datetimefields
|
|
||||||
# log.date has an invalid microsecond value so I can't construct
|
|
||||||
# a date to compare.
|
|
||||||
for i in range(1001, 3113, 33):
|
|
||||||
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, i)
|
|
||||||
log.date = d1
|
|
||||||
log.save()
|
|
||||||
log.reload()
|
|
||||||
self.assertEqual(log.date, d1)
|
|
||||||
log1 = LogEntry.objects.get(date=d1)
|
|
||||||
self.assertEqual(log, log1)
|
|
||||||
|
|
||||||
# Test string padding
|
|
||||||
microsecond = map(int, [math.pow(10, x) for x in range(6)])
|
|
||||||
mm = dd = hh = ii = ss = [1, 10]
|
|
||||||
|
|
||||||
for values in itertools.product([2014], mm, dd, hh, ii, ss, microsecond):
|
|
||||||
stored = LogEntry(date=datetime.datetime(*values)).to_mongo()['date']
|
|
||||||
self.assertTrue(re.match('^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$', stored) is not None)
|
|
||||||
|
|
||||||
# Test separator
|
|
||||||
stored = LogEntry(date_with_dots=datetime.datetime(2014, 1, 1)).to_mongo()['date_with_dots']
|
|
||||||
self.assertTrue(re.match('^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$', stored) is not None)
|
|
||||||
|
|
||||||
def test_complexdatetime_usage(self):
|
|
||||||
"""Tests for complex datetime fields - which can handle
|
|
||||||
microseconds without rounding.
|
|
||||||
"""
|
|
||||||
class LogEntry(Document):
|
|
||||||
date = ComplexDateTimeField()
|
|
||||||
|
|
||||||
LogEntry.drop_collection()
|
|
||||||
|
|
||||||
d1 = datetime.datetime(1950, 1, 1, 0, 0, 1, 999)
|
|
||||||
log = LogEntry()
|
|
||||||
log.date = d1
|
|
||||||
log.save()
|
|
||||||
|
|
||||||
log1 = LogEntry.objects.get(date=d1)
|
|
||||||
self.assertEqual(log, log1)
|
|
||||||
|
|
||||||
# create extra 59 log entries for a total of 60
|
|
||||||
for i in range(1951, 2010):
|
|
||||||
d = datetime.datetime(i, 1, 1, 0, 0, 1, 999)
|
|
||||||
LogEntry(date=d).save()
|
|
||||||
|
|
||||||
self.assertEqual(LogEntry.objects.count(), 60)
|
|
||||||
|
|
||||||
# Test ordering
|
|
||||||
logs = LogEntry.objects.order_by("date")
|
|
||||||
i = 0
|
|
||||||
while i < 59:
|
|
||||||
self.assertTrue(logs[i].date <= logs[i + 1].date)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
logs = LogEntry.objects.order_by("-date")
|
|
||||||
i = 0
|
|
||||||
while i < 59:
|
|
||||||
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(), 30)
|
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(date__lte=datetime.datetime(1980, 1, 1))
|
|
||||||
self.assertEqual(logs.count(), 30)
|
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(
|
|
||||||
date__lte=datetime.datetime(2011, 1, 1),
|
|
||||||
date__gte=datetime.datetime(2000, 1, 1),
|
|
||||||
)
|
|
||||||
self.assertEqual(logs.count(), 10)
|
|
||||||
|
|
||||||
LogEntry.drop_collection()
|
|
||||||
|
|
||||||
# Test microsecond-level ordering/filtering
|
|
||||||
for microsecond in (99, 999, 9999, 10000):
|
|
||||||
LogEntry(
|
|
||||||
date=datetime.datetime(2015, 1, 1, 0, 0, 0, microsecond)
|
|
||||||
).save()
|
|
||||||
|
|
||||||
logs = list(LogEntry.objects.order_by('date'))
|
|
||||||
for next_idx, log in enumerate(logs[:-1], start=1):
|
|
||||||
next_log = logs[next_idx]
|
|
||||||
self.assertTrue(log.date < next_log.date)
|
|
||||||
|
|
||||||
logs = list(LogEntry.objects.order_by('-date'))
|
|
||||||
for next_idx, log in enumerate(logs[:-1], start=1):
|
|
||||||
next_log = logs[next_idx]
|
|
||||||
self.assertTrue(log.date > next_log.date)
|
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(
|
|
||||||
date__lte=datetime.datetime(2015, 1, 1, 0, 0, 0, 10000))
|
|
||||||
self.assertEqual(logs.count(), 4)
|
|
||||||
|
|
||||||
def test_no_default_value(self):
|
|
||||||
class Log(Document):
|
|
||||||
timestamp = ComplexDateTimeField()
|
|
||||||
|
|
||||||
Log.drop_collection()
|
|
||||||
|
|
||||||
log = Log()
|
|
||||||
self.assertIsNone(log.timestamp)
|
|
||||||
log.save()
|
|
||||||
|
|
||||||
fetched_log = Log.objects.with_id(log.id)
|
|
||||||
self.assertIsNone(fetched_log.timestamp)
|
|
||||||
|
|
||||||
def test_default_static_value(self):
|
|
||||||
NOW = datetime.datetime.utcnow()
|
|
||||||
class Log(Document):
|
|
||||||
timestamp = ComplexDateTimeField(default=NOW)
|
|
||||||
|
|
||||||
Log.drop_collection()
|
|
||||||
|
|
||||||
log = Log()
|
|
||||||
self.assertEqual(log.timestamp, NOW)
|
|
||||||
log.save()
|
|
||||||
|
|
||||||
fetched_log = Log.objects.with_id(log.id)
|
|
||||||
self.assertEqual(fetched_log.timestamp, NOW)
|
|
||||||
|
|
||||||
def test_default_callable(self):
|
|
||||||
NOW = datetime.datetime.utcnow()
|
|
||||||
|
|
||||||
class Log(Document):
|
|
||||||
timestamp = ComplexDateTimeField(default=datetime.datetime.utcnow)
|
|
||||||
|
|
||||||
Log.drop_collection()
|
|
||||||
|
|
||||||
log = Log()
|
|
||||||
self.assertGreaterEqual(log.timestamp, NOW)
|
|
||||||
log.save()
|
|
||||||
|
|
||||||
fetched_log = Log.objects.with_id(log.id)
|
|
||||||
self.assertGreaterEqual(fetched_log.timestamp, NOW)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
189
tests/fields/test_complex_datetime_field.py
Normal file
189
tests/fields/test_complex_datetime_field.py
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
|
import math
|
||||||
|
import itertools
|
||||||
|
import re
|
||||||
|
|
||||||
|
try:
|
||||||
|
from bson.int64 import Int64
|
||||||
|
except ImportError:
|
||||||
|
Int64 = long
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
|
|
||||||
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class ComplexDateTimeFieldTest(MongoDBTestCase):
|
||||||
|
def test_complexdatetime_storage(self):
|
||||||
|
"""Tests for complex datetime fields - which can handle
|
||||||
|
microseconds without rounding.
|
||||||
|
"""
|
||||||
|
class LogEntry(Document):
|
||||||
|
date = ComplexDateTimeField()
|
||||||
|
date_with_dots = ComplexDateTimeField(separator='.')
|
||||||
|
|
||||||
|
LogEntry.drop_collection()
|
||||||
|
|
||||||
|
# Post UTC - microseconds are rounded (down) nearest millisecond and
|
||||||
|
# dropped - with default datetimefields
|
||||||
|
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 999)
|
||||||
|
log = LogEntry()
|
||||||
|
log.date = d1
|
||||||
|
log.save()
|
||||||
|
log.reload()
|
||||||
|
self.assertEqual(log.date, d1)
|
||||||
|
|
||||||
|
# Post UTC - microseconds are rounded (down) nearest millisecond - with
|
||||||
|
# default datetimefields
|
||||||
|
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 9999)
|
||||||
|
log.date = d1
|
||||||
|
log.save()
|
||||||
|
log.reload()
|
||||||
|
self.assertEqual(log.date, d1)
|
||||||
|
|
||||||
|
# Pre UTC dates microseconds below 1000 are dropped - with default
|
||||||
|
# datetimefields
|
||||||
|
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, 999)
|
||||||
|
log.date = d1
|
||||||
|
log.save()
|
||||||
|
log.reload()
|
||||||
|
self.assertEqual(log.date, d1)
|
||||||
|
|
||||||
|
# Pre UTC microseconds above 1000 is wonky - with default datetimefields
|
||||||
|
# log.date has an invalid microsecond value so I can't construct
|
||||||
|
# a date to compare.
|
||||||
|
for i in range(1001, 3113, 33):
|
||||||
|
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, i)
|
||||||
|
log.date = d1
|
||||||
|
log.save()
|
||||||
|
log.reload()
|
||||||
|
self.assertEqual(log.date, d1)
|
||||||
|
log1 = LogEntry.objects.get(date=d1)
|
||||||
|
self.assertEqual(log, log1)
|
||||||
|
|
||||||
|
# Test string padding
|
||||||
|
microsecond = map(int, [math.pow(10, x) for x in range(6)])
|
||||||
|
mm = dd = hh = ii = ss = [1, 10]
|
||||||
|
|
||||||
|
for values in itertools.product([2014], mm, dd, hh, ii, ss, microsecond):
|
||||||
|
stored = LogEntry(date=datetime.datetime(*values)).to_mongo()['date']
|
||||||
|
self.assertTrue(re.match('^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$', stored) is not None)
|
||||||
|
|
||||||
|
# Test separator
|
||||||
|
stored = LogEntry(date_with_dots=datetime.datetime(2014, 1, 1)).to_mongo()['date_with_dots']
|
||||||
|
self.assertTrue(re.match('^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$', stored) is not None)
|
||||||
|
|
||||||
|
def test_complexdatetime_usage(self):
|
||||||
|
"""Tests for complex datetime fields - which can handle
|
||||||
|
microseconds without rounding.
|
||||||
|
"""
|
||||||
|
class LogEntry(Document):
|
||||||
|
date = ComplexDateTimeField()
|
||||||
|
|
||||||
|
LogEntry.drop_collection()
|
||||||
|
|
||||||
|
d1 = datetime.datetime(1950, 1, 1, 0, 0, 1, 999)
|
||||||
|
log = LogEntry()
|
||||||
|
log.date = d1
|
||||||
|
log.save()
|
||||||
|
|
||||||
|
log1 = LogEntry.objects.get(date=d1)
|
||||||
|
self.assertEqual(log, log1)
|
||||||
|
|
||||||
|
# create extra 59 log entries for a total of 60
|
||||||
|
for i in range(1951, 2010):
|
||||||
|
d = datetime.datetime(i, 1, 1, 0, 0, 1, 999)
|
||||||
|
LogEntry(date=d).save()
|
||||||
|
|
||||||
|
self.assertEqual(LogEntry.objects.count(), 60)
|
||||||
|
|
||||||
|
# Test ordering
|
||||||
|
logs = LogEntry.objects.order_by("date")
|
||||||
|
i = 0
|
||||||
|
while i < 59:
|
||||||
|
self.assertTrue(logs[i].date <= logs[i + 1].date)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
logs = LogEntry.objects.order_by("-date")
|
||||||
|
i = 0
|
||||||
|
while i < 59:
|
||||||
|
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(), 30)
|
||||||
|
|
||||||
|
logs = LogEntry.objects.filter(date__lte=datetime.datetime(1980, 1, 1))
|
||||||
|
self.assertEqual(logs.count(), 30)
|
||||||
|
|
||||||
|
logs = LogEntry.objects.filter(
|
||||||
|
date__lte=datetime.datetime(2011, 1, 1),
|
||||||
|
date__gte=datetime.datetime(2000, 1, 1),
|
||||||
|
)
|
||||||
|
self.assertEqual(logs.count(), 10)
|
||||||
|
|
||||||
|
LogEntry.drop_collection()
|
||||||
|
|
||||||
|
# Test microsecond-level ordering/filtering
|
||||||
|
for microsecond in (99, 999, 9999, 10000):
|
||||||
|
LogEntry(
|
||||||
|
date=datetime.datetime(2015, 1, 1, 0, 0, 0, microsecond)
|
||||||
|
).save()
|
||||||
|
|
||||||
|
logs = list(LogEntry.objects.order_by('date'))
|
||||||
|
for next_idx, log in enumerate(logs[:-1], start=1):
|
||||||
|
next_log = logs[next_idx]
|
||||||
|
self.assertTrue(log.date < next_log.date)
|
||||||
|
|
||||||
|
logs = list(LogEntry.objects.order_by('-date'))
|
||||||
|
for next_idx, log in enumerate(logs[:-1], start=1):
|
||||||
|
next_log = logs[next_idx]
|
||||||
|
self.assertTrue(log.date > next_log.date)
|
||||||
|
|
||||||
|
logs = LogEntry.objects.filter(
|
||||||
|
date__lte=datetime.datetime(2015, 1, 1, 0, 0, 0, 10000))
|
||||||
|
self.assertEqual(logs.count(), 4)
|
||||||
|
|
||||||
|
def test_no_default_value(self):
|
||||||
|
class Log(Document):
|
||||||
|
timestamp = ComplexDateTimeField()
|
||||||
|
|
||||||
|
Log.drop_collection()
|
||||||
|
|
||||||
|
log = Log()
|
||||||
|
self.assertIsNone(log.timestamp)
|
||||||
|
log.save()
|
||||||
|
|
||||||
|
fetched_log = Log.objects.with_id(log.id)
|
||||||
|
self.assertIsNone(fetched_log.timestamp)
|
||||||
|
|
||||||
|
def test_default_static_value(self):
|
||||||
|
NOW = datetime.datetime.utcnow()
|
||||||
|
class Log(Document):
|
||||||
|
timestamp = ComplexDateTimeField(default=NOW)
|
||||||
|
|
||||||
|
Log.drop_collection()
|
||||||
|
|
||||||
|
log = Log()
|
||||||
|
self.assertEqual(log.timestamp, NOW)
|
||||||
|
log.save()
|
||||||
|
|
||||||
|
fetched_log = Log.objects.with_id(log.id)
|
||||||
|
self.assertEqual(fetched_log.timestamp, NOW)
|
||||||
|
|
||||||
|
def test_default_callable(self):
|
||||||
|
NOW = datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
class Log(Document):
|
||||||
|
timestamp = ComplexDateTimeField(default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
Log.drop_collection()
|
||||||
|
|
||||||
|
log = Log()
|
||||||
|
self.assertGreaterEqual(log.timestamp, NOW)
|
||||||
|
log.save()
|
||||||
|
|
||||||
|
fetched_log = Log.objects.with_id(log.id)
|
||||||
|
self.assertGreaterEqual(fetched_log.timestamp, NOW)
|
184
tests/fields/test_date_field.py
Normal file
184
tests/fields/test_date_field.py
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
|
import unittest
|
||||||
|
import uuid
|
||||||
|
import math
|
||||||
|
import itertools
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from nose.plugins.skip import SkipTest
|
||||||
|
import six
|
||||||
|
|
||||||
|
try:
|
||||||
|
import dateutil
|
||||||
|
except ImportError:
|
||||||
|
dateutil = None
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from bson import Binary, DBRef, ObjectId, SON
|
||||||
|
try:
|
||||||
|
from bson.int64 import Int64
|
||||||
|
except ImportError:
|
||||||
|
Int64 = long
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
|
from mongoengine.connection import get_db
|
||||||
|
from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList,
|
||||||
|
_document_registry, LazyReference)
|
||||||
|
|
||||||
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestDateField(MongoDBTestCase):
|
||||||
|
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_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_today(self):
|
||||||
|
"""Ensure that default field values are used when creating
|
||||||
|
a document.
|
||||||
|
"""
|
||||||
|
class Person(Document):
|
||||||
|
day = DateField(default=datetime.date.today)
|
||||||
|
|
||||||
|
person = Person()
|
||||||
|
person.validate()
|
||||||
|
self.assertEqual(person.day, person.day)
|
||||||
|
self.assertEqual(person.day, datetime.date.today())
|
||||||
|
self.assertEqual(person._data['day'], person.day)
|
||||||
|
|
||||||
|
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_regular_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_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)
|
208
tests/fields/test_datetime_field.py
Normal file
208
tests/fields/test_datetime_field.py
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
|
import six
|
||||||
|
|
||||||
|
try:
|
||||||
|
import dateutil
|
||||||
|
except ImportError:
|
||||||
|
dateutil = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
from bson.int64 import Int64
|
||||||
|
except ImportError:
|
||||||
|
Int64 = long
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
|
from mongoengine import connection
|
||||||
|
|
||||||
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestDateTimeField(MongoDBTestCase):
|
||||||
|
def test_datetime_from_empty_string(self):
|
||||||
|
"""
|
||||||
|
Ensure an exception is raised when trying to
|
||||||
|
cast an empty string to datetime.
|
||||||
|
"""
|
||||||
|
class MyDoc(Document):
|
||||||
|
dt = DateTimeField()
|
||||||
|
|
||||||
|
md = MyDoc(dt='')
|
||||||
|
self.assertRaises(ValidationError, md.save)
|
||||||
|
|
||||||
|
def test_datetime_from_whitespace_string(self):
|
||||||
|
"""
|
||||||
|
Ensure an exception is raised when trying to
|
||||||
|
cast a whitespace-only string to datetime.
|
||||||
|
"""
|
||||||
|
class MyDoc(Document):
|
||||||
|
dt = DateTimeField()
|
||||||
|
|
||||||
|
md = MyDoc(dt=' ')
|
||||||
|
self.assertRaises(ValidationError, md.save)
|
||||||
|
|
||||||
|
def test_default_value_utcnow(self):
|
||||||
|
"""Ensure that default field values are used when creating
|
||||||
|
a document.
|
||||||
|
"""
|
||||||
|
class Person(Document):
|
||||||
|
created = DateTimeField(default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
utcnow = datetime.datetime.utcnow()
|
||||||
|
person = Person()
|
||||||
|
person.validate()
|
||||||
|
person_created_t0 = person.created
|
||||||
|
self.assertLess(person.created - utcnow, datetime.timedelta(seconds=1))
|
||||||
|
self.assertEqual(person_created_t0, person.created) # make sure it does not change
|
||||||
|
self.assertEqual(person._data['created'], person.created)
|
||||||
|
|
||||||
|
def test_handling_microseconds(self):
|
||||||
|
"""Tests showing pymongo datetime fields handling of microseconds.
|
||||||
|
Microseconds are rounded to the nearest millisecond and pre UTC
|
||||||
|
handling is wonky.
|
||||||
|
|
||||||
|
See: http://api.mongodb.org/python/current/api/bson/son.html#dt
|
||||||
|
"""
|
||||||
|
class LogEntry(Document):
|
||||||
|
date = DateTimeField()
|
||||||
|
|
||||||
|
LogEntry.drop_collection()
|
||||||
|
|
||||||
|
# Test can save dates
|
||||||
|
log = LogEntry()
|
||||||
|
log.date = datetime.date.today()
|
||||||
|
log.save()
|
||||||
|
log.reload()
|
||||||
|
self.assertEqual(log.date.date(), datetime.date.today())
|
||||||
|
|
||||||
|
# Post UTC - microseconds are rounded (down) nearest millisecond and
|
||||||
|
# dropped
|
||||||
|
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.assertNotEqual(log.date, d1)
|
||||||
|
self.assertEqual(log.date, d2)
|
||||||
|
|
||||||
|
# Post UTC - microseconds are rounded (down) nearest millisecond
|
||||||
|
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.assertNotEqual(log.date, d1)
|
||||||
|
self.assertEqual(log.date, d2)
|
||||||
|
|
||||||
|
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.assertNotEqual(log.date, d1)
|
||||||
|
self.assertEqual(log.date, d2)
|
||||||
|
|
||||||
|
def test_regular_usage(self):
|
||||||
|
"""Tests for regular datetime fields"""
|
||||||
|
class LogEntry(Document):
|
||||||
|
date = DateTimeField()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
logs = LogEntry.objects.filter(date__lte=datetime.datetime(1980, 1, 1))
|
||||||
|
self.assertEqual(logs.count(), 10)
|
||||||
|
|
||||||
|
logs = LogEntry.objects.filter(
|
||||||
|
date__lte=datetime.datetime(1980, 1, 1),
|
||||||
|
date__gte=datetime.datetime(1975, 1, 1),
|
||||||
|
)
|
||||||
|
self.assertEqual(logs.count(), 5)
|
||||||
|
|
||||||
|
def test_datetime_validation(self):
|
||||||
|
"""Ensure that invalid values cannot be assigned to datetime
|
||||||
|
fields.
|
||||||
|
"""
|
||||||
|
class LogEntry(Document):
|
||||||
|
time = DateTimeField()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDateTimeTzAware(MongoDBTestCase):
|
||||||
|
def test_datetime_tz_aware_mark_as_changed(self):
|
||||||
|
# Reset the connections
|
||||||
|
connection._connection_settings = {}
|
||||||
|
connection._connections = {}
|
||||||
|
connection._dbs = {}
|
||||||
|
|
||||||
|
connect(db='mongoenginetest', tz_aware=True)
|
||||||
|
|
||||||
|
class LogEntry(Document):
|
||||||
|
time = DateTimeField()
|
||||||
|
|
||||||
|
LogEntry.drop_collection()
|
||||||
|
|
||||||
|
LogEntry(time=datetime.datetime(2013, 1, 1, 0, 0, 0)).save()
|
||||||
|
|
||||||
|
log = LogEntry.objects.first()
|
||||||
|
log.time = datetime.datetime(2013, 1, 1, 0, 0, 0)
|
||||||
|
self.assertEqual(['time'], log._changed_fields)
|
@ -2234,6 +2234,19 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
bar.reload()
|
bar.reload()
|
||||||
self.assertEqual(len(bar.foos), 0)
|
self.assertEqual(len(bar.foos), 0)
|
||||||
|
|
||||||
|
def test_update_one_check_return_with_full_result(self):
|
||||||
|
class BlogTag(Document):
|
||||||
|
name = StringField(required=True)
|
||||||
|
|
||||||
|
BlogTag.drop_collection()
|
||||||
|
|
||||||
|
BlogTag(name='garbage').save()
|
||||||
|
default_update = BlogTag.objects.update_one(name='new')
|
||||||
|
self.assertEqual(default_update, 1)
|
||||||
|
|
||||||
|
full_result_update = BlogTag.objects.update_one(name='new', full_result=True)
|
||||||
|
self.assertIsInstance(full_result_update, UpdateResult)
|
||||||
|
|
||||||
def test_update_one_pop_generic_reference(self):
|
def test_update_one_pop_generic_reference(self):
|
||||||
|
|
||||||
class BlogTag(Document):
|
class BlogTag(Document):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user