Merge branch 'v0.4' of git://github.com/hmarr/mongoengine into v0.4
Conflicts: docs/changelog.rst mongoengine/base.py mongoengine/queryset.py
This commit is contained in:
@@ -200,6 +200,37 @@ class DocumentTest(unittest.TestCase):
|
||||
Person.drop_collection()
|
||||
self.assertFalse(collection in self.db.collection_names())
|
||||
|
||||
def test_inherited_collections(self):
|
||||
"""Ensure that subclassed documents don't override parents' collections.
|
||||
"""
|
||||
class Drink(Document):
|
||||
name = StringField()
|
||||
|
||||
class AlcoholicDrink(Drink):
|
||||
meta = {'collection': 'booze'}
|
||||
|
||||
class Drinker(Document):
|
||||
drink = GenericReferenceField()
|
||||
|
||||
Drink.drop_collection()
|
||||
AlcoholicDrink.drop_collection()
|
||||
Drinker.drop_collection()
|
||||
|
||||
red_bull = Drink(name='Red Bull')
|
||||
red_bull.save()
|
||||
|
||||
programmer = Drinker(drink=red_bull)
|
||||
programmer.save()
|
||||
|
||||
beer = AlcoholicDrink(name='Beer')
|
||||
beer.save()
|
||||
|
||||
real_person = Drinker(drink=beer)
|
||||
real_person.save()
|
||||
|
||||
self.assertEqual(Drinker.objects[0].drink.name, red_bull.name)
|
||||
self.assertEqual(Drinker.objects[1].drink.name, beer.name)
|
||||
|
||||
def test_capped_collection(self):
|
||||
"""Ensure that capped collections work properly.
|
||||
"""
|
||||
|
||||
@@ -189,6 +189,9 @@ class FieldTest(unittest.TestCase):
|
||||
def test_list_validation(self):
|
||||
"""Ensure that a list field only accepts lists with valid elements.
|
||||
"""
|
||||
class User(Document):
|
||||
pass
|
||||
|
||||
class Comment(EmbeddedDocument):
|
||||
content = StringField()
|
||||
|
||||
@@ -196,6 +199,7 @@ class FieldTest(unittest.TestCase):
|
||||
content = StringField()
|
||||
comments = ListField(EmbeddedDocumentField(Comment))
|
||||
tags = ListField(StringField())
|
||||
authors = ListField(ReferenceField(User))
|
||||
|
||||
post = BlogPost(content='Went for a walk today...')
|
||||
post.validate()
|
||||
@@ -210,15 +214,21 @@ class FieldTest(unittest.TestCase):
|
||||
post.tags = ('fun', 'leisure')
|
||||
post.validate()
|
||||
|
||||
comments = [Comment(content='Good for you'), Comment(content='Yay.')]
|
||||
post.comments = comments
|
||||
post.validate()
|
||||
|
||||
post.comments = ['a']
|
||||
self.assertRaises(ValidationError, post.validate)
|
||||
post.comments = 'yay'
|
||||
self.assertRaises(ValidationError, post.validate)
|
||||
|
||||
comments = [Comment(content='Good for you'), Comment(content='Yay.')]
|
||||
post.comments = comments
|
||||
post.validate()
|
||||
|
||||
post.authors = [Comment()]
|
||||
self.assertRaises(ValidationError, post.validate)
|
||||
|
||||
post.authors = [User()]
|
||||
post.validate()
|
||||
|
||||
def test_sorted_list_sorting(self):
|
||||
"""Ensure that a sorted list field properly sorts values.
|
||||
"""
|
||||
@@ -395,14 +405,54 @@ class FieldTest(unittest.TestCase):
|
||||
class Employee(Document):
|
||||
name = StringField()
|
||||
boss = ReferenceField('self')
|
||||
friends = ListField(ReferenceField('self'))
|
||||
|
||||
bill = Employee(name='Bill Lumbergh')
|
||||
bill.save()
|
||||
peter = Employee(name='Peter Gibbons', boss=bill)
|
||||
|
||||
michael = Employee(name='Michael Bolton')
|
||||
michael.save()
|
||||
|
||||
samir = Employee(name='Samir Nagheenanajar')
|
||||
samir.save()
|
||||
|
||||
friends = [michael, samir]
|
||||
peter = Employee(name='Peter Gibbons', boss=bill, friends=friends)
|
||||
peter.save()
|
||||
|
||||
peter = Employee.objects.with_id(peter.id)
|
||||
self.assertEqual(peter.boss, bill)
|
||||
self.assertEqual(peter.friends, friends)
|
||||
|
||||
def test_recursive_embedding(self):
|
||||
"""Ensure that EmbeddedDocumentFields can contain their own documents.
|
||||
"""
|
||||
class Tree(Document):
|
||||
name = StringField()
|
||||
children = ListField(EmbeddedDocumentField('TreeNode'))
|
||||
|
||||
class TreeNode(EmbeddedDocument):
|
||||
name = StringField()
|
||||
children = ListField(EmbeddedDocumentField('self'))
|
||||
|
||||
tree = Tree(name="Tree")
|
||||
|
||||
first_child = TreeNode(name="Child 1")
|
||||
tree.children.append(first_child)
|
||||
|
||||
second_child = TreeNode(name="Child 2")
|
||||
first_child.children.append(second_child)
|
||||
|
||||
third_child = TreeNode(name="Child 3")
|
||||
first_child.children.append(third_child)
|
||||
|
||||
tree.save()
|
||||
|
||||
tree_obj = Tree.objects.first()
|
||||
self.assertEqual(len(tree.children), 1)
|
||||
self.assertEqual(tree.children[0].name, first_child.name)
|
||||
self.assertEqual(tree.children[0].children[0].name, second_child.name)
|
||||
self.assertEqual(tree.children[0].children[1].name, third_child.name)
|
||||
|
||||
def test_undefined_reference(self):
|
||||
"""Ensure that ReferenceFields may reference undefined Documents.
|
||||
|
||||
@@ -53,9 +53,6 @@ class QuerySetTest(unittest.TestCase):
|
||||
person2 = self.Person(name="User B", age=30)
|
||||
person2.save()
|
||||
|
||||
q1 = Q(name='test')
|
||||
q2 = Q(age__gte=18)
|
||||
|
||||
# Find all people in the collection
|
||||
people = self.Person.objects
|
||||
self.assertEqual(len(people), 2)
|
||||
@@ -156,7 +153,8 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
# Retrieve the first person from the database
|
||||
self.assertRaises(MultipleObjectsReturned, self.Person.objects.get)
|
||||
self.assertRaises(self.Person.MultipleObjectsReturned, self.Person.objects.get)
|
||||
self.assertRaises(self.Person.MultipleObjectsReturned,
|
||||
self.Person.objects.get)
|
||||
|
||||
# Use a query to filter the people found to just person2
|
||||
person = self.Person.objects.get(age=30)
|
||||
@@ -234,7 +232,8 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual(created, False)
|
||||
|
||||
# Try retrieving when no objects exists - new doc should be created
|
||||
person, created = self.Person.objects.get_or_create(age=50, defaults={'name': 'User C'})
|
||||
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)
|
||||
@@ -337,6 +336,18 @@ class QuerySetTest(unittest.TestCase):
|
||||
obj = self.Person.objects(Q(name__icontains='[.\'Geek')).first()
|
||||
self.assertEqual(obj, person)
|
||||
|
||||
def test_not(self):
|
||||
"""Ensure that the __not operator works as expected.
|
||||
"""
|
||||
alice = self.Person(name='Alice', age=25)
|
||||
alice.save()
|
||||
|
||||
obj = self.Person.objects(name__iexact='alice').first()
|
||||
self.assertEqual(obj, alice)
|
||||
|
||||
obj = self.Person.objects(name__not__iexact='alice').first()
|
||||
self.assertEqual(obj, None)
|
||||
|
||||
def test_filter_chaining(self):
|
||||
"""Ensure filters can be chained together.
|
||||
"""
|
||||
@@ -546,9 +557,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
obj = self.Person.objects(Q(name=re.compile('^gui', re.I))).first()
|
||||
self.assertEqual(obj, person)
|
||||
|
||||
obj = self.Person.objects(Q(name__ne=re.compile('^bob'))).first()
|
||||
obj = self.Person.objects(Q(name__not=re.compile('^bob'))).first()
|
||||
self.assertEqual(obj, person)
|
||||
obj = self.Person.objects(Q(name__ne=re.compile('^Gui'))).first()
|
||||
|
||||
obj = self.Person.objects(Q(name__not=re.compile('^Gui'))).first()
|
||||
self.assertEqual(obj, None)
|
||||
|
||||
def test_q_lists(self):
|
||||
@@ -717,6 +729,11 @@ class QuerySetTest(unittest.TestCase):
|
||||
post.reload()
|
||||
self.assertEqual(post.tags, tags)
|
||||
|
||||
BlogPost.objects.update_one(add_to_set__tags='unique')
|
||||
BlogPost.objects.update_one(add_to_set__tags='unique')
|
||||
post.reload()
|
||||
self.assertEqual(post.tags.count('unique'), 1)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_update_pull(self):
|
||||
@@ -968,7 +985,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
BlogPost(hits=1, tags=['music', 'film', 'actors']).save()
|
||||
BlogPost(hits=2, tags=['music']).save()
|
||||
BlogPost(hits=3, tags=['music', 'actors']).save()
|
||||
BlogPost(hits=2, tags=['music', 'actors']).save()
|
||||
|
||||
f = BlogPost.objects.item_frequencies('tags')
|
||||
f = dict((key, int(val)) for key, val in f.items())
|
||||
@@ -990,6 +1007,13 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertAlmostEqual(f['actors'], 2.0/6.0)
|
||||
self.assertAlmostEqual(f['film'], 1.0/6.0)
|
||||
|
||||
# Check item_frequencies works for non-list fields
|
||||
f = BlogPost.objects.item_frequencies('hits')
|
||||
f = dict((key, int(val)) for key, val in f.items())
|
||||
self.assertEqual(set(['1', '2']), set(f.keys()))
|
||||
self.assertEqual(f['1'], 1)
|
||||
self.assertEqual(f['2'], 2)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_average(self):
|
||||
@@ -1026,9 +1050,13 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.Person(name='Mr Orange', age=20).save()
|
||||
self.Person(name='Mr White', age=20).save()
|
||||
self.Person(name='Mr Orange', age=30).save()
|
||||
self.assertEqual(self.Person.objects.distinct('name'),
|
||||
['Mr Orange', 'Mr White'])
|
||||
self.assertEqual(self.Person.objects.distinct('age'), [20, 30])
|
||||
self.Person(name='Mr Pink', age=30).save()
|
||||
self.assertEqual(set(self.Person.objects.distinct('name')),
|
||||
set(['Mr Orange', 'Mr White', 'Mr Pink']))
|
||||
self.assertEqual(set(self.Person.objects.distinct('age')),
|
||||
set([20, 30]))
|
||||
self.assertEqual(set(self.Person.objects(age=30).distinct('name')),
|
||||
set(['Mr Orange', 'Mr Pink']))
|
||||
|
||||
def test_custom_manager(self):
|
||||
"""Ensure that custom QuerySetManager instances work as expected.
|
||||
@@ -1330,43 +1358,6 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
class QTest(unittest.TestCase):
|
||||
|
||||
def test_or_and(self):
|
||||
"""Ensure that Q objects may be combined correctly.
|
||||
"""
|
||||
q1 = Q(name='test')
|
||||
q2 = Q(age__gte=18)
|
||||
|
||||
query = ['(', {'name': 'test'}, '||', {'age__gte': 18}, ')']
|
||||
self.assertEqual((q1 | q2).query, query)
|
||||
|
||||
query = ['(', {'name': 'test'}, '&&', {'age__gte': 18}, ')']
|
||||
self.assertEqual((q1 & q2).query, query)
|
||||
|
||||
query = ['(', '(', {'name': 'test'}, '&&', {'age__gte': 18}, ')', '||',
|
||||
{'name': 'example'}, ')']
|
||||
self.assertEqual((q1 & q2 | Q(name='example')).query, query)
|
||||
|
||||
def test_item_query_as_js(self):
|
||||
"""Ensure that the _item_query_as_js utilitiy method works properly.
|
||||
"""
|
||||
q = Q()
|
||||
examples = [
|
||||
|
||||
({'name': 'test'}, ('((this.name instanceof Array) && '
|
||||
'this.name.indexOf(i0f0) != -1) || this.name == i0f0'),
|
||||
{'i0f0': 'test'}),
|
||||
({'age': {'$gt': 18}}, 'this.age > i0f0o0', {'i0f0o0': 18}),
|
||||
({'name': 'test', 'age': {'$gt': 18, '$lte': 65}},
|
||||
('this.age <= i0f0o0 && this.age > i0f0o1 && '
|
||||
'((this.name instanceof Array) && '
|
||||
'this.name.indexOf(i0f1) != -1) || this.name == i0f1'),
|
||||
{'i0f0o0': 65, 'i0f0o1': 18, 'i0f1': 'test'}),
|
||||
]
|
||||
for item, js, scope in examples:
|
||||
test_scope = {}
|
||||
self.assertEqual(q._item_query_as_js(item, test_scope, 0), js)
|
||||
self.assertEqual(scope, test_scope)
|
||||
|
||||
def test_empty_q(self):
|
||||
"""Ensure that empty Q objects won't hurt.
|
||||
"""
|
||||
@@ -1376,11 +1367,15 @@ class QTest(unittest.TestCase):
|
||||
q4 = Q(name='test')
|
||||
q5 = Q()
|
||||
|
||||
query = ['(', {'age__gte': 18}, '||', {'name': 'test'}, ')']
|
||||
self.assertEqual((q1 | q2 | q3 | q4 | q5).query, query)
|
||||
class Person(Document):
|
||||
name = StringField()
|
||||
age = IntField()
|
||||
|
||||
query = ['(', {'age__gte': 18}, '&&', {'name': 'test'}, ')']
|
||||
self.assertEqual((q1 & q2 & q3 & q4 & q5).query, query)
|
||||
query = {'$or': [{'age': {'$gte': 18}}, {'name': 'test'}]}
|
||||
self.assertEqual((q1 | q2 | q3 | q4 | q5).to_query(Person), query)
|
||||
|
||||
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"""
|
||||
@@ -1398,6 +1393,105 @@ class QTest(unittest.TestCase):
|
||||
self.assertEqual(Post.objects.filter(created_user=user).count(), 1)
|
||||
self.assertEqual(Post.objects.filter(Q(created_user=user)).count(), 1)
|
||||
|
||||
def test_and_combination(self):
|
||||
"""Ensure that Q-objects correctly AND together.
|
||||
"""
|
||||
class TestDoc(Document):
|
||||
x = IntField()
|
||||
y = StringField()
|
||||
|
||||
# Check than an error is raised when conflicting queries are anded
|
||||
def invalid_combination():
|
||||
query = Q(x__lt=7) & Q(x__lt=3)
|
||||
query.to_query(TestDoc)
|
||||
self.assertRaises(InvalidQueryError, invalid_combination)
|
||||
|
||||
# Check normal cases work without an error
|
||||
query = Q(x__lt=7) & Q(x__gt=3)
|
||||
|
||||
q1 = Q(x__lt=7)
|
||||
q2 = Q(x__gt=3)
|
||||
query = (q1 & q2).to_query(TestDoc)
|
||||
self.assertEqual(query, {'x': {'$lt': 7, '$gt': 3}})
|
||||
|
||||
# More complex nested example
|
||||
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},
|
||||
'y': {'$ne': 'NotMyString', '$in': ['a', 'b', 'c']},
|
||||
}
|
||||
self.assertEqual(query.to_query(TestDoc), mongo_query)
|
||||
|
||||
def test_or_combination(self):
|
||||
"""Ensure that Q-objects correctly OR together.
|
||||
"""
|
||||
class TestDoc(Document):
|
||||
x = IntField()
|
||||
|
||||
q1 = Q(x__lt=3)
|
||||
q2 = Q(x__gt=7)
|
||||
query = (q1 | q2).to_query(TestDoc)
|
||||
self.assertEqual(query, {
|
||||
'$or': [
|
||||
{'x': {'$lt': 3}},
|
||||
{'x': {'$gt': 7}},
|
||||
]
|
||||
})
|
||||
|
||||
def test_and_or_combination(self):
|
||||
"""Ensure that Q-objects handle ANDing ORed components.
|
||||
"""
|
||||
class TestDoc(Document):
|
||||
x = IntField()
|
||||
y = BooleanField()
|
||||
|
||||
query = (Q(x__gt=0) | Q(x__exists=False))
|
||||
query &= Q(x__lt=100)
|
||||
self.assertEqual(query.to_query(TestDoc), {
|
||||
'$or': [
|
||||
{'x': {'$lt': 100, '$gt': 0}},
|
||||
{'x': {'$lt': 100, '$exists': False}},
|
||||
]
|
||||
})
|
||||
|
||||
q1 = (Q(x__gt=0) | Q(x__exists=False))
|
||||
q2 = (Q(x__lt=100) | Q(y=True))
|
||||
query = (q1 & q2).to_query(TestDoc)
|
||||
|
||||
self.assertEqual(['$or'], query.keys())
|
||||
conditions = [
|
||||
{'x': {'$lt': 100, '$gt': 0}},
|
||||
{'x': {'$lt': 100, '$exists': False}},
|
||||
{'x': {'$gt': 0}, 'y': True},
|
||||
{'x': {'$exists': False}, 'y': True},
|
||||
]
|
||||
self.assertEqual(len(conditions), len(query['$or']))
|
||||
for condition in conditions:
|
||||
self.assertTrue(condition in query['$or'])
|
||||
|
||||
def test_or_and_or_combination(self):
|
||||
"""Ensure that Q-objects handle ORing ANDed ORed components. :)
|
||||
"""
|
||||
class TestDoc(Document):
|
||||
x = IntField()
|
||||
y = BooleanField()
|
||||
|
||||
q1 = (Q(x__gt=0) & (Q(y=True) | Q(y__exists=False)))
|
||||
q2 = (Q(x__lt=100) & (Q(y=False) | Q(y__exists=False)))
|
||||
query = (q1 | q2).to_query(TestDoc)
|
||||
|
||||
self.assertEqual(['$or'], query.keys())
|
||||
conditions = [
|
||||
{'x': {'$gt': 0}, 'y': True},
|
||||
{'x': {'$gt': 0}, 'y': {'$exists': False}},
|
||||
{'x': {'$lt': 100}, 'y':False},
|
||||
{'x': {'$lt': 100}, 'y': {'$exists': False}},
|
||||
]
|
||||
self.assertEqual(len(conditions), len(query['$or']))
|
||||
for condition in conditions:
|
||||
self.assertTrue(condition in query['$or'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user