Merge branch 'master' into 0.8
Conflicts: AUTHORS docs/changelog.rst mongoengine/__init__.py mongoengine/base.py mongoengine/fields.py python-mongoengine.spec tests/test_document.py tests/test_fields.py tests/test_queryset.py
This commit is contained in:
commit
3425264077
6
AUTHORS
6
AUTHORS
@ -106,7 +106,7 @@ that much better:
|
|||||||
* Adam Reeve
|
* Adam Reeve
|
||||||
* Anthony Nemitz
|
* Anthony Nemitz
|
||||||
* deignacio
|
* deignacio
|
||||||
* shaunduncan
|
* Shaun Duncan
|
||||||
* Meir Kriheli
|
* Meir Kriheli
|
||||||
* Andrey Fedoseev
|
* Andrey Fedoseev
|
||||||
* aparajita
|
* aparajita
|
||||||
@ -125,3 +125,7 @@ that much better:
|
|||||||
* dimonb
|
* dimonb
|
||||||
* Garry Polley
|
* Garry Polley
|
||||||
* James Slagle
|
* James Slagle
|
||||||
|
* Adrian Scott
|
||||||
|
* Peter Teichman
|
||||||
|
* Jakub Kot
|
||||||
|
* Jorge Bastida
|
||||||
|
@ -14,7 +14,7 @@ About
|
|||||||
MongoEngine is a Python Object-Document Mapper for working with MongoDB.
|
MongoEngine is a Python Object-Document Mapper for working with MongoDB.
|
||||||
Documentation available at http://mongoengine-odm.rtfd.org - there is currently
|
Documentation available at http://mongoengine-odm.rtfd.org - there is currently
|
||||||
a `tutorial <http://readthedocs.org/docs/mongoengine-odm/en/latest/tutorial.html>`_, a `user guide
|
a `tutorial <http://readthedocs.org/docs/mongoengine-odm/en/latest/tutorial.html>`_, a `user guide
|
||||||
<http://readthedocs.org/docs/mongoengine-odm/en/latest/userguide.html>`_ and an `API reference
|
<https://mongoengine-odm.readthedocs.org/en/latest/guide/index.html>`_ and an `API reference
|
||||||
<http://readthedocs.org/docs/mongoengine-odm/en/latest/apireference.html>`_.
|
<http://readthedocs.org/docs/mongoengine-odm/en/latest/apireference.html>`_.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
|
@ -20,6 +20,20 @@ Changes in 0.8
|
|||||||
- Inheritance is off by default (MongoEngine/mongoengine#122)
|
- Inheritance is off by default (MongoEngine/mongoengine#122)
|
||||||
- Remove _types and just use _cls for inheritance (MongoEngine/mongoengine#148)
|
- Remove _types and just use _cls for inheritance (MongoEngine/mongoengine#148)
|
||||||
|
|
||||||
|
Changes in 0.7.9
|
||||||
|
================
|
||||||
|
- Better fix handling for old style _types
|
||||||
|
- Embedded SequenceFields follow collection naming convention
|
||||||
|
|
||||||
|
Changes in 0.7.8
|
||||||
|
================
|
||||||
|
- Fix sequence fields in embedded documents (MongoEngine/mongoengine#166)
|
||||||
|
- Fix query chaining with .order_by() (MongoEngine/mongoengine#176)
|
||||||
|
- Added optional encoding and collection config for Django sessions (MongoEngine/mongoengine#180, MongoEngine/mongoengine#181, MongoEngine/mongoengine#183)
|
||||||
|
- Fixed EmailField so can add extra validation (MongoEngine/mongoengine#173, MongoEngine/mongoengine#174, MongoEngine/mongoengine#187)
|
||||||
|
- Fixed bulk inserts can now handle custom pk's (MongoEngine/mongoengine#192)
|
||||||
|
- Added as_pymongo method to return raw or cast results from pymongo (MongoEngine/mongoengine#193)
|
||||||
|
|
||||||
Changes in 0.7.7
|
Changes in 0.7.7
|
||||||
================
|
================
|
||||||
- Fix handling for old style _types
|
- Fix handling for old style _types
|
||||||
|
@ -9,10 +9,12 @@ _document_registry = {}
|
|||||||
|
|
||||||
def get_document(name):
|
def get_document(name):
|
||||||
doc = _document_registry.get(name, None)
|
doc = _document_registry.get(name, None)
|
||||||
if not doc and '.' in name:
|
if not doc:
|
||||||
# Possible old style name
|
# Possible old style name
|
||||||
end = name.split('.')[-1]
|
single_end = name.split('.')[-1]
|
||||||
possible_match = [k for k in _document_registry.keys() if k == end]
|
compound_end = '.%s' % single_end
|
||||||
|
possible_match = [k for k in _document_registry.keys()
|
||||||
|
if k.endswith(compound_end) or k == single_end]
|
||||||
if len(possible_match) == 1:
|
if len(possible_match) == 1:
|
||||||
doc = _document_registry.get(possible_match.pop(), None)
|
doc = _document_registry.get(possible_match.pop(), None)
|
||||||
if not doc:
|
if not doc:
|
||||||
|
@ -15,13 +15,23 @@ MONGOENGINE_SESSION_DB_ALIAS = getattr(
|
|||||||
settings, 'MONGOENGINE_SESSION_DB_ALIAS',
|
settings, 'MONGOENGINE_SESSION_DB_ALIAS',
|
||||||
DEFAULT_CONNECTION_NAME)
|
DEFAULT_CONNECTION_NAME)
|
||||||
|
|
||||||
|
# a setting for the name of the collection used to store sessions
|
||||||
|
MONGOENGINE_SESSION_COLLECTION = getattr(
|
||||||
|
settings, 'MONGOENGINE_SESSION_COLLECTION',
|
||||||
|
'django_session')
|
||||||
|
|
||||||
|
# a setting for whether session data is stored encoded or not
|
||||||
|
MONGOENGINE_SESSION_DATA_ENCODE = getattr(
|
||||||
|
settings, 'MONGOENGINE_SESSION_DATA_ENCODE',
|
||||||
|
True)
|
||||||
|
|
||||||
class MongoSession(Document):
|
class MongoSession(Document):
|
||||||
session_key = fields.StringField(primary_key=True, max_length=40)
|
session_key = fields.StringField(primary_key=True, max_length=40)
|
||||||
session_data = fields.StringField()
|
session_data = fields.StringField() if MONGOENGINE_SESSION_DATA_ENCODE \
|
||||||
|
else fields.DictField()
|
||||||
expire_date = fields.DateTimeField()
|
expire_date = fields.DateTimeField()
|
||||||
|
|
||||||
meta = {'collection': 'django_session',
|
meta = {'collection': MONGOENGINE_SESSION_COLLECTION,
|
||||||
'db_alias': MONGOENGINE_SESSION_DB_ALIAS,
|
'db_alias': MONGOENGINE_SESSION_DB_ALIAS,
|
||||||
'allow_inheritance': False}
|
'allow_inheritance': False}
|
||||||
|
|
||||||
@ -34,7 +44,10 @@ class SessionStore(SessionBase):
|
|||||||
try:
|
try:
|
||||||
s = MongoSession.objects(session_key=self.session_key,
|
s = MongoSession.objects(session_key=self.session_key,
|
||||||
expire_date__gt=datetime.now())[0]
|
expire_date__gt=datetime.now())[0]
|
||||||
return self.decode(force_unicode(s.session_data))
|
if MONGOENGINE_SESSION_DATA_ENCODE:
|
||||||
|
return self.decode(force_unicode(s.session_data))
|
||||||
|
else:
|
||||||
|
return s.session_data
|
||||||
except (IndexError, SuspiciousOperation):
|
except (IndexError, SuspiciousOperation):
|
||||||
self.create()
|
self.create()
|
||||||
return {}
|
return {}
|
||||||
@ -57,7 +70,10 @@ class SessionStore(SessionBase):
|
|||||||
if self.session_key is None:
|
if self.session_key is None:
|
||||||
self._session_key = self._get_new_session_key()
|
self._session_key = self._get_new_session_key()
|
||||||
s = MongoSession(session_key=self.session_key)
|
s = MongoSession(session_key=self.session_key)
|
||||||
s.session_data = self.encode(self._get_session(no_load=must_create))
|
if MONGOENGINE_SESSION_DATA_ENCODE:
|
||||||
|
s.session_data = self.encode(self._get_session(no_load=must_create))
|
||||||
|
else:
|
||||||
|
s.session_data = self._get_session(no_load=must_create)
|
||||||
s.expire_date = self.get_expiry_date()
|
s.expire_date = self.get_expiry_date()
|
||||||
try:
|
try:
|
||||||
s.save(force_insert=must_create, safe=True)
|
s.save(force_insert=must_create, safe=True)
|
||||||
|
@ -149,6 +149,7 @@ class EmailField(StringField):
|
|||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if not EmailField.EMAIL_REGEX.match(value):
|
if not EmailField.EMAIL_REGEX.match(value):
|
||||||
self.error('Invalid Mail-address: %s' % value)
|
self.error('Invalid Mail-address: %s' % value)
|
||||||
|
super(EmailField, self).validate(value)
|
||||||
|
|
||||||
|
|
||||||
class IntField(BaseField):
|
class IntField(BaseField):
|
||||||
@ -782,7 +783,7 @@ class ReferenceField(BaseField):
|
|||||||
def to_mongo(self, document):
|
def to_mongo(self, document):
|
||||||
if isinstance(document, DBRef):
|
if isinstance(document, DBRef):
|
||||||
if not self.dbref:
|
if not self.dbref:
|
||||||
return DBRef.id
|
return document.id
|
||||||
return document
|
return document
|
||||||
elif not self.dbref and isinstance(document, basestring):
|
elif not self.dbref and isinstance(document, basestring):
|
||||||
return document
|
return document
|
||||||
@ -1377,6 +1378,16 @@ class SequenceField(BaseField):
|
|||||||
upsert=True)
|
upsert=True)
|
||||||
return self.value_decorator(counter['next'])
|
return self.value_decorator(counter['next'])
|
||||||
|
|
||||||
|
def get_sequence_name(self):
|
||||||
|
if self.sequence_name:
|
||||||
|
return self.sequence_name
|
||||||
|
owner = self.owner_document
|
||||||
|
if issubclass(owner, Document):
|
||||||
|
return owner._get_collection_name()
|
||||||
|
else:
|
||||||
|
return ''.join('_%s' % c if c.isupper() else c
|
||||||
|
for c in owner._class_name).strip('_').lower()
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
value = super(SequenceField, self).__get__(instance, owner)
|
value = super(SequenceField, self).__get__(instance, owner)
|
||||||
if value is None and instance._initialised:
|
if value is None and instance._initialised:
|
||||||
|
@ -58,6 +58,8 @@ class QuerySet(object):
|
|||||||
self._read_preference = None
|
self._read_preference = None
|
||||||
self._iter = False
|
self._iter = False
|
||||||
self._scalar = []
|
self._scalar = []
|
||||||
|
self._as_pymongo = False
|
||||||
|
self._as_pymongo_coerce = False
|
||||||
|
|
||||||
# If inheritance is allowed, only return instances and instances of
|
# If inheritance is allowed, only return instances and instances of
|
||||||
# subclasses of the class being used
|
# subclasses of the class being used
|
||||||
@ -178,11 +180,13 @@ class QuerySet(object):
|
|||||||
if self._where_clause:
|
if self._where_clause:
|
||||||
self._cursor_obj.where(self._where_clause)
|
self._cursor_obj.where(self._where_clause)
|
||||||
|
|
||||||
# apply default ordering
|
|
||||||
if self._ordering:
|
if self._ordering:
|
||||||
|
# Apply query ordering
|
||||||
self._cursor_obj.sort(self._ordering)
|
self._cursor_obj.sort(self._ordering)
|
||||||
elif self._document._meta['ordering']:
|
elif self._document._meta['ordering']:
|
||||||
|
# Otherwise, apply the ordering from the document model
|
||||||
self.order_by(*self._document._meta['ordering'])
|
self.order_by(*self._document._meta['ordering'])
|
||||||
|
self._cursor_obj.sort(self._ordering)
|
||||||
|
|
||||||
if self._limit is not None:
|
if self._limit is not None:
|
||||||
self._cursor_obj.limit(self._limit - (self._skip or 0))
|
self._cursor_obj.limit(self._limit - (self._skip or 0))
|
||||||
@ -328,7 +332,7 @@ class QuerySet(object):
|
|||||||
msg = ("Some documents inserted aren't instances of %s"
|
msg = ("Some documents inserted aren't instances of %s"
|
||||||
% str(self._document))
|
% str(self._document))
|
||||||
raise OperationError(msg)
|
raise OperationError(msg)
|
||||||
if doc.pk:
|
if doc.pk and not doc._created:
|
||||||
msg = "Some documents have ObjectIds use doc.update() instead"
|
msg = "Some documents have ObjectIds use doc.update() instead"
|
||||||
raise OperationError(msg)
|
raise OperationError(msg)
|
||||||
raw.append(doc.to_mongo())
|
raw.append(doc.to_mongo())
|
||||||
@ -388,6 +392,9 @@ class QuerySet(object):
|
|||||||
for doc in docs:
|
for doc in docs:
|
||||||
doc_map[doc['_id']] = self._get_scalar(
|
doc_map[doc['_id']] = self._get_scalar(
|
||||||
self._document._from_son(doc))
|
self._document._from_son(doc))
|
||||||
|
elif self._as_pymongo:
|
||||||
|
for doc in docs:
|
||||||
|
doc_map[doc['_id']] = self._get_as_pymongo(doc)
|
||||||
else:
|
else:
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
doc_map[doc['_id']] = self._document._from_son(doc)
|
doc_map[doc['_id']] = self._document._from_son(doc)
|
||||||
@ -404,6 +411,9 @@ class QuerySet(object):
|
|||||||
if self._scalar:
|
if self._scalar:
|
||||||
return self._get_scalar(self._document._from_son(
|
return self._get_scalar(self._document._from_son(
|
||||||
self._cursor.next()))
|
self._cursor.next()))
|
||||||
|
if self._as_pymongo:
|
||||||
|
return self._get_as_pymongo(self._cursor.next())
|
||||||
|
|
||||||
return self._document._from_son(self._cursor.next())
|
return self._document._from_son(self._cursor.next())
|
||||||
except StopIteration, e:
|
except StopIteration, e:
|
||||||
self.rewind()
|
self.rewind()
|
||||||
@ -592,6 +602,8 @@ class QuerySet(object):
|
|||||||
if self._scalar:
|
if self._scalar:
|
||||||
return self._get_scalar(self._document._from_son(
|
return self._get_scalar(self._document._from_son(
|
||||||
self._cursor[key]))
|
self._cursor[key]))
|
||||||
|
if self._as_pymongo:
|
||||||
|
return self._get_as_pymongo(self._cursor.next())
|
||||||
return self._document._from_son(self._cursor[key])
|
return self._document._from_son(self._cursor[key])
|
||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
@ -714,7 +726,7 @@ class QuerySet(object):
|
|||||||
key_list.append((key, direction))
|
key_list.append((key, direction))
|
||||||
|
|
||||||
self._ordering = key_list
|
self._ordering = key_list
|
||||||
self._cursor.sort(key_list)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def explain(self, format=False):
|
def explain(self, format=False):
|
||||||
@ -887,6 +899,48 @@ class QuerySet(object):
|
|||||||
|
|
||||||
return tuple(data)
|
return tuple(data)
|
||||||
|
|
||||||
|
def _get_as_pymongo(self, row):
|
||||||
|
# Extract which fields paths we should follow if .fields(...) was
|
||||||
|
# used. If not, handle all fields.
|
||||||
|
if not getattr(self, '__as_pymongo_fields', None):
|
||||||
|
self.__as_pymongo_fields = []
|
||||||
|
for field in self._loaded_fields.fields - set(['_cls', '_id', '_types']):
|
||||||
|
self.__as_pymongo_fields.append(field)
|
||||||
|
while '.' in field:
|
||||||
|
field, _ = field.rsplit('.', 1)
|
||||||
|
self.__as_pymongo_fields.append(field)
|
||||||
|
|
||||||
|
all_fields = not self.__as_pymongo_fields
|
||||||
|
|
||||||
|
def clean(data, path=None):
|
||||||
|
path = path or ''
|
||||||
|
|
||||||
|
if isinstance(data, dict):
|
||||||
|
new_data = {}
|
||||||
|
for key, value in data.iteritems():
|
||||||
|
new_path = '%s.%s' % (path, key) if path else key
|
||||||
|
if all_fields or new_path in self.__as_pymongo_fields:
|
||||||
|
new_data[key] = clean(value, path=new_path)
|
||||||
|
data = new_data
|
||||||
|
elif isinstance(data, list):
|
||||||
|
data = [clean(d, path=path) for d in data]
|
||||||
|
else:
|
||||||
|
if self._as_pymongo_coerce:
|
||||||
|
# If we need to coerce types, we need to determine the
|
||||||
|
# type of this field and use the corresponding .to_python(...)
|
||||||
|
from mongoengine.fields import EmbeddedDocumentField
|
||||||
|
obj = self._document
|
||||||
|
for chunk in path.split('.'):
|
||||||
|
obj = getattr(obj, chunk, None)
|
||||||
|
if obj is None:
|
||||||
|
break
|
||||||
|
elif isinstance(obj, EmbeddedDocumentField):
|
||||||
|
obj = obj.document_type
|
||||||
|
if obj and data is not None:
|
||||||
|
data = obj.to_python(data)
|
||||||
|
return data
|
||||||
|
return clean(row)
|
||||||
|
|
||||||
def scalar(self, *fields):
|
def scalar(self, *fields):
|
||||||
"""Instead of returning Document instances, return either a specific
|
"""Instead of returning Document instances, return either a specific
|
||||||
value or a tuple of values in order.
|
value or a tuple of values in order.
|
||||||
@ -909,6 +963,16 @@ class QuerySet(object):
|
|||||||
"""An alias for scalar"""
|
"""An alias for scalar"""
|
||||||
return self.scalar(*fields)
|
return self.scalar(*fields)
|
||||||
|
|
||||||
|
def as_pymongo(self, coerce_types=False):
|
||||||
|
"""Instead of returning Document instances, return raw values from
|
||||||
|
pymongo.
|
||||||
|
|
||||||
|
:param coerce_type: Field types (if applicable) would be use to coerce types.
|
||||||
|
"""
|
||||||
|
self._as_pymongo = True
|
||||||
|
self._as_pymongo_coerce = coerce_types
|
||||||
|
return self
|
||||||
|
|
||||||
def _sub_js_fields(self, code):
|
def _sub_js_fields(self, code):
|
||||||
"""When fields are specified with [~fieldname] syntax, where
|
"""When fields are specified with [~fieldname] syntax, where
|
||||||
*fieldname* is the Python name of a field, *fieldname* will be
|
*fieldname* is the Python name of a field, *fieldname* will be
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
%define srcname mongoengine
|
%define srcname mongoengine
|
||||||
|
|
||||||
Name: python-%{srcname}
|
Name: python-%{srcname}
|
||||||
Version: 0.7.7
|
Version: 0.7.9
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: A Python Document-Object Mapper for working with MongoDB
|
Summary: A Python Document-Object Mapper for working with MongoDB
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ from mongoengine.errors import (NotRegistered, InvalidDocumentError,
|
|||||||
InvalidQueryError)
|
InvalidQueryError)
|
||||||
from mongoengine.queryset import NULLIFY, Q
|
from mongoengine.queryset import NULLIFY, Q
|
||||||
from mongoengine.connection import get_db
|
from mongoengine.connection import get_db
|
||||||
|
from mongoengine.base import get_document
|
||||||
|
|
||||||
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png')
|
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png')
|
||||||
|
|
||||||
@ -281,7 +282,6 @@ class InstanceTest(unittest.TestCase):
|
|||||||
|
|
||||||
User.drop_collection()
|
User.drop_collection()
|
||||||
|
|
||||||
|
|
||||||
def test_document_not_registered(self):
|
def test_document_not_registered(self):
|
||||||
|
|
||||||
class Place(Document):
|
class Place(Document):
|
||||||
@ -306,6 +306,19 @@ class InstanceTest(unittest.TestCase):
|
|||||||
print Place.objects.all()
|
print Place.objects.all()
|
||||||
self.assertRaises(NotRegistered, query_without_importing_nice_place)
|
self.assertRaises(NotRegistered, query_without_importing_nice_place)
|
||||||
|
|
||||||
|
def test_document_registry_regressions(self):
|
||||||
|
|
||||||
|
class Location(Document):
|
||||||
|
name = StringField()
|
||||||
|
meta = {'allow_inheritance': True}
|
||||||
|
|
||||||
|
class Area(Location):
|
||||||
|
location = ReferenceField('Location', dbref=True)
|
||||||
|
|
||||||
|
Location.drop_collection()
|
||||||
|
|
||||||
|
self.assertEquals(Area, get_document("Area"))
|
||||||
|
self.assertEquals(Area, get_document("Location.Area"))
|
||||||
|
|
||||||
def test_creation(self):
|
def test_creation(self):
|
||||||
"""Ensure that document may be created using keyword arguments.
|
"""Ensure that document may be created using keyword arguments.
|
||||||
|
@ -1118,6 +1118,16 @@ class FieldTest(unittest.TestCase):
|
|||||||
p = Person.objects.get(name="Ross")
|
p = Person.objects.get(name="Ross")
|
||||||
self.assertEqual(p.parent, p1)
|
self.assertEqual(p.parent, p1)
|
||||||
|
|
||||||
|
def test_dbref_to_mongo(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
parent = ReferenceField('self', dbref=False)
|
||||||
|
|
||||||
|
p1 = Person._from_son({'name': "Yakxxx",
|
||||||
|
'parent': "50a234ea469ac1eda42d347d"})
|
||||||
|
mongoed = p1.to_mongo()
|
||||||
|
self.assertTrue(isinstance(mongoed['parent'], ObjectId))
|
||||||
|
|
||||||
def test_objectid_reference_fields(self):
|
def test_objectid_reference_fields(self):
|
||||||
|
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@ -2216,6 +2226,29 @@ class FieldTest(unittest.TestCase):
|
|||||||
c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
|
c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
|
||||||
self.assertEqual(c['next'], 10)
|
self.assertEqual(c['next'], 10)
|
||||||
|
|
||||||
|
def test_embedded_sequence_field(self):
|
||||||
|
class Comment(EmbeddedDocument):
|
||||||
|
id = SequenceField()
|
||||||
|
content = StringField(required=True)
|
||||||
|
|
||||||
|
class Post(Document):
|
||||||
|
title = StringField(required=True)
|
||||||
|
comments = ListField(EmbeddedDocumentField(Comment))
|
||||||
|
|
||||||
|
self.db['mongoengine.counters'].drop()
|
||||||
|
Post.drop_collection()
|
||||||
|
|
||||||
|
Post(title="MongoEngine",
|
||||||
|
comments=[Comment(content="NoSQL Rocks"),
|
||||||
|
Comment(content="MongoEngine Rocks")]).save()
|
||||||
|
import ipdb; ipdb.set_trace();
|
||||||
|
c = self.db['mongoengine.counters'].find_one({'_id': 'comment.id'})
|
||||||
|
self.assertEqual(c['next'], 2)
|
||||||
|
post = Post.objects.first()
|
||||||
|
self.assertEqual(1, post.comments[0].id)
|
||||||
|
self.assertEqual(2, post.comments[1].id)
|
||||||
|
|
||||||
|
|
||||||
def test_generic_embedded_document(self):
|
def test_generic_embedded_document(self):
|
||||||
class Car(EmbeddedDocument):
|
class Car(EmbeddedDocument):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
@ -2339,6 +2372,18 @@ class FieldTest(unittest.TestCase):
|
|||||||
post.comments[1].content = 'here we go'
|
post.comments[1].content = 'here we go'
|
||||||
post.validate()
|
post.validate()
|
||||||
|
|
||||||
|
def test_email_field_honors_regex(self):
|
||||||
|
class User(Document):
|
||||||
|
email = EmailField(regex=r'\w+@example.com')
|
||||||
|
|
||||||
|
# Fails regex validation
|
||||||
|
user = User(email='me@foo.com')
|
||||||
|
self.assertRaises(ValidationError, user.validate)
|
||||||
|
|
||||||
|
# Passes regex validation
|
||||||
|
user = User(email='me@example.com')
|
||||||
|
self.assertTrue(user.validate() is None)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -625,6 +625,10 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertRaises(OperationError, throw_operation_error)
|
self.assertRaises(OperationError, throw_operation_error)
|
||||||
|
|
||||||
|
# Test can insert new doc
|
||||||
|
new_post = Blog(title="code", id=ObjectId())
|
||||||
|
Blog.objects.insert(new_post)
|
||||||
|
|
||||||
# test handles other classes being inserted
|
# test handles other classes being inserted
|
||||||
def throw_operation_error_wrong_doc():
|
def throw_operation_error_wrong_doc():
|
||||||
class Author(Document):
|
class Author(Document):
|
||||||
@ -1967,6 +1971,22 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
ages = [p.age for p in self.Person.objects.order_by('-name')]
|
ages = [p.age for p in self.Person.objects.order_by('-name')]
|
||||||
self.assertEqual(ages, [30, 40, 20])
|
self.assertEqual(ages, [30, 40, 20])
|
||||||
|
|
||||||
|
def test_order_by_chaining(self):
|
||||||
|
"""Ensure that an order_by query chains properly and allows .only()
|
||||||
|
"""
|
||||||
|
self.Person(name="User A", age=20).save()
|
||||||
|
self.Person(name="User B", age=40).save()
|
||||||
|
self.Person(name="User C", age=30).save()
|
||||||
|
|
||||||
|
only_age = self.Person.objects.order_by('-age').only('age')
|
||||||
|
|
||||||
|
names = [p.name for p in only_age]
|
||||||
|
ages = [p.age for p in only_age]
|
||||||
|
|
||||||
|
# The .only('age') clause should mean that all names are None
|
||||||
|
self.assertEqual(names, [None, None, None])
|
||||||
|
self.assertEqual(ages, [40, 30, 20])
|
||||||
|
|
||||||
def test_confirm_order_by_reference_wont_work(self):
|
def test_confirm_order_by_reference_wont_work(self):
|
||||||
"""Ordering by reference is not possible. Use map / reduce.. or
|
"""Ordering by reference is not possible. Use map / reduce.. or
|
||||||
denormalise"""
|
denormalise"""
|
||||||
@ -3761,5 +3781,38 @@ class QueryFieldListTest(unittest.TestCase):
|
|||||||
Test.objects(test='foo').update_one(upsert=True, set__test='foo')
|
Test.objects(test='foo').update_one(upsert=True, set__test='foo')
|
||||||
self.assertTrue('_cls' in Test._collection.find_one())
|
self.assertTrue('_cls' in Test._collection.find_one())
|
||||||
|
|
||||||
|
def test_as_pymongo(self):
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
class User(Document):
|
||||||
|
id = ObjectIdField('_id')
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
price = DecimalField()
|
||||||
|
|
||||||
|
User.drop_collection()
|
||||||
|
User(name="Bob Dole", age=89, price=Decimal('1.11')).save()
|
||||||
|
User(name="Barack Obama", age=51, price=Decimal('2.22')).save()
|
||||||
|
|
||||||
|
users = User.objects.only('name', 'price').as_pymongo()
|
||||||
|
results = list(users)
|
||||||
|
self.assertTrue(isinstance(results[0], dict))
|
||||||
|
self.assertTrue(isinstance(results[1], dict))
|
||||||
|
self.assertEqual(results[0]['name'], 'Bob Dole')
|
||||||
|
self.assertEqual(results[0]['price'], '1.11')
|
||||||
|
self.assertEqual(results[1]['name'], 'Barack Obama')
|
||||||
|
self.assertEqual(results[1]['price'], '2.22')
|
||||||
|
|
||||||
|
# Test coerce_types
|
||||||
|
users = User.objects.only('name', 'price').as_pymongo(coerce_types=True)
|
||||||
|
results = list(users)
|
||||||
|
self.assertTrue(isinstance(results[0], dict))
|
||||||
|
self.assertTrue(isinstance(results[1], dict))
|
||||||
|
self.assertEqual(results[0]['name'], 'Bob Dole')
|
||||||
|
self.assertEqual(results[0]['price'], Decimal('1.11'))
|
||||||
|
self.assertEqual(results[1]['name'], 'Barack Obama')
|
||||||
|
self.assertEqual(results[1]['price'], Decimal('2.22'))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user