Merge branch 'dev' into pull_124
This commit is contained in:
@@ -1,13 +1,25 @@
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
import pymongo
|
||||
import pickle
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.base import BaseField
|
||||
from mongoengine.connection import _get_db
|
||||
|
||||
|
||||
class PickleEmbedded(EmbeddedDocument):
|
||||
date = DateTimeField(default=datetime.now)
|
||||
|
||||
class PickleTest(Document):
|
||||
number = IntField()
|
||||
string = StringField()
|
||||
embedded = EmbeddedDocumentField(PickleEmbedded)
|
||||
lists = ListField(StringField())
|
||||
|
||||
|
||||
class DocumentTest(unittest.TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
connect(db='mongoenginetest')
|
||||
self.db = _get_db()
|
||||
@@ -17,6 +29,9 @@ class DocumentTest(unittest.TestCase):
|
||||
age = IntField()
|
||||
self.Person = Person
|
||||
|
||||
def tearDown(self):
|
||||
self.Person.drop_collection()
|
||||
|
||||
def test_drop_collection(self):
|
||||
"""Ensure that the collection may be dropped from the database.
|
||||
"""
|
||||
@@ -38,7 +53,7 @@ class DocumentTest(unittest.TestCase):
|
||||
name = name_field
|
||||
age = age_field
|
||||
non_field = True
|
||||
|
||||
|
||||
self.assertEqual(Person._fields['name'], name_field)
|
||||
self.assertEqual(Person._fields['age'], age_field)
|
||||
self.assertFalse('non_field' in Person._fields)
|
||||
@@ -60,7 +75,7 @@ class DocumentTest(unittest.TestCase):
|
||||
|
||||
mammal_superclasses = {'Animal': Animal}
|
||||
self.assertEqual(Mammal._superclasses, mammal_superclasses)
|
||||
|
||||
|
||||
dog_superclasses = {
|
||||
'Animal': Animal,
|
||||
'Animal.Mammal': Mammal,
|
||||
@@ -68,7 +83,7 @@ class DocumentTest(unittest.TestCase):
|
||||
self.assertEqual(Dog._superclasses, dog_superclasses)
|
||||
|
||||
def test_get_subclasses(self):
|
||||
"""Ensure that the correct list of subclasses is retrieved by the
|
||||
"""Ensure that the correct list of subclasses is retrieved by the
|
||||
_get_subclasses method.
|
||||
"""
|
||||
class Animal(Document): pass
|
||||
@@ -78,15 +93,15 @@ class DocumentTest(unittest.TestCase):
|
||||
class Dog(Mammal): pass
|
||||
|
||||
mammal_subclasses = {
|
||||
'Animal.Mammal.Dog': Dog,
|
||||
'Animal.Mammal.Dog': Dog,
|
||||
'Animal.Mammal.Human': Human
|
||||
}
|
||||
self.assertEqual(Mammal._get_subclasses(), mammal_subclasses)
|
||||
|
||||
|
||||
animal_subclasses = {
|
||||
'Animal.Fish': Fish,
|
||||
'Animal.Mammal': Mammal,
|
||||
'Animal.Mammal.Dog': Dog,
|
||||
'Animal.Mammal.Dog': Dog,
|
||||
'Animal.Mammal.Human': Human
|
||||
}
|
||||
self.assertEqual(Animal._get_subclasses(), animal_subclasses)
|
||||
@@ -124,7 +139,7 @@ class DocumentTest(unittest.TestCase):
|
||||
|
||||
self.assertTrue('name' in Employee._fields)
|
||||
self.assertTrue('salary' in Employee._fields)
|
||||
self.assertEqual(Employee._meta['collection'],
|
||||
self.assertEqual(Employee._meta['collection'],
|
||||
self.Person._meta['collection'])
|
||||
|
||||
# Ensure that MRO error is not raised
|
||||
@@ -146,7 +161,7 @@ class DocumentTest(unittest.TestCase):
|
||||
class Dog(Animal):
|
||||
pass
|
||||
self.assertRaises(ValueError, create_dog_class)
|
||||
|
||||
|
||||
# Check that _cls etc aren't present on simple documents
|
||||
dog = Animal(name='dog')
|
||||
dog.save()
|
||||
@@ -161,7 +176,7 @@ class DocumentTest(unittest.TestCase):
|
||||
class Employee(self.Person):
|
||||
meta = {'allow_inheritance': False}
|
||||
self.assertRaises(ValueError, create_employee_class)
|
||||
|
||||
|
||||
# Test the same for embedded documents
|
||||
class Comment(EmbeddedDocument):
|
||||
content = StringField()
|
||||
@@ -176,6 +191,34 @@ class DocumentTest(unittest.TestCase):
|
||||
self.assertFalse('_cls' in comment.to_mongo())
|
||||
self.assertFalse('_types' in comment.to_mongo())
|
||||
|
||||
def test_abstract_documents(self):
|
||||
"""Ensure that a document superclass can be marked as abstract
|
||||
thereby not using it as the name for the collection."""
|
||||
|
||||
class Animal(Document):
|
||||
name = StringField()
|
||||
meta = {'abstract': True}
|
||||
|
||||
class Fish(Animal): pass
|
||||
class Guppy(Fish): pass
|
||||
|
||||
class Mammal(Animal):
|
||||
meta = {'abstract': True}
|
||||
class Human(Mammal): pass
|
||||
|
||||
self.assertFalse('collection' in Animal._meta)
|
||||
self.assertFalse('collection' in Mammal._meta)
|
||||
|
||||
self.assertEqual(Fish._meta['collection'], 'fish')
|
||||
self.assertEqual(Guppy._meta['collection'], 'fish')
|
||||
self.assertEqual(Human._meta['collection'], 'human')
|
||||
|
||||
def create_bad_abstract():
|
||||
class EvilHuman(Human):
|
||||
evil = BooleanField(default=True)
|
||||
meta = {'abstract': True}
|
||||
self.assertRaises(ValueError, create_bad_abstract)
|
||||
|
||||
def test_collection_name(self):
|
||||
"""Ensure that a collection with a specified name may be used.
|
||||
"""
|
||||
@@ -186,7 +229,7 @@ class DocumentTest(unittest.TestCase):
|
||||
class Person(Document):
|
||||
name = StringField()
|
||||
meta = {'collection': collection}
|
||||
|
||||
|
||||
user = Person(name="Test User")
|
||||
user.save()
|
||||
self.assertTrue(collection in self.db.collection_names())
|
||||
@@ -200,6 +243,22 @@ class DocumentTest(unittest.TestCase):
|
||||
Person.drop_collection()
|
||||
self.assertFalse(collection in self.db.collection_names())
|
||||
|
||||
def test_collection_name_and_primary(self):
|
||||
"""Ensure that a collection with a specified name may be used.
|
||||
"""
|
||||
|
||||
class Person(Document):
|
||||
name = StringField(primary_key=True)
|
||||
meta = {'collection': 'app'}
|
||||
|
||||
user = Person(name="Test User")
|
||||
user.save()
|
||||
|
||||
user_obj = Person.objects[0]
|
||||
self.assertEqual(user_obj.name, "Test User")
|
||||
|
||||
Person.drop_collection()
|
||||
|
||||
def test_inherited_collections(self):
|
||||
"""Ensure that subclassed documents don't override parents' collections.
|
||||
"""
|
||||
@@ -280,7 +339,7 @@ class DocumentTest(unittest.TestCase):
|
||||
tags = ListField(StringField())
|
||||
meta = {
|
||||
'indexes': [
|
||||
'-date',
|
||||
'-date',
|
||||
'tags',
|
||||
('category', '-date')
|
||||
],
|
||||
@@ -296,12 +355,12 @@ class DocumentTest(unittest.TestCase):
|
||||
list(BlogPost.objects)
|
||||
info = BlogPost.objects._collection.index_information()
|
||||
info = [value['key'] for key, value in info.iteritems()]
|
||||
self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)]
|
||||
self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)]
|
||||
in info)
|
||||
self.assertTrue([('_types', 1), ('addDate', -1)] in info)
|
||||
# tags is a list field so it shouldn't have _types in the index
|
||||
self.assertTrue([('tags', 1)] in info)
|
||||
|
||||
|
||||
class ExtendedBlogPost(BlogPost):
|
||||
title = StringField()
|
||||
meta = {'indexes': ['title']}
|
||||
@@ -311,7 +370,7 @@ class DocumentTest(unittest.TestCase):
|
||||
list(ExtendedBlogPost.objects)
|
||||
info = ExtendedBlogPost.objects._collection.index_information()
|
||||
info = [value['key'] for key, value in info.iteritems()]
|
||||
self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)]
|
||||
self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)]
|
||||
in info)
|
||||
self.assertTrue([('_types', 1), ('addDate', -1)] in info)
|
||||
self.assertTrue([('_types', 1), ('title', 1)] in info)
|
||||
@@ -334,6 +393,10 @@ class DocumentTest(unittest.TestCase):
|
||||
post2 = BlogPost(title='test2', slug='test')
|
||||
self.assertRaises(OperationError, post2.save)
|
||||
|
||||
|
||||
def test_unique_with(self):
|
||||
"""Ensure that unique_with constraints are applied to fields.
|
||||
"""
|
||||
class Date(EmbeddedDocument):
|
||||
year = IntField(db_field='yr')
|
||||
|
||||
@@ -357,6 +420,108 @@ class DocumentTest(unittest.TestCase):
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_unique_embedded_document(self):
|
||||
"""Ensure that uniqueness constraints are applied to fields on embedded documents.
|
||||
"""
|
||||
class SubDocument(EmbeddedDocument):
|
||||
year = IntField(db_field='yr')
|
||||
slug = StringField(unique=True)
|
||||
|
||||
class BlogPost(Document):
|
||||
title = StringField()
|
||||
sub = EmbeddedDocumentField(SubDocument)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
post1 = BlogPost(title='test1', sub=SubDocument(year=2009, slug="test"))
|
||||
post1.save()
|
||||
|
||||
# sub.slug is different so won't raise exception
|
||||
post2 = BlogPost(title='test2', sub=SubDocument(year=2010, slug='another-slug'))
|
||||
post2.save()
|
||||
|
||||
# Now there will be two docs with the same sub.slug
|
||||
post3 = BlogPost(title='test3', sub=SubDocument(year=2010, slug='test'))
|
||||
self.assertRaises(OperationError, post3.save)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_unique_with_embedded_document_and_embedded_unique(self):
|
||||
"""Ensure that uniqueness constraints are applied to fields on
|
||||
embedded documents. And work with unique_with as well.
|
||||
"""
|
||||
class SubDocument(EmbeddedDocument):
|
||||
year = IntField(db_field='yr')
|
||||
slug = StringField(unique=True)
|
||||
|
||||
class BlogPost(Document):
|
||||
title = StringField(unique_with='sub.year')
|
||||
sub = EmbeddedDocumentField(SubDocument)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
post1 = BlogPost(title='test1', sub=SubDocument(year=2009, slug="test"))
|
||||
post1.save()
|
||||
|
||||
# sub.slug is different so won't raise exception
|
||||
post2 = BlogPost(title='test2', sub=SubDocument(year=2010, slug='another-slug'))
|
||||
post2.save()
|
||||
|
||||
# Now there will be two docs with the same sub.slug
|
||||
post3 = BlogPost(title='test3', sub=SubDocument(year=2010, slug='test'))
|
||||
self.assertRaises(OperationError, post3.save)
|
||||
|
||||
# Now there will be two docs with the same title and year
|
||||
post3 = BlogPost(title='test1', sub=SubDocument(year=2009, slug='test-1'))
|
||||
self.assertRaises(OperationError, post3.save)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_unique_and_indexes(self):
|
||||
"""Ensure that 'unique' constraints aren't overridden by
|
||||
meta.indexes.
|
||||
"""
|
||||
class Customer(Document):
|
||||
cust_id = IntField(unique=True, required=True)
|
||||
meta = {
|
||||
'indexes': ['cust_id'],
|
||||
'allow_inheritance': False,
|
||||
}
|
||||
|
||||
Customer.drop_collection()
|
||||
cust = Customer(cust_id=1)
|
||||
cust.save()
|
||||
|
||||
cust_dupe = Customer(cust_id=1)
|
||||
try:
|
||||
cust_dupe.save()
|
||||
raise AssertionError, "We saved a dupe!"
|
||||
except OperationError:
|
||||
pass
|
||||
Customer.drop_collection()
|
||||
|
||||
def test_unique_and_primary(self):
|
||||
"""If you set a field as primary, then unexpected behaviour can occur.
|
||||
You won't create a duplicate but you will update an existing document.
|
||||
"""
|
||||
|
||||
class User(Document):
|
||||
name = StringField(primary_key=True, unique=True)
|
||||
password = StringField()
|
||||
|
||||
User.drop_collection()
|
||||
|
||||
user = User(name='huangz', password='secret')
|
||||
user.save()
|
||||
|
||||
user = User(name='huangz', password='secret2')
|
||||
user.save()
|
||||
|
||||
self.assertEqual(User.objects.count(), 1)
|
||||
self.assertEqual(User.objects.get().password, 'secret2')
|
||||
|
||||
User.drop_collection()
|
||||
|
||||
def test_custom_id_field(self):
|
||||
"""Ensure that documents may be created with custom primary keys.
|
||||
"""
|
||||
@@ -380,7 +545,7 @@ class DocumentTest(unittest.TestCase):
|
||||
|
||||
class EmailUser(User):
|
||||
email = StringField()
|
||||
|
||||
|
||||
user = User(username='test', name='test user')
|
||||
user.save()
|
||||
|
||||
@@ -391,20 +556,20 @@ class DocumentTest(unittest.TestCase):
|
||||
user_son = User.objects._collection.find_one()
|
||||
self.assertEqual(user_son['_id'], 'test')
|
||||
self.assertTrue('username' not in user_son['_id'])
|
||||
|
||||
|
||||
User.drop_collection()
|
||||
|
||||
|
||||
user = User(pk='mongo', name='mongo user')
|
||||
user.save()
|
||||
|
||||
|
||||
user_obj = User.objects.first()
|
||||
self.assertEqual(user_obj.id, 'mongo')
|
||||
self.assertEqual(user_obj.pk, 'mongo')
|
||||
|
||||
|
||||
user_son = User.objects._collection.find_one()
|
||||
self.assertEqual(user_son['_id'], 'mongo')
|
||||
self.assertTrue('username' not in user_son['_id'])
|
||||
|
||||
|
||||
User.drop_collection()
|
||||
|
||||
def test_creation(self):
|
||||
@@ -457,18 +622,18 @@ class DocumentTest(unittest.TestCase):
|
||||
"""
|
||||
class Comment(EmbeddedDocument):
|
||||
content = StringField()
|
||||
|
||||
|
||||
self.assertTrue('content' in Comment._fields)
|
||||
self.assertFalse('id' in Comment._fields)
|
||||
self.assertFalse('collection' in Comment._meta)
|
||||
|
||||
|
||||
def test_embedded_document_validation(self):
|
||||
"""Ensure that embedded documents may be validated.
|
||||
"""
|
||||
class Comment(EmbeddedDocument):
|
||||
date = DateTimeField()
|
||||
content = StringField(required=True)
|
||||
|
||||
|
||||
comment = Comment()
|
||||
self.assertRaises(ValidationError, comment.validate)
|
||||
|
||||
@@ -496,7 +661,7 @@ class DocumentTest(unittest.TestCase):
|
||||
# Test skipping validation on save
|
||||
class Recipient(Document):
|
||||
email = EmailField(required=True)
|
||||
|
||||
|
||||
recipient = Recipient(email='root@localhost')
|
||||
self.assertRaises(ValidationError, recipient.save)
|
||||
try:
|
||||
@@ -517,19 +682,19 @@ class DocumentTest(unittest.TestCase):
|
||||
"""Ensure that a document may be saved with a custom _id.
|
||||
"""
|
||||
# Create person object and save it to the database
|
||||
person = self.Person(name='Test User', age=30,
|
||||
person = self.Person(name='Test User', age=30,
|
||||
id='497ce96f395f2f052a494fd4')
|
||||
person.save()
|
||||
# Ensure that the object is in the database with the correct _id
|
||||
collection = self.db[self.Person._meta['collection']]
|
||||
person_obj = collection.find_one({'name': 'Test User'})
|
||||
self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4')
|
||||
|
||||
|
||||
def test_save_custom_pk(self):
|
||||
"""Ensure that a document may be saved with a custom _id using pk alias.
|
||||
"""
|
||||
# Create person object and save it to the database
|
||||
person = self.Person(name='Test User', age=30,
|
||||
person = self.Person(name='Test User', age=30,
|
||||
pk='497ce96f395f2f052a494fd4')
|
||||
person.save()
|
||||
# Ensure that the object is in the database with the correct _id
|
||||
@@ -565,7 +730,7 @@ class DocumentTest(unittest.TestCase):
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_save_embedded_document(self):
|
||||
"""Ensure that a document with an embedded document field may be
|
||||
"""Ensure that a document with an embedded document field may be
|
||||
saved in the database.
|
||||
"""
|
||||
class EmployeeDetails(EmbeddedDocument):
|
||||
@@ -588,10 +753,38 @@ class DocumentTest(unittest.TestCase):
|
||||
# Ensure that the 'details' embedded object saved correctly
|
||||
self.assertEqual(employee_obj['details']['position'], 'Developer')
|
||||
|
||||
def test_updating_an_embedded_document(self):
|
||||
"""Ensure that a document with an embedded document field may be
|
||||
saved in the database.
|
||||
"""
|
||||
class EmployeeDetails(EmbeddedDocument):
|
||||
position = StringField()
|
||||
|
||||
class Employee(self.Person):
|
||||
salary = IntField()
|
||||
details = EmbeddedDocumentField(EmployeeDetails)
|
||||
|
||||
# Create employee object and save it to the database
|
||||
employee = Employee(name='Test Employee', age=50, salary=20000)
|
||||
employee.details = EmployeeDetails(position='Developer')
|
||||
employee.save()
|
||||
|
||||
# Test updating an embedded document
|
||||
promoted_employee = Employee.objects.get(name='Test Employee')
|
||||
promoted_employee.details.position = 'Senior Developer'
|
||||
promoted_employee.save()
|
||||
|
||||
collection = self.db[self.Person._meta['collection']]
|
||||
employee_obj = collection.find_one({'name': 'Test Employee'})
|
||||
self.assertEqual(employee_obj['name'], 'Test Employee')
|
||||
self.assertEqual(employee_obj['age'], 50)
|
||||
# Ensure that the 'details' embedded object saved correctly
|
||||
self.assertEqual(employee_obj['details']['position'], 'Senior Developer')
|
||||
|
||||
def test_save_reference(self):
|
||||
"""Ensure that a document reference field may be saved in the database.
|
||||
"""
|
||||
|
||||
|
||||
class BlogPost(Document):
|
||||
meta = {'collection': 'blogpost_1'}
|
||||
content = StringField()
|
||||
@@ -610,7 +803,7 @@ class DocumentTest(unittest.TestCase):
|
||||
post_obj = BlogPost.objects.first()
|
||||
|
||||
# Test laziness
|
||||
self.assertTrue(isinstance(post_obj._data['author'],
|
||||
self.assertTrue(isinstance(post_obj._data['author'],
|
||||
pymongo.dbref.DBRef))
|
||||
self.assertTrue(isinstance(post_obj.author, self.Person))
|
||||
self.assertEqual(post_obj.author.name, 'Test User')
|
||||
@@ -725,9 +918,25 @@ class DocumentTest(unittest.TestCase):
|
||||
self.Person.drop_collection()
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def subclasses_and_unique_keys_works(self):
|
||||
|
||||
def tearDown(self):
|
||||
self.Person.drop_collection()
|
||||
class A(Document):
|
||||
pass
|
||||
|
||||
class B(A):
|
||||
foo = BooleanField(unique=True)
|
||||
|
||||
A.drop_collection()
|
||||
B.drop_collection()
|
||||
|
||||
A().save()
|
||||
A().save()
|
||||
B(foo=True).save()
|
||||
|
||||
self.assertEquals(A.objects.count(), 2)
|
||||
self.assertEquals(B.objects.count(), 1)
|
||||
A.drop_collection()
|
||||
B.drop_collection()
|
||||
|
||||
def test_document_hash(self):
|
||||
"""Test document in list, dict, set
|
||||
@@ -737,7 +946,7 @@ class DocumentTest(unittest.TestCase):
|
||||
|
||||
class BlogPost(Document):
|
||||
pass
|
||||
|
||||
|
||||
# Clear old datas
|
||||
User.drop_collection()
|
||||
BlogPost.drop_collection()
|
||||
@@ -774,9 +983,46 @@ class DocumentTest(unittest.TestCase):
|
||||
|
||||
# in Set
|
||||
all_user_set = set(User.objects.all())
|
||||
|
||||
|
||||
self.assertTrue(u1 in all_user_set )
|
||||
|
||||
|
||||
def test_picklable(self):
|
||||
|
||||
pickle_doc = PickleTest(number=1, string="OH HAI", lists=['1', '2'])
|
||||
pickle_doc.embedded = PickleEmbedded()
|
||||
pickle_doc.save()
|
||||
|
||||
pickled_doc = pickle.dumps(pickle_doc)
|
||||
resurrected = pickle.loads(pickled_doc)
|
||||
|
||||
self.assertEquals(resurrected, pickle_doc)
|
||||
|
||||
resurrected.string = "Working"
|
||||
resurrected.save()
|
||||
|
||||
pickle_doc.reload()
|
||||
self.assertEquals(resurrected, pickle_doc)
|
||||
|
||||
def test_write_options(self):
|
||||
"""Test that passing write_options works"""
|
||||
|
||||
self.Person.drop_collection()
|
||||
|
||||
write_options = {"fsync": True}
|
||||
|
||||
author, created = self.Person.objects.get_or_create(
|
||||
name='Test User', write_options=write_options)
|
||||
author.save(write_options=write_options)
|
||||
|
||||
self.Person.objects.update(set__name='Ross', write_options=write_options)
|
||||
|
||||
author = self.Person.objects.first()
|
||||
self.assertEquals(author.name, 'Ross')
|
||||
|
||||
self.Person.objects.update_one(set__name='Test User', write_options=write_options)
|
||||
author = self.Person.objects.first()
|
||||
self.assertEquals(author.name, 'Test User')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
160
tests/fields.py
160
tests/fields.py
@@ -7,6 +7,7 @@ import gridfs
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import _get_db
|
||||
from mongoengine.base import _document_registry, NotRegistered
|
||||
|
||||
|
||||
class FieldTest(unittest.TestCase):
|
||||
@@ -45,7 +46,7 @@ class FieldTest(unittest.TestCase):
|
||||
"""
|
||||
class Person(Document):
|
||||
name = StringField()
|
||||
|
||||
|
||||
person = Person(name='Test User')
|
||||
self.assertEqual(person.id, None)
|
||||
|
||||
@@ -95,7 +96,7 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
link.url = 'http://www.google.com:8080'
|
||||
link.validate()
|
||||
|
||||
|
||||
def test_int_validation(self):
|
||||
"""Ensure that invalid values cannot be assigned to int fields.
|
||||
"""
|
||||
@@ -129,12 +130,12 @@ class FieldTest(unittest.TestCase):
|
||||
self.assertRaises(ValidationError, person.validate)
|
||||
person.height = 4.0
|
||||
self.assertRaises(ValidationError, person.validate)
|
||||
|
||||
|
||||
def test_decimal_validation(self):
|
||||
"""Ensure that invalid values cannot be assigned to decimal fields.
|
||||
"""
|
||||
class Person(Document):
|
||||
height = DecimalField(min_value=Decimal('0.1'),
|
||||
height = DecimalField(min_value=Decimal('0.1'),
|
||||
max_value=Decimal('3.5'))
|
||||
|
||||
Person.drop_collection()
|
||||
@@ -249,7 +250,7 @@ class FieldTest(unittest.TestCase):
|
||||
post.save()
|
||||
post.reload()
|
||||
self.assertEqual(post.tags, ['fun', 'leisure'])
|
||||
|
||||
|
||||
comment1 = Comment(content='Good for you', order=1)
|
||||
comment2 = Comment(content='Yay.', order=0)
|
||||
comments = [comment1, comment2]
|
||||
@@ -261,12 +262,14 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_dict_validation(self):
|
||||
def test_dict_field(self):
|
||||
"""Ensure that dict types work as expected.
|
||||
"""
|
||||
class BlogPost(Document):
|
||||
info = DictField()
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
post = BlogPost()
|
||||
post.info = 'my post'
|
||||
self.assertRaises(ValidationError, post.validate)
|
||||
@@ -281,7 +284,24 @@ class FieldTest(unittest.TestCase):
|
||||
self.assertRaises(ValidationError, post.validate)
|
||||
|
||||
post.info = {'title': 'test'}
|
||||
post.validate()
|
||||
post.save()
|
||||
|
||||
post = BlogPost()
|
||||
post.info = {'details': {'test': 'test'}}
|
||||
post.save()
|
||||
|
||||
post = BlogPost()
|
||||
post.info = {'details': {'test': 3}}
|
||||
post.save()
|
||||
|
||||
self.assertEquals(BlogPost.objects.count(), 3)
|
||||
self.assertEquals(BlogPost.objects.filter(info__title__exact='test').count(), 1)
|
||||
self.assertEquals(BlogPost.objects.filter(info__details__test__exact='test').count(), 1)
|
||||
|
||||
# Confirm handles non strings or non existing keys
|
||||
self.assertEquals(BlogPost.objects.filter(info__details__test__exact=5).count(), 0)
|
||||
self.assertEquals(BlogPost.objects.filter(info__made_up__test__exact='test').count(), 0)
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_embedded_document_validation(self):
|
||||
"""Ensure that invalid embedded documents cannot be assigned to
|
||||
@@ -315,7 +335,7 @@ class FieldTest(unittest.TestCase):
|
||||
person.validate()
|
||||
|
||||
def test_embedded_document_inheritance(self):
|
||||
"""Ensure that subclasses of embedded documents may be provided to
|
||||
"""Ensure that subclasses of embedded documents may be provided to
|
||||
EmbeddedDocumentFields of the superclass' type.
|
||||
"""
|
||||
class User(EmbeddedDocument):
|
||||
@@ -327,7 +347,7 @@ class FieldTest(unittest.TestCase):
|
||||
class BlogPost(Document):
|
||||
content = StringField()
|
||||
author = EmbeddedDocumentField(User)
|
||||
|
||||
|
||||
post = BlogPost(content='What I did today...')
|
||||
post.author = User(name='Test User')
|
||||
post.author = PowerUser(name='Test User', power=47)
|
||||
@@ -370,7 +390,7 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
User.drop_collection()
|
||||
BlogPost.drop_collection()
|
||||
|
||||
|
||||
def test_list_item_dereference(self):
|
||||
"""Ensure that DBRef items in ListFields are dereferenced.
|
||||
"""
|
||||
@@ -434,7 +454,7 @@ class FieldTest(unittest.TestCase):
|
||||
class TreeNode(EmbeddedDocument):
|
||||
name = StringField()
|
||||
children = ListField(EmbeddedDocumentField('self'))
|
||||
|
||||
|
||||
tree = Tree(name="Tree")
|
||||
|
||||
first_child = TreeNode(name="Child 1")
|
||||
@@ -442,7 +462,7 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
second_child = TreeNode(name="Child 2")
|
||||
first_child.children.append(second_child)
|
||||
|
||||
|
||||
third_child = TreeNode(name="Child 3")
|
||||
first_child.children.append(third_child)
|
||||
|
||||
@@ -506,20 +526,20 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
Member.drop_collection()
|
||||
BlogPost.drop_collection()
|
||||
|
||||
|
||||
def test_generic_reference(self):
|
||||
"""Ensure that a GenericReferenceField properly dereferences items.
|
||||
"""
|
||||
class Link(Document):
|
||||
title = StringField()
|
||||
meta = {'allow_inheritance': False}
|
||||
|
||||
|
||||
class Post(Document):
|
||||
title = StringField()
|
||||
|
||||
|
||||
class Bookmark(Document):
|
||||
bookmark_object = GenericReferenceField()
|
||||
|
||||
|
||||
Link.drop_collection()
|
||||
Post.drop_collection()
|
||||
Bookmark.drop_collection()
|
||||
@@ -574,16 +594,49 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
user = User(bookmarks=[post_1, link_1])
|
||||
user.save()
|
||||
|
||||
|
||||
user = User.objects(bookmarks__all=[post_1, link_1]).first()
|
||||
|
||||
|
||||
self.assertEqual(user.bookmarks[0], post_1)
|
||||
self.assertEqual(user.bookmarks[1], link_1)
|
||||
|
||||
|
||||
Link.drop_collection()
|
||||
Post.drop_collection()
|
||||
User.drop_collection()
|
||||
|
||||
def test_generic_reference_document_not_registered(self):
|
||||
"""Ensure dereferencing out of the document registry throws a
|
||||
`NotRegistered` error.
|
||||
"""
|
||||
class Link(Document):
|
||||
title = StringField()
|
||||
|
||||
class User(Document):
|
||||
bookmarks = ListField(GenericReferenceField())
|
||||
|
||||
Link.drop_collection()
|
||||
User.drop_collection()
|
||||
|
||||
link_1 = Link(title="Pitchfork")
|
||||
link_1.save()
|
||||
|
||||
user = User(bookmarks=[link_1])
|
||||
user.save()
|
||||
|
||||
# Mimic User and Link definitions being in a different file
|
||||
# and the Link model not being imported in the User file.
|
||||
del(_document_registry["Link"])
|
||||
|
||||
user = User.objects.first()
|
||||
try:
|
||||
user.bookmarks
|
||||
raise AssertionError, "Link was removed from the registry"
|
||||
except NotRegistered:
|
||||
pass
|
||||
|
||||
Link.drop_collection()
|
||||
User.drop_collection()
|
||||
|
||||
def test_binary_fields(self):
|
||||
"""Ensure that binary fields can be stored and retrieved.
|
||||
"""
|
||||
@@ -701,6 +754,12 @@ class FieldTest(unittest.TestCase):
|
||||
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()
|
||||
|
||||
# Ensure deleted file returns None
|
||||
@@ -721,7 +780,7 @@ class FieldTest(unittest.TestCase):
|
||||
result = SetFile.objects.first()
|
||||
self.assertTrue(setfile == result)
|
||||
self.assertEquals(result.file.read(), more_text)
|
||||
result.file.delete()
|
||||
result.file.delete()
|
||||
|
||||
PutFile.drop_collection()
|
||||
StreamFile.drop_collection()
|
||||
@@ -785,5 +844,66 @@ class FieldTest(unittest.TestCase):
|
||||
self.assertEqual(d2.data, {})
|
||||
self.assertEqual(d2.data2, {})
|
||||
|
||||
def test_mapfield(self):
|
||||
"""Ensure that the MapField handles the declared type."""
|
||||
|
||||
class Simple(Document):
|
||||
mapping = MapField(IntField())
|
||||
|
||||
Simple.drop_collection()
|
||||
|
||||
e = Simple()
|
||||
e.mapping['someint'] = 1
|
||||
e.save()
|
||||
|
||||
def create_invalid_mapping():
|
||||
e.mapping['somestring'] = "abc"
|
||||
e.save()
|
||||
|
||||
self.assertRaises(ValidationError, create_invalid_mapping)
|
||||
|
||||
def create_invalid_class():
|
||||
class NoDeclaredType(Document):
|
||||
mapping = MapField()
|
||||
|
||||
self.assertRaises(ValidationError, create_invalid_class)
|
||||
|
||||
Simple.drop_collection()
|
||||
|
||||
def test_complex_mapfield(self):
|
||||
"""Ensure that the MapField can handle complex declared types."""
|
||||
|
||||
class SettingBase(EmbeddedDocument):
|
||||
pass
|
||||
|
||||
class StringSetting(SettingBase):
|
||||
value = StringField()
|
||||
|
||||
class IntegerSetting(SettingBase):
|
||||
value = IntField()
|
||||
|
||||
class Extensible(Document):
|
||||
mapping = MapField(EmbeddedDocumentField(SettingBase))
|
||||
|
||||
Extensible.drop_collection()
|
||||
|
||||
e = Extensible()
|
||||
e.mapping['somestring'] = StringSetting(value='foo')
|
||||
e.mapping['someint'] = IntegerSetting(value=42)
|
||||
e.save()
|
||||
|
||||
e2 = Extensible.objects.get(id=e.id)
|
||||
self.assertTrue(isinstance(e2.mapping['somestring'], StringSetting))
|
||||
self.assertTrue(isinstance(e2.mapping['someint'], IntegerSetting))
|
||||
|
||||
def create_invalid_mapping():
|
||||
e.mapping['someint'] = 123
|
||||
e.save()
|
||||
|
||||
self.assertRaises(ValidationError, create_invalid_mapping)
|
||||
|
||||
Extensible.drop_collection()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -5,8 +5,9 @@ import unittest
|
||||
import pymongo
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from mongoengine.queryset import (QuerySet, MultipleObjectsReturned,
|
||||
DoesNotExist, QueryFieldList)
|
||||
from mongoengine.queryset import (QuerySet, QuerySetManager,
|
||||
MultipleObjectsReturned, DoesNotExist,
|
||||
QueryFieldList)
|
||||
from mongoengine import *
|
||||
|
||||
|
||||
@@ -105,6 +106,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
people = list(self.Person.objects[1:1])
|
||||
self.assertEqual(len(people), 0)
|
||||
|
||||
# Test slice out of range
|
||||
people = list(self.Person.objects[80000:80001])
|
||||
self.assertEqual(len(people), 0)
|
||||
|
||||
def test_find_one(self):
|
||||
"""Ensure that a query using find_one returns a valid result.
|
||||
"""
|
||||
@@ -162,7 +167,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
person = self.Person.objects.get(age__lt=30)
|
||||
self.assertEqual(person.name, "User A")
|
||||
|
||||
|
||||
def test_find_array_position(self):
|
||||
"""Ensure that query by array position works.
|
||||
"""
|
||||
@@ -177,7 +182,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
posts = ListField(EmbeddedDocumentField(Post))
|
||||
|
||||
Blog.drop_collection()
|
||||
|
||||
|
||||
Blog.objects.create(tags=['a', 'b'])
|
||||
self.assertEqual(len(Blog.objects(tags__0='a')), 1)
|
||||
self.assertEqual(len(Blog.objects(tags__0='b')), 0)
|
||||
@@ -207,6 +212,55 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
Blog.drop_collection()
|
||||
|
||||
def test_update_array_position(self):
|
||||
"""Ensure that updating by array position works.
|
||||
|
||||
Check update() and update_one() can take syntax like:
|
||||
set__posts__1__comments__1__name="testc"
|
||||
Check that it only works for ListFields.
|
||||
"""
|
||||
class Comment(EmbeddedDocument):
|
||||
name = StringField()
|
||||
|
||||
class Post(EmbeddedDocument):
|
||||
comments = ListField(EmbeddedDocumentField(Comment))
|
||||
|
||||
class Blog(Document):
|
||||
tags = ListField(StringField())
|
||||
posts = ListField(EmbeddedDocumentField(Post))
|
||||
|
||||
Blog.drop_collection()
|
||||
|
||||
comment1 = Comment(name='testa')
|
||||
comment2 = Comment(name='testb')
|
||||
post1 = Post(comments=[comment1, comment2])
|
||||
post2 = Post(comments=[comment2, comment2])
|
||||
blog1 = Blog.objects.create(posts=[post1, post2])
|
||||
blog2 = Blog.objects.create(posts=[post2, post1])
|
||||
|
||||
# Update all of the first comments of second posts of all blogs
|
||||
blog = Blog.objects().update(set__posts__1__comments__0__name="testc")
|
||||
testc_blogs = Blog.objects(posts__1__comments__0__name="testc")
|
||||
self.assertEqual(len(testc_blogs), 2)
|
||||
|
||||
Blog.drop_collection()
|
||||
|
||||
blog1 = Blog.objects.create(posts=[post1, post2])
|
||||
blog2 = Blog.objects.create(posts=[post2, post1])
|
||||
|
||||
# Update only the first blog returned by the query
|
||||
blog = Blog.objects().update_one(
|
||||
set__posts__1__comments__1__name="testc")
|
||||
testc_blogs = Blog.objects(posts__1__comments__1__name="testc")
|
||||
self.assertEqual(len(testc_blogs), 1)
|
||||
|
||||
# Check that using this indexing syntax on a non-list fails
|
||||
def non_list_indexing():
|
||||
Blog.objects().update(set__posts__1__comments__0__name__1="asdf")
|
||||
self.assertRaises(InvalidQueryError, non_list_indexing)
|
||||
|
||||
Blog.drop_collection()
|
||||
|
||||
def test_get_or_create(self):
|
||||
"""Ensure that ``get_or_create`` returns one result or creates a new
|
||||
document.
|
||||
@@ -226,16 +280,16 @@ class QuerySetTest(unittest.TestCase):
|
||||
person, created = self.Person.objects.get_or_create(age=30)
|
||||
self.assertEqual(person.name, "User B")
|
||||
self.assertEqual(created, False)
|
||||
|
||||
|
||||
person, created = self.Person.objects.get_or_create(age__lt=30)
|
||||
self.assertEqual(person.name, "User A")
|
||||
self.assertEqual(created, False)
|
||||
|
||||
|
||||
# Try retrieving when no objects exists - new doc should be created
|
||||
kwargs = dict(age=50, defaults={'name': 'User C'})
|
||||
person, created = self.Person.objects.get_or_create(**kwargs)
|
||||
self.assertEqual(created, True)
|
||||
|
||||
|
||||
person = self.Person.objects.get(age=50)
|
||||
self.assertEqual(person.name, "User C")
|
||||
|
||||
@@ -328,7 +382,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual(obj, person)
|
||||
obj = self.Person.objects(Q(name__iexact='gUIDO VAN rOSSU')).first()
|
||||
self.assertEqual(obj, None)
|
||||
|
||||
|
||||
# Test unsafe expressions
|
||||
person = self.Person(name='Guido van Rossum [.\'Geek\']')
|
||||
person.save()
|
||||
@@ -593,6 +647,81 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
Email.drop_collection()
|
||||
|
||||
def test_slicing_fields(self):
|
||||
"""Ensure that query slicing an array works.
|
||||
"""
|
||||
class Numbers(Document):
|
||||
n = ListField(IntField())
|
||||
|
||||
Numbers.drop_collection()
|
||||
|
||||
numbers = Numbers(n=[0,1,2,3,4,5,-5,-4,-3,-2,-1])
|
||||
numbers.save()
|
||||
|
||||
# first three
|
||||
numbers = Numbers.objects.fields(slice__n=3).get()
|
||||
self.assertEquals(numbers.n, [0, 1, 2])
|
||||
|
||||
# last three
|
||||
numbers = Numbers.objects.fields(slice__n=-3).get()
|
||||
self.assertEquals(numbers.n, [-3, -2, -1])
|
||||
|
||||
# skip 2, limit 3
|
||||
numbers = Numbers.objects.fields(slice__n=[2, 3]).get()
|
||||
self.assertEquals(numbers.n, [2, 3, 4])
|
||||
|
||||
# skip to fifth from last, limit 4
|
||||
numbers = Numbers.objects.fields(slice__n=[-5, 4]).get()
|
||||
self.assertEquals(numbers.n, [-5, -4, -3, -2])
|
||||
|
||||
# skip to fifth from last, limit 10
|
||||
numbers = Numbers.objects.fields(slice__n=[-5, 10]).get()
|
||||
self.assertEquals(numbers.n, [-5, -4, -3, -2, -1])
|
||||
|
||||
# skip to fifth from last, limit 10 dict method
|
||||
numbers = Numbers.objects.fields(n={"$slice": [-5, 10]}).get()
|
||||
self.assertEquals(numbers.n, [-5, -4, -3, -2, -1])
|
||||
|
||||
def test_slicing_nested_fields(self):
|
||||
"""Ensure that query slicing an embedded array works.
|
||||
"""
|
||||
|
||||
class EmbeddedNumber(EmbeddedDocument):
|
||||
n = ListField(IntField())
|
||||
|
||||
class Numbers(Document):
|
||||
embedded = EmbeddedDocumentField(EmbeddedNumber)
|
||||
|
||||
Numbers.drop_collection()
|
||||
|
||||
numbers = Numbers()
|
||||
numbers.embedded = EmbeddedNumber(n=[0,1,2,3,4,5,-5,-4,-3,-2,-1])
|
||||
numbers.save()
|
||||
|
||||
# first three
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=3).get()
|
||||
self.assertEquals(numbers.embedded.n, [0, 1, 2])
|
||||
|
||||
# last three
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=-3).get()
|
||||
self.assertEquals(numbers.embedded.n, [-3, -2, -1])
|
||||
|
||||
# skip 2, limit 3
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=[2, 3]).get()
|
||||
self.assertEquals(numbers.embedded.n, [2, 3, 4])
|
||||
|
||||
# skip to fifth from last, limit 4
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=[-5, 4]).get()
|
||||
self.assertEquals(numbers.embedded.n, [-5, -4, -3, -2])
|
||||
|
||||
# skip to fifth from last, limit 10
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=[-5, 10]).get()
|
||||
self.assertEquals(numbers.embedded.n, [-5, -4, -3, -2, -1])
|
||||
|
||||
# skip to fifth from last, limit 10 dict method
|
||||
numbers = Numbers.objects.fields(embedded__n={"$slice": [-5, 10]}).get()
|
||||
self.assertEquals(numbers.embedded.n, [-5, -4, -3, -2, -1])
|
||||
|
||||
def test_find_embedded(self):
|
||||
"""Ensure that an embedded document is properly returned from a query.
|
||||
"""
|
||||
@@ -674,7 +803,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
posts = [post.id for post in q]
|
||||
published_posts = (post1, post2, post3, post5, post6)
|
||||
self.assertTrue(all(obj.id in posts for obj in published_posts))
|
||||
|
||||
|
||||
|
||||
# Check Q object combination
|
||||
date = datetime(2010, 1, 10)
|
||||
@@ -714,7 +843,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
obj = self.Person.objects(Q(name__not=re.compile('^bob'))).first()
|
||||
self.assertEqual(obj, person)
|
||||
|
||||
|
||||
obj = self.Person.objects(Q(name__not=re.compile('^Gui'))).first()
|
||||
self.assertEqual(obj, None)
|
||||
|
||||
@@ -786,7 +915,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
class BlogPost(Document):
|
||||
name = StringField(db_field='doc-name')
|
||||
comments = ListField(EmbeddedDocumentField(Comment),
|
||||
comments = ListField(EmbeddedDocumentField(Comment),
|
||||
db_field='cmnts')
|
||||
|
||||
BlogPost.drop_collection()
|
||||
@@ -958,7 +1087,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
BlogPost.objects.update_one(unset__hits=1)
|
||||
post.reload()
|
||||
self.assertEqual(post.hits, None)
|
||||
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_update_pull(self):
|
||||
@@ -1027,7 +1156,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
"""
|
||||
|
||||
# run a map/reduce operation spanning all posts
|
||||
results = BlogPost.objects.map_reduce(map_f, reduce_f)
|
||||
results = BlogPost.objects.map_reduce(map_f, reduce_f, "myresults")
|
||||
results = list(results)
|
||||
self.assertEqual(len(results), 4)
|
||||
|
||||
@@ -1038,7 +1167,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual(film.value, 3)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
|
||||
def test_map_reduce_with_custom_object_ids(self):
|
||||
"""Ensure that QuerySet.map_reduce works properly with custom
|
||||
primary keys.
|
||||
@@ -1047,24 +1176,24 @@ class QuerySetTest(unittest.TestCase):
|
||||
class BlogPost(Document):
|
||||
title = StringField(primary_key=True)
|
||||
tags = ListField(StringField())
|
||||
|
||||
|
||||
post1 = BlogPost(title="Post #1", tags=["mongodb", "mongoengine"])
|
||||
post2 = BlogPost(title="Post #2", tags=["django", "mongodb"])
|
||||
post3 = BlogPost(title="Post #3", tags=["hitchcock films"])
|
||||
|
||||
|
||||
post1.save()
|
||||
post2.save()
|
||||
post3.save()
|
||||
|
||||
|
||||
self.assertEqual(BlogPost._fields['title'].db_field, '_id')
|
||||
self.assertEqual(BlogPost._meta['id_field'], 'title')
|
||||
|
||||
|
||||
map_f = """
|
||||
function() {
|
||||
emit(this._id, 1);
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
# reduce to a list of tag ids and counts
|
||||
reduce_f = """
|
||||
function(key, values) {
|
||||
@@ -1075,10 +1204,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
return total;
|
||||
}
|
||||
"""
|
||||
|
||||
results = BlogPost.objects.map_reduce(map_f, reduce_f)
|
||||
|
||||
results = BlogPost.objects.map_reduce(map_f, reduce_f, "myresults")
|
||||
results = list(results)
|
||||
|
||||
|
||||
self.assertEqual(results[0].object, post1)
|
||||
self.assertEqual(results[1].object, post2)
|
||||
self.assertEqual(results[2].object, post3)
|
||||
@@ -1168,7 +1297,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
finalize_f = """
|
||||
function(key, value) {
|
||||
// f(sec_since_epoch,y,z) =
|
||||
// f(sec_since_epoch,y,z) =
|
||||
// log10(z) + ((y*sec_since_epoch) / 45000)
|
||||
z_10 = Math.log(value.z) / Math.log(10);
|
||||
weight = z_10 + ((value.y * value.t_s) / 45000);
|
||||
@@ -1187,6 +1316,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
results = Link.objects.order_by("-value")
|
||||
results = results.map_reduce(map_f,
|
||||
reduce_f,
|
||||
"myresults",
|
||||
finalize_f=finalize_f,
|
||||
scope=scope)
|
||||
results = list(results)
|
||||
@@ -1289,6 +1419,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
class BlogPost(Document):
|
||||
tags = ListField(StringField())
|
||||
deleted = BooleanField(default=False)
|
||||
date = DateTimeField(default=datetime.now)
|
||||
|
||||
@queryset_manager
|
||||
def objects(doc_cls, queryset):
|
||||
@@ -1296,7 +1427,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
@queryset_manager
|
||||
def music_posts(doc_cls, queryset):
|
||||
return queryset(tags='music', deleted=False)
|
||||
return queryset(tags='music', deleted=False).order_by('-date')
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
@@ -1312,7 +1443,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual([p.id for p in BlogPost.objects],
|
||||
[post1.id, post2.id, post3.id])
|
||||
self.assertEqual([p.id for p in BlogPost.music_posts],
|
||||
[post1.id, post2.id])
|
||||
[post2.id, post1.id])
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
@@ -1452,10 +1583,12 @@ class QuerySetTest(unittest.TestCase):
|
||||
class Test(Document):
|
||||
testdict = DictField()
|
||||
|
||||
Test.drop_collection()
|
||||
|
||||
t = Test(testdict={'f': 'Value'})
|
||||
t.save()
|
||||
|
||||
self.assertEqual(len(Test.objects(testdict__f__startswith='Val')), 0)
|
||||
self.assertEqual(len(Test.objects(testdict__f__startswith='Val')), 1)
|
||||
self.assertEqual(len(Test.objects(testdict__f='Value')), 1)
|
||||
Test.drop_collection()
|
||||
|
||||
@@ -1514,12 +1647,12 @@ class QuerySetTest(unittest.TestCase):
|
||||
title = StringField()
|
||||
date = DateTimeField()
|
||||
location = GeoPointField()
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
Event.drop_collection()
|
||||
|
||||
|
||||
event1 = Event(title="Coltrane Motion @ Double Door",
|
||||
date=datetime.now() - timedelta(days=1),
|
||||
location=[41.909889, -87.677137])
|
||||
@@ -1529,7 +1662,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
event3 = Event(title="Coltrane Motion @ Empty Bottle",
|
||||
date=datetime.now(),
|
||||
location=[41.900474, -87.686638])
|
||||
|
||||
|
||||
event1.save()
|
||||
event2.save()
|
||||
event3.save()
|
||||
@@ -1541,7 +1674,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual(events.count(), 3)
|
||||
self.assertEqual(list(events), [event1, event3, event2])
|
||||
|
||||
# find events within 5 miles of pitchfork office, chicago
|
||||
# find events within 5 degrees of pitchfork office, chicago
|
||||
point_and_distance = [[41.9120459, -87.67892], 5]
|
||||
events = Event.objects(location__within_distance=point_and_distance)
|
||||
self.assertEqual(events.count(), 2)
|
||||
@@ -1549,24 +1682,24 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertTrue(event2 not in events)
|
||||
self.assertTrue(event1 in events)
|
||||
self.assertTrue(event3 in events)
|
||||
|
||||
|
||||
# ensure ordering is respected by "near"
|
||||
events = Event.objects(location__near=[41.9120459, -87.67892])
|
||||
events = events.order_by("-date")
|
||||
self.assertEqual(events.count(), 3)
|
||||
self.assertEqual(list(events), [event3, event1, event2])
|
||||
|
||||
# find events around san francisco
|
||||
|
||||
# find events within 10 degrees of san francisco
|
||||
point_and_distance = [[37.7566023, -122.415579], 10]
|
||||
events = Event.objects(location__within_distance=point_and_distance)
|
||||
self.assertEqual(events.count(), 1)
|
||||
self.assertEqual(events[0], event2)
|
||||
|
||||
# find events within 1 mile of greenpoint, broolyn, nyc, ny
|
||||
|
||||
# find events within 1 degree of greenpoint, broolyn, nyc, ny
|
||||
point_and_distance = [[40.7237134, -73.9509714], 1]
|
||||
events = Event.objects(location__within_distance=point_and_distance)
|
||||
self.assertEqual(events.count(), 0)
|
||||
|
||||
|
||||
# ensure ordering is respected by "within_distance"
|
||||
point_and_distance = [[41.9120459, -87.67892], 10]
|
||||
events = Event.objects(location__within_distance=point_and_distance)
|
||||
@@ -1579,9 +1712,61 @@ class QuerySetTest(unittest.TestCase):
|
||||
events = Event.objects(location__within_box=box)
|
||||
self.assertEqual(events.count(), 1)
|
||||
self.assertEqual(events[0].id, event2.id)
|
||||
|
||||
|
||||
Event.drop_collection()
|
||||
|
||||
def test_spherical_geospatial_operators(self):
|
||||
"""Ensure that spherical geospatial queries are working
|
||||
"""
|
||||
class Point(Document):
|
||||
location = GeoPointField()
|
||||
|
||||
Point.drop_collection()
|
||||
|
||||
# These points are one degree apart, which (according to Google Maps)
|
||||
# is about 110 km apart at this place on the Earth.
|
||||
north_point = Point(location=[-122, 38]) # Near Concord, CA
|
||||
south_point = Point(location=[-122, 37]) # Near Santa Cruz, CA
|
||||
north_point.save()
|
||||
south_point.save()
|
||||
|
||||
earth_radius = 6378.009; # in km (needs to be a float for dividing by)
|
||||
|
||||
# Finds both points because they are within 60 km of the reference
|
||||
# point equidistant between them.
|
||||
points = Point.objects(location__near_sphere=[-122, 37.5])
|
||||
self.assertEqual(points.count(), 2)
|
||||
|
||||
# Same behavior for _within_spherical_distance
|
||||
points = Point.objects(
|
||||
location__within_spherical_distance=[[-122, 37.5], 60/earth_radius]
|
||||
);
|
||||
self.assertEqual(points.count(), 2)
|
||||
|
||||
# Finds both points, but orders the north point first because it's
|
||||
# closer to the reference point to the north.
|
||||
points = Point.objects(location__near_sphere=[-122, 38.5])
|
||||
self.assertEqual(points.count(), 2)
|
||||
self.assertEqual(points[0].id, north_point.id)
|
||||
self.assertEqual(points[1].id, south_point.id)
|
||||
|
||||
# Finds both points, but orders the south point first because it's
|
||||
# closer to the reference point to the south.
|
||||
points = Point.objects(location__near_sphere=[-122, 36.5])
|
||||
self.assertEqual(points.count(), 2)
|
||||
self.assertEqual(points[0].id, south_point.id)
|
||||
self.assertEqual(points[1].id, north_point.id)
|
||||
|
||||
# Finds only one point because only the first point is within 60km of
|
||||
# the reference point to the south.
|
||||
points = Point.objects(
|
||||
location__within_spherical_distance=[[-122, 36.5], 60/earth_radius]
|
||||
);
|
||||
self.assertEqual(points.count(), 1)
|
||||
self.assertEqual(points[0].id, south_point.id)
|
||||
|
||||
Point.drop_collection()
|
||||
|
||||
def test_custom_querysets(self):
|
||||
"""Ensure that custom QuerySet classes may be used.
|
||||
"""
|
||||
@@ -1602,6 +1787,53 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
Post.drop_collection()
|
||||
|
||||
def test_custom_querysets_set_manager_directly(self):
|
||||
"""Ensure that custom QuerySet classes may be used.
|
||||
"""
|
||||
|
||||
class CustomQuerySet(QuerySet):
|
||||
def not_empty(self):
|
||||
return len(self) > 0
|
||||
|
||||
class CustomQuerySetManager(QuerySetManager):
|
||||
queryset_class = CustomQuerySet
|
||||
|
||||
class Post(Document):
|
||||
objects = CustomQuerySetManager()
|
||||
|
||||
Post.drop_collection()
|
||||
|
||||
self.assertTrue(isinstance(Post.objects, CustomQuerySet))
|
||||
self.assertFalse(Post.objects.not_empty())
|
||||
|
||||
Post().save()
|
||||
self.assertTrue(Post.objects.not_empty())
|
||||
|
||||
Post.drop_collection()
|
||||
|
||||
def test_custom_querysets_managers_directly(self):
|
||||
"""Ensure that custom QuerySet classes may be used.
|
||||
"""
|
||||
|
||||
class CustomQuerySetManager(QuerySetManager):
|
||||
|
||||
@staticmethod
|
||||
def get_queryset(doc_cls, queryset):
|
||||
return queryset(is_published=True)
|
||||
|
||||
class Post(Document):
|
||||
is_published = BooleanField(default=False)
|
||||
published = CustomQuerySetManager()
|
||||
|
||||
Post.drop_collection()
|
||||
|
||||
Post().save()
|
||||
Post(is_published=True).save()
|
||||
self.assertEquals(Post.objects.count(), 2)
|
||||
self.assertEquals(Post.published.count(), 1)
|
||||
|
||||
Post.drop_collection()
|
||||
|
||||
def test_call_after_limits_set(self):
|
||||
"""Ensure that re-filtering after slicing works
|
||||
"""
|
||||
@@ -1637,6 +1869,35 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
Number.drop_collection()
|
||||
|
||||
def test_clone(self):
|
||||
"""Ensure that cloning clones complex querysets
|
||||
"""
|
||||
class Number(Document):
|
||||
n = IntField()
|
||||
|
||||
Number.drop_collection()
|
||||
|
||||
for i in xrange(1, 101):
|
||||
t = Number(n=i)
|
||||
t.save()
|
||||
|
||||
test = Number.objects
|
||||
test2 = test.clone()
|
||||
self.assertFalse(test == test2)
|
||||
self.assertEqual(test.count(), test2.count())
|
||||
|
||||
test = test.filter(n__gt=11)
|
||||
test2 = test.clone()
|
||||
self.assertFalse(test == test2)
|
||||
self.assertEqual(test.count(), test2.count())
|
||||
|
||||
test = test.limit(10)
|
||||
test2 = test.clone()
|
||||
self.assertFalse(test == test2)
|
||||
self.assertEqual(test.count(), test2.count())
|
||||
|
||||
Number.drop_collection()
|
||||
|
||||
def test_unset_reference(self):
|
||||
class Comment(Document):
|
||||
text = StringField()
|
||||
@@ -1658,6 +1919,39 @@ class QuerySetTest(unittest.TestCase):
|
||||
Comment.drop_collection()
|
||||
Post.drop_collection()
|
||||
|
||||
def test_order_works_with_custom_db_field_names(self):
|
||||
class Number(Document):
|
||||
n = IntField(db_field='number')
|
||||
|
||||
Number.drop_collection()
|
||||
|
||||
n2 = Number.objects.create(n=2)
|
||||
n1 = Number.objects.create(n=1)
|
||||
|
||||
self.assertEqual(list(Number.objects), [n2,n1])
|
||||
self.assertEqual(list(Number.objects.order_by('n')), [n1,n2])
|
||||
|
||||
Number.drop_collection()
|
||||
|
||||
def test_order_works_with_primary(self):
|
||||
"""Ensure that order_by and primary work.
|
||||
"""
|
||||
class Number(Document):
|
||||
n = IntField(primary_key=True)
|
||||
|
||||
Number.drop_collection()
|
||||
|
||||
Number(n=1).save()
|
||||
Number(n=2).save()
|
||||
Number(n=3).save()
|
||||
|
||||
numbers = [n.n for n in Number.objects.order_by('-n')]
|
||||
self.assertEquals([3, 2, 1], numbers)
|
||||
|
||||
numbers = [n.n for n in Number.objects.order_by('+n')]
|
||||
self.assertEquals([1, 2, 3], numbers)
|
||||
Number.drop_collection()
|
||||
|
||||
|
||||
class QTest(unittest.TestCase):
|
||||
|
||||
@@ -1679,7 +1973,7 @@ class QTest(unittest.TestCase):
|
||||
|
||||
query = {'age': {'$gte': 18}, 'name': 'test'}
|
||||
self.assertEqual((q1 & q2 & q3 & q4 & q5).to_query(Person), query)
|
||||
|
||||
|
||||
def test_q_with_dbref(self):
|
||||
"""Ensure Q objects handle DBRefs correctly"""
|
||||
connect(db='mongoenginetest')
|
||||
@@ -1721,7 +2015,7 @@ class QTest(unittest.TestCase):
|
||||
query = Q(x__lt=100) & Q(y__ne='NotMyString')
|
||||
query &= Q(y__in=['a', 'b', 'c']) & Q(x__gt=-100)
|
||||
mongo_query = {
|
||||
'x': {'$lt': 100, '$gt': -100},
|
||||
'x': {'$lt': 100, '$gt': -100},
|
||||
'y': {'$ne': 'NotMyString', '$in': ['a', 'b', 'c']},
|
||||
}
|
||||
self.assertEqual(query.to_query(TestDoc), mongo_query)
|
||||
@@ -1795,6 +2089,30 @@ class QTest(unittest.TestCase):
|
||||
for condition in conditions:
|
||||
self.assertTrue(condition in query['$or'])
|
||||
|
||||
|
||||
def test_q_clone(self):
|
||||
|
||||
class TestDoc(Document):
|
||||
x = IntField()
|
||||
|
||||
TestDoc.drop_collection()
|
||||
for i in xrange(1, 101):
|
||||
t = TestDoc(x=i)
|
||||
t.save()
|
||||
|
||||
# Check normal cases work without an error
|
||||
test = TestDoc.objects(Q(x__lt=7) & Q(x__gt=3))
|
||||
|
||||
self.assertEqual(test.count(), 3)
|
||||
|
||||
test2 = test.clone()
|
||||
self.assertEqual(test2.count(), 3)
|
||||
self.assertFalse(test2 == test)
|
||||
|
||||
test2.filter(x=6)
|
||||
self.assertEqual(test2.count(), 1)
|
||||
self.assertEqual(test.count(), 3)
|
||||
|
||||
class QueryFieldListTest(unittest.TestCase):
|
||||
def test_empty(self):
|
||||
q = QueryFieldList()
|
||||
@@ -1805,51 +2123,52 @@ class QueryFieldListTest(unittest.TestCase):
|
||||
|
||||
def test_include_include(self):
|
||||
q = QueryFieldList()
|
||||
q += QueryFieldList(fields=['a', 'b'], direction=QueryFieldList.ONLY)
|
||||
q += QueryFieldList(fields=['a', 'b'], value=QueryFieldList.ONLY)
|
||||
self.assertEqual(q.as_dict(), {'a': True, 'b': True})
|
||||
q += QueryFieldList(fields=['b', 'c'], direction=QueryFieldList.ONLY)
|
||||
q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
|
||||
self.assertEqual(q.as_dict(), {'b': True})
|
||||
|
||||
def test_include_exclude(self):
|
||||
q = QueryFieldList()
|
||||
q += QueryFieldList(fields=['a', 'b'], direction=QueryFieldList.ONLY)
|
||||
q += QueryFieldList(fields=['a', 'b'], value=QueryFieldList.ONLY)
|
||||
self.assertEqual(q.as_dict(), {'a': True, 'b': True})
|
||||
q += QueryFieldList(fields=['b', 'c'], direction=QueryFieldList.EXCLUDE)
|
||||
q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.EXCLUDE)
|
||||
self.assertEqual(q.as_dict(), {'a': True})
|
||||
|
||||
def test_exclude_exclude(self):
|
||||
q = QueryFieldList()
|
||||
q += QueryFieldList(fields=['a', 'b'], direction=QueryFieldList.EXCLUDE)
|
||||
q += QueryFieldList(fields=['a', 'b'], value=QueryFieldList.EXCLUDE)
|
||||
self.assertEqual(q.as_dict(), {'a': False, 'b': False})
|
||||
q += QueryFieldList(fields=['b', 'c'], direction=QueryFieldList.EXCLUDE)
|
||||
q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.EXCLUDE)
|
||||
self.assertEqual(q.as_dict(), {'a': False, 'b': False, 'c': False})
|
||||
|
||||
def test_exclude_include(self):
|
||||
q = QueryFieldList()
|
||||
q += QueryFieldList(fields=['a', 'b'], direction=QueryFieldList.EXCLUDE)
|
||||
q += QueryFieldList(fields=['a', 'b'], value=QueryFieldList.EXCLUDE)
|
||||
self.assertEqual(q.as_dict(), {'a': False, 'b': False})
|
||||
q += QueryFieldList(fields=['b', 'c'], direction=QueryFieldList.ONLY)
|
||||
q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
|
||||
self.assertEqual(q.as_dict(), {'c': True})
|
||||
|
||||
def test_always_include(self):
|
||||
q = QueryFieldList(always_include=['x', 'y'])
|
||||
q += QueryFieldList(fields=['a', 'b', 'x'], direction=QueryFieldList.EXCLUDE)
|
||||
q += QueryFieldList(fields=['b', 'c'], direction=QueryFieldList.ONLY)
|
||||
q += QueryFieldList(fields=['a', 'b', 'x'], value=QueryFieldList.EXCLUDE)
|
||||
q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
|
||||
self.assertEqual(q.as_dict(), {'x': True, 'y': True, 'c': True})
|
||||
|
||||
|
||||
def test_reset(self):
|
||||
q = QueryFieldList(always_include=['x', 'y'])
|
||||
q += QueryFieldList(fields=['a', 'b', 'x'], direction=QueryFieldList.EXCLUDE)
|
||||
q += QueryFieldList(fields=['b', 'c'], direction=QueryFieldList.ONLY)
|
||||
q += QueryFieldList(fields=['a', 'b', 'x'], value=QueryFieldList.EXCLUDE)
|
||||
q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
|
||||
self.assertEqual(q.as_dict(), {'x': True, 'y': True, 'c': True})
|
||||
q.reset()
|
||||
self.assertFalse(q)
|
||||
q += QueryFieldList(fields=['b', 'c'], direction=QueryFieldList.ONLY)
|
||||
q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
|
||||
self.assertEqual(q.as_dict(), {'x': True, 'y': True, 'b': True, 'c': True})
|
||||
|
||||
|
||||
|
||||
def test_using_a_slice(self):
|
||||
q = QueryFieldList()
|
||||
q += QueryFieldList(fields=['a'], value={"$slice": 5})
|
||||
self.assertEqual(q.as_dict(), {'a': {"$slice": 5}})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user