Merge branch 'master' into dev
This commit is contained in:
commit
b87ef982f6
1
AUTHORS
1
AUTHORS
@ -114,3 +114,4 @@ that much better:
|
||||
* Jaime Irurzun
|
||||
* Alexandre González
|
||||
* Thomas Steinacher
|
||||
* Tommi Komulainen
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2010 Harry Marr
|
||||
Copyright (c) 2009-2012 See AUTHORS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
@ -2,6 +2,15 @@
|
||||
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)
|
||||
- Fixed queryset manager issue (MongoEngine/mongoengine#52)
|
||||
- Fixed FileField comparision (hmarr/mongoengine#547)
|
||||
|
||||
Changes in 0.6.18
|
||||
=================
|
||||
|
@ -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
|
||||
@ -845,12 +845,9 @@ class BinaryField(BaseField):
|
||||
def to_mongo(self, value):
|
||||
return Binary(value)
|
||||
|
||||
def to_python(self, value):
|
||||
return "%s" % value
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, basestring):
|
||||
self.error('BinaryField only accepts string values')
|
||||
if not isinstance(value, (basestring, Binary)):
|
||||
self.error('BinaryField only accepts string or bson Binary values')
|
||||
|
||||
if self.max_bytes is not None and len(value) > self.max_bytes:
|
||||
self.error('Binary value is too long')
|
||||
@ -907,6 +904,8 @@ class GridFSProxy(object):
|
||||
return '<%s: %s>' % (self.__class__.__name__, self.grid_id)
|
||||
|
||||
def __cmp__(self, other):
|
||||
if not isinstance(other, GridFSProxy):
|
||||
return -1
|
||||
return cmp((self.grid_id, self.collection_name, self.db_alias),
|
||||
(other.grid_id, other.collection_name, other.db_alias))
|
||||
|
||||
@ -1289,7 +1288,7 @@ class SequenceField(IntField):
|
||||
instance._data[self.name] = value
|
||||
instance._mark_as_changed(self.name)
|
||||
|
||||
return value
|
||||
return int(value) if value else None
|
||||
|
||||
def __set__(self, instance, value):
|
||||
|
||||
@ -1309,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 self.binary:
|
||||
if not isinstance(value, basestring):
|
||||
value = unicode(value)
|
||||
return uuid.UUID(value)
|
||||
return value
|
||||
|
||||
def to_mongo(self, 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):
|
||||
|
@ -641,7 +641,7 @@ class QuerySet(object):
|
||||
from mongoengine.fields import ReferenceField, GenericReferenceField
|
||||
if isinstance(field, (ReferenceField, GenericReferenceField)):
|
||||
raise InvalidQueryError('Cannot perform join in mongoDB: %s' % '__'.join(parts))
|
||||
if getattr(field, 'field', None):
|
||||
if hasattr(getattr(field, 'field', None), 'lookup_member'):
|
||||
new_field = field.field.lookup_member(field_name)
|
||||
else:
|
||||
# Look up subfield on the previous field
|
||||
@ -1886,10 +1886,10 @@ class QuerySetManager(object):
|
||||
queryset_class = owner._meta['queryset_class'] or QuerySet
|
||||
queryset = queryset_class(owner, owner._get_collection())
|
||||
if self.get_queryset:
|
||||
var_names = self.get_queryset.func_code.co_varnames
|
||||
if len(var_names) == 1:
|
||||
arg_count = self.get_queryset.func_code.co_argcount
|
||||
if arg_count == 1:
|
||||
queryset = self.get_queryset(queryset)
|
||||
elif len(var_names) == 2:
|
||||
elif arg_count == 2:
|
||||
queryset = self.get_queryset(owner, queryset)
|
||||
else:
|
||||
queryset = partial(self.get_queryset, owner, queryset)
|
||||
|
@ -6,6 +6,7 @@ import StringIO
|
||||
import tempfile
|
||||
import gridfs
|
||||
|
||||
from bson import Binary
|
||||
from decimal import Decimal
|
||||
|
||||
from mongoengine import *
|
||||
@ -271,26 +272,55 @@ 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()
|
||||
valid = (uuid.uuid4(), uuid.uuid1())
|
||||
for api_key in valid:
|
||||
person.api_key = api_key
|
||||
person.validate()
|
||||
|
||||
# last g cannot belong to an hex number
|
||||
person.api_key = '9d159858-549b-4975-9f98-dd2f987c113g'
|
||||
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)
|
||||
# short strings don't validate
|
||||
person.api_key = '9d159858-549b-4975-9f98-dd2f987c113'
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def test_datetime_validation(self):
|
||||
"""Ensure that invalid values cannot be assigned to datetime fields.
|
||||
"""
|
||||
@ -928,6 +958,19 @@ class FieldTest(unittest.TestCase):
|
||||
doc = self.db.test.find_one()
|
||||
self.assertEqual(doc['x']['DICTIONARY_KEY']['i'], 2)
|
||||
|
||||
def test_map_field_lookup(self):
|
||||
"""Ensure MapField lookups succeed on Fields without a lookup method"""
|
||||
|
||||
class Log(Document):
|
||||
name = StringField()
|
||||
visited = MapField(DateTimeField())
|
||||
|
||||
Log.drop_collection()
|
||||
Log(name="wilson", visited={'friends': datetime.datetime.now()}).save()
|
||||
|
||||
self.assertEqual(1, Log.objects(
|
||||
visited__friends__exists=True).count())
|
||||
|
||||
def test_embedded_db_field(self):
|
||||
|
||||
class Embedded(EmbeddedDocument):
|
||||
@ -1428,7 +1471,7 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
attachment_1 = Attachment.objects().first()
|
||||
self.assertEqual(MIME_TYPE, attachment_1.content_type)
|
||||
self.assertEqual(BLOB, attachment_1.blob)
|
||||
self.assertEqual(BLOB, str(attachment_1.blob))
|
||||
|
||||
Attachment.drop_collection()
|
||||
|
||||
@ -1455,7 +1498,7 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
attachment_required = AttachmentRequired()
|
||||
self.assertRaises(ValidationError, attachment_required.validate)
|
||||
attachment_required.blob = '\xe6\x00\xc4\xff\x07'
|
||||
attachment_required.blob = Binary('\xe6\x00\xc4\xff\x07')
|
||||
attachment_required.validate()
|
||||
|
||||
attachment_size_limit = AttachmentSizeLimit(blob='\xe6\x00\xc4\xff\x07')
|
||||
@ -1467,6 +1510,18 @@ class FieldTest(unittest.TestCase):
|
||||
AttachmentRequired.drop_collection()
|
||||
AttachmentSizeLimit.drop_collection()
|
||||
|
||||
def test_binary_field_primary(self):
|
||||
|
||||
class Attachment(Document):
|
||||
id = BinaryField(primary_key=True)
|
||||
|
||||
Attachment.drop_collection()
|
||||
|
||||
att = Attachment(id=uuid.uuid4().bytes).save()
|
||||
att.delete()
|
||||
|
||||
self.assertEqual(0, Attachment.objects.count())
|
||||
|
||||
def test_choices_validation(self):
|
||||
"""Ensure that value is in a container of allowed values.
|
||||
"""
|
||||
@ -1567,13 +1622,13 @@ class FieldTest(unittest.TestCase):
|
||||
"""Ensure that file fields can be written to and their data retrieved
|
||||
"""
|
||||
class PutFile(Document):
|
||||
file = FileField()
|
||||
the_file = FileField()
|
||||
|
||||
class StreamFile(Document):
|
||||
file = FileField()
|
||||
the_file = FileField()
|
||||
|
||||
class SetFile(Document):
|
||||
file = FileField()
|
||||
the_file = FileField()
|
||||
|
||||
text = 'Hello, World!'
|
||||
more_text = 'Foo Bar'
|
||||
@ -1584,14 +1639,14 @@ class FieldTest(unittest.TestCase):
|
||||
SetFile.drop_collection()
|
||||
|
||||
putfile = PutFile()
|
||||
putfile.file.put(text, content_type=content_type)
|
||||
putfile.the_file.put(text, content_type=content_type)
|
||||
putfile.save()
|
||||
putfile.validate()
|
||||
result = PutFile.objects.first()
|
||||
self.assertTrue(putfile == result)
|
||||
self.assertEquals(result.file.read(), text)
|
||||
self.assertEquals(result.file.content_type, content_type)
|
||||
result.file.delete() # Remove file from GridFS
|
||||
self.assertEquals(result.the_file.read(), text)
|
||||
self.assertEquals(result.the_file.content_type, content_type)
|
||||
result.the_file.delete() # Remove file from GridFS
|
||||
PutFile.objects.delete()
|
||||
|
||||
# Ensure file-like objects are stored
|
||||
@ -1599,53 +1654,53 @@ class FieldTest(unittest.TestCase):
|
||||
putstring = StringIO.StringIO()
|
||||
putstring.write(text)
|
||||
putstring.seek(0)
|
||||
putfile.file.put(putstring, content_type=content_type)
|
||||
putfile.the_file.put(putstring, content_type=content_type)
|
||||
putfile.save()
|
||||
putfile.validate()
|
||||
result = PutFile.objects.first()
|
||||
self.assertTrue(putfile == result)
|
||||
self.assertEquals(result.file.read(), text)
|
||||
self.assertEquals(result.file.content_type, content_type)
|
||||
result.file.delete()
|
||||
self.assertEquals(result.the_file.read(), text)
|
||||
self.assertEquals(result.the_file.content_type, content_type)
|
||||
result.the_file.delete()
|
||||
|
||||
streamfile = StreamFile()
|
||||
streamfile.file.new_file(content_type=content_type)
|
||||
streamfile.file.write(text)
|
||||
streamfile.file.write(more_text)
|
||||
streamfile.file.close()
|
||||
streamfile.the_file.new_file(content_type=content_type)
|
||||
streamfile.the_file.write(text)
|
||||
streamfile.the_file.write(more_text)
|
||||
streamfile.the_file.close()
|
||||
streamfile.save()
|
||||
streamfile.validate()
|
||||
result = StreamFile.objects.first()
|
||||
self.assertTrue(streamfile == result)
|
||||
self.assertEquals(result.file.read(), text + more_text)
|
||||
self.assertEquals(result.file.content_type, content_type)
|
||||
result.file.seek(0)
|
||||
self.assertEquals(result.file.tell(), 0)
|
||||
self.assertEquals(result.file.read(len(text)), text)
|
||||
self.assertEquals(result.file.tell(), len(text))
|
||||
self.assertEquals(result.file.read(len(more_text)), more_text)
|
||||
self.assertEquals(result.file.tell(), len(text + more_text))
|
||||
result.file.delete()
|
||||
self.assertEquals(result.the_file.read(), text + more_text)
|
||||
self.assertEquals(result.the_file.content_type, content_type)
|
||||
result.the_file.seek(0)
|
||||
self.assertEquals(result.the_file.tell(), 0)
|
||||
self.assertEquals(result.the_file.read(len(text)), text)
|
||||
self.assertEquals(result.the_file.tell(), len(text))
|
||||
self.assertEquals(result.the_file.read(len(more_text)), more_text)
|
||||
self.assertEquals(result.the_file.tell(), len(text + more_text))
|
||||
result.the_file.delete()
|
||||
|
||||
# Ensure deleted file returns None
|
||||
self.assertTrue(result.file.read() == None)
|
||||
self.assertTrue(result.the_file.read() == None)
|
||||
|
||||
setfile = SetFile()
|
||||
setfile.file = text
|
||||
setfile.the_file = text
|
||||
setfile.save()
|
||||
setfile.validate()
|
||||
result = SetFile.objects.first()
|
||||
self.assertTrue(setfile == result)
|
||||
self.assertEquals(result.file.read(), text)
|
||||
self.assertEquals(result.the_file.read(), text)
|
||||
|
||||
# Try replacing file with new one
|
||||
result.file.replace(more_text)
|
||||
result.the_file.replace(more_text)
|
||||
result.save()
|
||||
result.validate()
|
||||
result = SetFile.objects.first()
|
||||
self.assertTrue(setfile == result)
|
||||
self.assertEquals(result.file.read(), more_text)
|
||||
result.file.delete()
|
||||
self.assertEquals(result.the_file.read(), more_text)
|
||||
result.the_file.delete()
|
||||
|
||||
PutFile.drop_collection()
|
||||
StreamFile.drop_collection()
|
||||
@ -1653,7 +1708,7 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
# Make sure FileField is optional and not required
|
||||
class DemoFile(Document):
|
||||
file = FileField()
|
||||
the_file = FileField()
|
||||
DemoFile.objects.create()
|
||||
|
||||
|
||||
@ -1704,20 +1759,20 @@ class FieldTest(unittest.TestCase):
|
||||
"""
|
||||
class TestFile(Document):
|
||||
name = StringField()
|
||||
file = FileField()
|
||||
the_file = FileField()
|
||||
|
||||
# First instance
|
||||
testfile = TestFile()
|
||||
testfile.name = "Hello, World!"
|
||||
testfile.file.put('Hello, World!')
|
||||
testfile.save()
|
||||
test_file = TestFile()
|
||||
test_file.name = "Hello, World!"
|
||||
test_file.the_file.put('Hello, World!')
|
||||
test_file.save()
|
||||
|
||||
# Second instance
|
||||
testfiledupe = TestFile()
|
||||
data = testfiledupe.file.read() # Should be None
|
||||
test_file_dupe = TestFile()
|
||||
data = test_file_dupe.the_file.read() # Should be None
|
||||
|
||||
self.assertTrue(testfile.name != testfiledupe.name)
|
||||
self.assertTrue(testfile.file.read() != data)
|
||||
self.assertTrue(test_file.name != test_file_dupe.name)
|
||||
self.assertTrue(test_file.the_file.read() != data)
|
||||
|
||||
TestFile.drop_collection()
|
||||
|
||||
@ -1725,17 +1780,25 @@ class FieldTest(unittest.TestCase):
|
||||
"""Ensure that a boolean test of a FileField indicates its presence
|
||||
"""
|
||||
class TestFile(Document):
|
||||
file = FileField()
|
||||
the_file = FileField()
|
||||
|
||||
testfile = TestFile()
|
||||
self.assertFalse(bool(testfile.file))
|
||||
testfile.file = 'Hello, World!'
|
||||
testfile.file.content_type = 'text/plain'
|
||||
testfile.save()
|
||||
self.assertTrue(bool(testfile.file))
|
||||
test_file = TestFile()
|
||||
self.assertFalse(bool(test_file.the_file))
|
||||
test_file.the_file = 'Hello, World!'
|
||||
test_file.the_file.content_type = 'text/plain'
|
||||
test_file.save()
|
||||
self.assertTrue(bool(test_file.the_file))
|
||||
|
||||
TestFile.drop_collection()
|
||||
|
||||
def test_file_cmp(self):
|
||||
"""Test comparing against other types"""
|
||||
class TestFile(Document):
|
||||
the_file = FileField()
|
||||
|
||||
test_file = TestFile()
|
||||
self.assertFalse(test_file.the_file in [{"test": 1}])
|
||||
|
||||
def test_image_field(self):
|
||||
|
||||
class TestImage(Document):
|
||||
@ -1799,30 +1862,30 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
|
||||
def test_file_multidb(self):
|
||||
register_connection('testfiles', 'testfiles')
|
||||
register_connection('test_files', 'test_files')
|
||||
class TestFile(Document):
|
||||
name = StringField()
|
||||
file = FileField(db_alias="testfiles",
|
||||
the_file = FileField(db_alias="test_files",
|
||||
collection_name="macumba")
|
||||
|
||||
TestFile.drop_collection()
|
||||
|
||||
# delete old filesystem
|
||||
get_db("testfiles").macumba.files.drop()
|
||||
get_db("testfiles").macumba.chunks.drop()
|
||||
get_db("test_files").macumba.files.drop()
|
||||
get_db("test_files").macumba.chunks.drop()
|
||||
|
||||
# First instance
|
||||
testfile = TestFile()
|
||||
testfile.name = "Hello, World!"
|
||||
testfile.file.put('Hello, World!',
|
||||
test_file = TestFile()
|
||||
test_file.name = "Hello, World!"
|
||||
test_file.the_file.put('Hello, World!',
|
||||
name="hello.txt")
|
||||
testfile.save()
|
||||
test_file.save()
|
||||
|
||||
data = get_db("testfiles").macumba.files.find_one()
|
||||
data = get_db("test_files").macumba.files.find_one()
|
||||
self.assertEquals(data.get('name'), 'hello.txt')
|
||||
|
||||
testfile = TestFile.objects.first()
|
||||
self.assertEquals(testfile.file.read(),
|
||||
test_file = TestFile.objects.first()
|
||||
self.assertEquals(test_file.the_file.read(),
|
||||
'Hello, World!')
|
||||
|
||||
def test_geo_indexes(self):
|
||||
|
@ -2287,7 +2287,8 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
@queryset_manager
|
||||
def objects(cls, qryset):
|
||||
return qryset(deleted=False)
|
||||
opts = {"deleted": False}
|
||||
return qryset(**opts)
|
||||
|
||||
@queryset_manager
|
||||
def music_posts(doc_cls, queryset, deleted=False):
|
||||
|
Loading…
x
Reference in New Issue
Block a user