Compare commits

..

7 Commits

Author SHA1 Message Date
Stefan Wojcik
70f1067470 more thorough and isolated unit tests 2017-02-08 00:05:19 -08:00
Stefan Wojcik
fba4477dd8 fix flake8 2017-02-07 23:51:38 -08:00
Stefan Wojcik
6ac7a8eec6 fix chaining of the limit, skip, hint, and batch_size 2017-02-07 23:43:46 -08:00
Stefan Wojcik
bad4e6846a Cleaner ordering code + allow clearing an explicit sort 2017-02-07 23:38:56 -08:00
Stefan Wojcik
0e77efb0c7 Fix BaseQuerySet.comment 2017-02-07 23:34:39 -08:00
Stefan Wojcik
8a6f36256e minor cleanup 2017-02-06 19:29:35 -07:00
Stefan Wojcik
515ffac246 unit test showing issues with skip & limit on an existing queryset 2017-02-06 18:55:15 -07:00
8 changed files with 60 additions and 124 deletions

View File

@@ -4,10 +4,7 @@ Changelog
Development Development
=========== ===========
- (Fill this out as you fix issues and develop your features). - (Fill this out as you fix issues and develop you features).
- Fixed using sets in field choices #1481
- POTENTIAL BREAKING CHANGE: Fixed limit/skip/hint/batch_size chaining #1476
- POTENTIAL BREAKING CHANGE: Changed a public `QuerySet.clone_into` method to a private `QuerySet._clone_into` #1476
- Fixed connecting to a replica set with PyMongo 2.x #1436 - Fixed connecting to a replica set with PyMongo 2.x #1436
- Fixed an obscure error message when filtering by `field__in=non_iterable`. #1237 - Fixed an obscure error message when filtering by `field__in=non_iterable`. #1237

View File

@@ -150,7 +150,7 @@ arguments can be set on all fields:
.. note:: If set, this field is also accessible through the `pk` field. .. note:: If set, this field is also accessible through the `pk` field.
:attr:`choices` (Default: None) :attr:`choices` (Default: None)
An iterable (e.g. list, tuple or set) of choices to which the value of this An iterable (e.g. a list or tuple) of choices to which the value of this
field should be limited. field should be limited.
Can be either be a nested tuples of value (stored in mongo) and a Can be either be a nested tuples of value (stored in mongo) and a

View File

@@ -2,20 +2,6 @@
Upgrading Upgrading
######### #########
Development
***********
(Fill this out whenever you introduce breaking changes to MongoEngine)
This release includes various fixes for the `BaseQuerySet` methods and how they
are chained together. Since version 0.10.1 applying limit/skip/hint/batch_size
to an already-existing queryset wouldn't modify the underlying PyMongo cursor.
This has been fixed now, so you'll need to make sure that your code didn't rely
on the broken implementation.
Additionally, a public `BaseQuerySet.clone_into` has been renamed to a private
`_clone_into`. If you directly used that method in your code, you'll need to
rename its occurrences.
0.11.0 0.11.0
****** ******
This release includes a major rehaul of MongoEngine's code quality and This release includes a major rehaul of MongoEngine's code quality and

View File

@@ -193,8 +193,7 @@ class BaseField(object):
EmbeddedDocument = _import_class('EmbeddedDocument') EmbeddedDocument = _import_class('EmbeddedDocument')
choice_list = self.choices choice_list = self.choices
if isinstance(next(iter(choice_list)), (list, tuple)): if isinstance(choice_list[0], (list, tuple)):
# next(iter) is useful for sets
choice_list = [k for k, _ in choice_list] choice_list = [k for k, _ in choice_list]
# Choices which are other types of Documents # Choices which are other types of Documents

View File

@@ -139,12 +139,12 @@ class URLField(StringField):
# Check first if the scheme is valid # Check first if the scheme is valid
scheme = value.split('://')[0].lower() scheme = value.split('://')[0].lower()
if scheme not in self.schemes: if scheme not in self.schemes:
self.error(u'Invalid scheme {} in URL: {}'.format(scheme, value)) self.error('Invalid scheme {} in URL: {}'.format(scheme, value))
return return
# Then check full URL # Then check full URL
if not self.url_regex.match(value): if not self.url_regex.match(value):
self.error(u'Invalid URL: {}'.format(value)) self.error('Invalid URL: {}'.format(value))
return return

View File

@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import six
from nose.plugins.skip import SkipTest
import datetime import datetime
import unittest import unittest
import uuid import uuid
import math import math
import itertools import itertools
import re import re
from nose.plugins.skip import SkipTest
import six import six
try: try:
@@ -26,13 +27,21 @@ from mongoengine import *
from mongoengine.connection import get_db from mongoengine.connection import get_db
from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList, from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList,
_document_registry) _document_registry)
from mongoengine.errors import NotRegistered, DoesNotExist
from tests.utils import MongoDBTestCase
__all__ = ("FieldTest", "EmbeddedDocumentListFieldTestCase") __all__ = ("FieldTest", "EmbeddedDocumentListFieldTestCase")
class FieldTest(MongoDBTestCase): class FieldTest(unittest.TestCase):
def setUp(self):
connect(db='mongoenginetest')
self.db = get_db()
def tearDown(self):
self.db.drop_collection('fs.files')
self.db.drop_collection('fs.chunks')
self.db.drop_collection('mongoengine.counters')
def test_default_values_nothing_set(self): def test_default_values_nothing_set(self):
"""Ensure that default field values are used when creating a document. """Ensure that default field values are used when creating a document.
@@ -218,9 +227,9 @@ class FieldTest(MongoDBTestCase):
self.assertTrue(isinstance(ret.comp_dt_fld, datetime.datetime)) self.assertTrue(isinstance(ret.comp_dt_fld, datetime.datetime))
def test_not_required_handles_none_from_database(self): def test_not_required_handles_none_from_database(self):
"""Ensure that every field can handle null values from the """Ensure that every fields can handle null values from the database.
database.
""" """
class HandleNoneFields(Document): class HandleNoneFields(Document):
str_fld = StringField(required=True) str_fld = StringField(required=True)
int_fld = IntField(required=True) int_fld = IntField(required=True)
@@ -341,12 +350,11 @@ class FieldTest(MongoDBTestCase):
person.validate() person.validate()
def test_url_validation(self): def test_url_validation(self):
"""Ensure that URLFields validate urls properly.""" """Ensure that URLFields validate urls properly.
"""
class Link(Document): class Link(Document):
url = URLField() url = URLField()
Link.drop_collection()
link = Link() link = Link()
link.url = 'google' link.url = 'google'
self.assertRaises(ValidationError, link.validate) self.assertRaises(ValidationError, link.validate)
@@ -354,27 +362,6 @@ class FieldTest(MongoDBTestCase):
link.url = 'http://www.google.com:8080' link.url = 'http://www.google.com:8080'
link.validate() link.validate()
def test_unicode_url_validation(self):
"""Ensure unicode URLs are validated properly."""
class Link(Document):
url = URLField()
Link.drop_collection()
link = Link()
link.url = u'http://привет.com'
# TODO fix URL validation - this *IS* a valid URL
# For now we just want to make sure that the error message is correct
try:
link.validate()
self.assertTrue(False)
except ValidationError as e:
self.assertEqual(
unicode(e),
u"ValidationError (Link:None) (Invalid URL: http://\u043f\u0440\u0438\u0432\u0435\u0442.com: ['url'])"
)
def test_url_scheme_validation(self): def test_url_scheme_validation(self):
"""Ensure that URLFields validate urls with specific schemes properly. """Ensure that URLFields validate urls with specific schemes properly.
""" """
@@ -3199,42 +3186,26 @@ class FieldTest(MongoDBTestCase):
att.delete() att.delete()
self.assertEqual(0, Attachment.objects.count()) self.assertEqual(0, Attachment.objects.count())
def test_choices_allow_using_sets_as_choices(self): def test_choices_validation(self):
"""Ensure that sets can be used when setting choices """Ensure that value is in a container of allowed values.
""" """
class Shirt(Document): class Shirt(Document):
size = StringField(choices={'M', 'L'}) size = StringField(max_length=3, choices=(
('S', 'Small'), ('M', 'Medium'), ('L', 'Large'),
('XL', 'Extra Large'), ('XXL', 'Extra Extra Large')))
Shirt(size='M').validate() Shirt.drop_collection()
def test_choices_validation_allow_no_value(self):
"""Ensure that .validate passes and no value was provided
for a field setup with choices
"""
class Shirt(Document):
size = StringField(choices=('S', 'M'))
shirt = Shirt() shirt = Shirt()
shirt.validate() shirt.validate()
def test_choices_validation_accept_possible_value(self): shirt.size = "S"
"""Ensure that value is in a container of allowed values.
"""
class Shirt(Document):
size = StringField(choices=('S', 'M'))
shirt = Shirt(size='S')
shirt.validate() shirt.validate()
def test_choices_validation_reject_unknown_value(self): shirt.size = "XS"
"""Ensure that unallowed value are rejected upon validation self.assertRaises(ValidationError, shirt.validate)
"""
class Shirt(Document):
size = StringField(choices=('S', 'M'))
shirt = Shirt(size="XS") Shirt.drop_collection()
with self.assertRaises(ValidationError):
shirt.validate()
def test_choices_validation_documents(self): def test_choices_validation_documents(self):
""" """
@@ -4053,13 +4024,12 @@ class FieldTest(MongoDBTestCase):
self.assertTrue(isinstance(doc.some_long, six.integer_types)) self.assertTrue(isinstance(doc.some_long, six.integer_types))
class EmbeddedDocumentListFieldTestCase(MongoDBTestCase): class EmbeddedDocumentListFieldTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.db = connect(db='EmbeddedDocumentListFieldTestCase')
def setUp(self):
"""
Create two BlogPost entries in the database, each with
several EmbeddedDocuments.
"""
class Comments(EmbeddedDocument): class Comments(EmbeddedDocument):
author = StringField() author = StringField()
message = StringField() message = StringField()
@@ -4067,11 +4037,14 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
class BlogPost(Document): class BlogPost(Document):
comments = EmbeddedDocumentListField(Comments) comments = EmbeddedDocumentListField(Comments)
BlogPost.drop_collection() cls.Comments = Comments
cls.BlogPost = BlogPost
self.Comments = Comments
self.BlogPost = BlogPost
def setUp(self):
"""
Create two BlogPost entries in the database, each with
several EmbeddedDocuments.
"""
self.post1 = self.BlogPost(comments=[ self.post1 = self.BlogPost(comments=[
self.Comments(author='user1', message='message1'), self.Comments(author='user1', message='message1'),
self.Comments(author='user2', message='message1') self.Comments(author='user2', message='message1')
@@ -4083,6 +4056,13 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
self.Comments(author='user3', message='message1') self.Comments(author='user3', message='message1')
]).save() ]).save()
def tearDown(self):
self.BlogPost.drop_collection()
@classmethod
def tearDownClass(cls):
cls.db.drop_database('EmbeddedDocumentListFieldTestCase')
def test_no_keyword_filter(self): def test_no_keyword_filter(self):
""" """
Tests the filter method of a List of Embedded Documents Tests the filter method of a List of Embedded Documents
@@ -4440,8 +4420,7 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
my_list = ListField(EmbeddedDocumentField(EmbeddedWithUnique)) my_list = ListField(EmbeddedDocumentField(EmbeddedWithUnique))
A(my_list=[]).save() A(my_list=[]).save()
with self.assertRaises(NotUniqueError): self.assertRaises(NotUniqueError, lambda: A(my_list=[]).save())
A(my_list=[]).save()
class EmbeddedWithSparseUnique(EmbeddedDocument): class EmbeddedWithSparseUnique(EmbeddedDocument):
number = IntField(unique=True, sparse=True) number = IntField(unique=True, sparse=True)
@@ -4449,9 +4428,6 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
class B(Document): class B(Document):
my_list = ListField(EmbeddedDocumentField(EmbeddedWithSparseUnique)) my_list = ListField(EmbeddedDocumentField(EmbeddedWithSparseUnique))
A.drop_collection()
B.drop_collection()
B(my_list=[]).save() B(my_list=[]).save()
B(my_list=[]).save() B(my_list=[]).save()
@@ -4491,8 +4467,6 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
a_field = IntField() a_field = IntField()
c_field = IntField(custom_data=custom_data) c_field = IntField(custom_data=custom_data)
CustomData.drop_collection()
a1 = CustomData(a_field=1, c_field=2).save() a1 = CustomData(a_field=1, c_field=2).save()
self.assertEqual(2, a1.c_field) self.assertEqual(2, a1.c_field)
self.assertFalse(hasattr(a1.c_field, 'custom_data')) self.assertFalse(hasattr(a1.c_field, 'custom_data'))

View File

@@ -18,13 +18,15 @@ try:
except ImportError: except ImportError:
HAS_PIL = False HAS_PIL = False
from tests.utils import MongoDBTestCase
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png') TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png')
TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), 'mongodb_leaf.png') TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), 'mongodb_leaf.png')
class FileTest(MongoDBTestCase): class FileTest(unittest.TestCase):
def setUp(self):
connect(db='mongoenginetest')
self.db = get_db()
def tearDown(self): def tearDown(self):
self.db.drop_collection('fs.files') self.db.drop_collection('fs.files')

View File

@@ -1,22 +0,0 @@
import unittest
from mongoengine import connect
from mongoengine.connection import get_db
MONGO_TEST_DB = 'mongoenginetest'
class MongoDBTestCase(unittest.TestCase):
"""Base class for tests that need a mongodb connection
db is being dropped automatically
"""
@classmethod
def setUpClass(cls):
cls._connection = connect(db=MONGO_TEST_DB)
cls._connection.drop_database(MONGO_TEST_DB)
cls.db = get_db()
@classmethod
def tearDownClass(cls):
cls._connection.drop_database(MONGO_TEST_DB)