diff --git a/docs/changelog.rst b/docs/changelog.rst index 1f52dfd4..08091acb 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,7 @@ Changelog Changes in 0.6.X ================ +- Added Binary support to UUID (MongoEngine/mongoengine#47) - Fixed MapField lookup for fields without declared lookups (MongoEngine/mongoengine#46) - Fixed BinaryField python value issue (MongoEngine/mongoengine#48) - Fixed SequenceField non numeric value lookup (MongoEngine/mongoengine#41) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 08bbc44c..82689929 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -4,9 +4,9 @@ import decimal import gridfs import re import uuid +import warnings from bson import Binary, DBRef, SON, ObjectId - from base import (BaseField, ComplexBaseField, ObjectIdField, ValidationError, get_document, BaseDocument) from queryset import DO_NOTHING, QuerySet @@ -1308,17 +1308,40 @@ class UUIDField(BaseField): .. versionadded:: 0.6 """ + _binary = None - def __init__(self, **kwargs): + def __init__(self, binary=None, **kwargs): + """ + Store UUID data in the database + + :param binary: (optional) boolean store as binary. + + .. versionchanged:: 0.6.19 + """ + if binary is None: + binary = False + msg = ("UUIDFields will soon default to store as binary, please " + "configure binary=False if you wish to store as a string") + warnings.warn(msg, FutureWarning) + self._binary = binary super(UUIDField, self).__init__(**kwargs) def to_python(self, value): - if not isinstance(value, basestring): - value = unicode(value) - return uuid.UUID(value) + if not self.binary: + if not isinstance(value, basestring): + value = unicode(value) + return uuid.UUID(value) + return value def to_mongo(self, value): - return unicode(value) + if not self._binary: + return unicode(value) + return value + + def prepare_query_value(self, op, value): + if value is None: + return None + return self.to_mongo(value) def validate(self, value): if not isinstance(value, uuid.UUID): diff --git a/tests/test_fields.py b/tests/test_fields.py index d3430031..c4013c10 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -272,25 +272,54 @@ class FieldTest(unittest.TestCase): person.admin = 'Yes' self.assertRaises(ValidationError, person.validate) - def test_uuid_validation(self): - """Ensure that invalid values cannot be assigned to UUID fields. + def test_uuid_field_string(self): + """Test UUID fields storing as String """ class Person(Document): - api_key = UUIDField() + api_key = UUIDField(binary=False) + + Person.drop_collection() + + uu = uuid.uuid4() + Person(api_key=uu).save() + self.assertEqual(1, Person.objects(api_key=uu).count()) person = Person() - # any uuid type is valid - person.api_key = uuid.uuid4() - person.validate() - person.api_key = uuid.uuid1() - person.validate() + valid = (uuid.uuid4(), uuid.uuid1()) + for api_key in valid: + person.api_key = api_key + person.validate() + + invalid = ('9d159858-549b-4975-9f98-dd2f987c113g', + '9d159858-549b-4975-9f98-dd2f987c113') + for api_key in invalid: + person.api_key = api_key + self.assertRaises(ValidationError, person.validate) + + def test_uuid_field_binary(self): + """Test UUID fields storing as Binary object + """ + class Person(Document): + api_key = UUIDField(binary=True) + + Person.drop_collection() + + uu = uuid.uuid4() + Person(api_key=uu).save() + self.assertEqual(1, Person.objects(api_key=uu).count()) + + person = Person() + valid = (uuid.uuid4(), uuid.uuid1()) + for api_key in valid: + person.api_key = api_key + person.validate() + + invalid = ('9d159858-549b-4975-9f98-dd2f987c113g', + '9d159858-549b-4975-9f98-dd2f987c113') + for api_key in invalid: + person.api_key = api_key + self.assertRaises(ValidationError, person.validate) - # last g cannot belong to an hex number - person.api_key = '9d159858-549b-4975-9f98-dd2f987c113g' - self.assertRaises(ValidationError, person.validate) - # short strings don't validate - person.api_key = '9d159858-549b-4975-9f98-dd2f987c113' - self.assertRaises(ValidationError, person.validate) def test_datetime_validation(self): """Ensure that invalid values cannot be assigned to datetime fields. @@ -937,7 +966,7 @@ class FieldTest(unittest.TestCase): visited = MapField(DateTimeField()) Log.drop_collection() - Log(name="wilson", visited={'friends': datetime.now()}).save() + Log(name="wilson", visited={'friends': datetime.datetime.now()}).save() self.assertEqual(1, Log.objects( visited__friends__exists=True).count())