Merge remote-tracking branch 'origin/master'
This commit is contained in:
		| @@ -9,6 +9,9 @@ MongoEngine | |||||||
| .. image:: https://secure.travis-ci.org/MongoEngine/mongoengine.png?branch=master | .. image:: https://secure.travis-ci.org/MongoEngine/mongoengine.png?branch=master | ||||||
|   :target: http://travis-ci.org/MongoEngine/mongoengine |   :target: http://travis-ci.org/MongoEngine/mongoengine | ||||||
|    |    | ||||||
|  | .. image:: https://coveralls.io/repos/MongoEngine/mongoengine/badge.png?branch=master  | ||||||
|  |   :target: https://coveralls.io/r/MongoEngine/mongoengine?branch=master | ||||||
|  |  | ||||||
| About | About | ||||||
| ===== | ===== | ||||||
| MongoEngine is a Python Object-Document Mapper for working with MongoDB. | MongoEngine is a Python Object-Document Mapper for working with MongoDB. | ||||||
|   | |||||||
| @@ -38,6 +38,7 @@ Context Managers | |||||||
| ================ | ================ | ||||||
|  |  | ||||||
| .. autoclass:: mongoengine.context_managers.switch_db | .. autoclass:: mongoengine.context_managers.switch_db | ||||||
|  | .. autoclass:: mongoengine.context_managers.switch_collection | ||||||
| .. autoclass:: mongoengine.context_managers.no_dereference | .. autoclass:: mongoengine.context_managers.no_dereference | ||||||
| .. autoclass:: mongoengine.context_managers.query_counter | .. autoclass:: mongoengine.context_managers.query_counter | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,12 @@ | |||||||
| Changelog | Changelog | ||||||
| ========= | ========= | ||||||
|  |  | ||||||
|  | Changes in 0.8.7 | ||||||
|  | ================ | ||||||
|  | - Calling reload on deleted / nonexistant documents raises DoesNotExist (#538) | ||||||
|  | - Stop ensure_indexes running on a secondaries (#555) | ||||||
|  | - Fix circular import issue with django auth (#531) (#545) | ||||||
|  |  | ||||||
| Changes in 0.8.6 | Changes in 0.8.6 | ||||||
| ================ | ================ | ||||||
| - Fix django auth import (#531) | - Fix django auth import (#531) | ||||||
|   | |||||||
| @@ -100,3 +100,18 @@ access to the same User document across databases:: | |||||||
|  |  | ||||||
| .. note:: Make sure any aliases have been registered with | .. note:: Make sure any aliases have been registered with | ||||||
|     :func:`~mongoengine.register_connection` before using the context manager. |     :func:`~mongoengine.register_connection` before using the context manager. | ||||||
|  |  | ||||||
|  | There is also a switch collection context manager as well.  The | ||||||
|  | :class:`~mongoengine.context_managers.switch_collection` context manager allows | ||||||
|  | you to change the collection for a given class allowing quick and easy | ||||||
|  | access to the same Group document across collection:: | ||||||
|  |  | ||||||
|  |         from mongoengine.context_managers import switch_db | ||||||
|  |  | ||||||
|  |         class Group(Document): | ||||||
|  |             name = StringField() | ||||||
|  |  | ||||||
|  |         Group(name="test").save()  # Saves in the default db | ||||||
|  |  | ||||||
|  |         with switch_collection(Group, 'group2000') as Group: | ||||||
|  |             Group(name="hello Group 2000 collection!").save()  # Saves in group2000 collection | ||||||
|   | |||||||
| @@ -92,8 +92,8 @@ were added in 0.8 for:  :class:`~mongoengine.fields.PointField`, | |||||||
| * ``geo_within`` -- Check if a geometry is within a polygon.  For ease of use | * ``geo_within`` -- Check if a geometry is within a polygon.  For ease of use | ||||||
|     it accepts either a geojson geometry or just the polygon coordinates eg:: |     it accepts either a geojson geometry or just the polygon coordinates eg:: | ||||||
|  |  | ||||||
|         loc.objects(point__geo_with=[[[40, 5], [40, 6], [41, 6], [40, 5]]]) |         loc.objects(point__geo_within=[[[40, 5], [40, 6], [41, 6], [40, 5]]]) | ||||||
|         loc.objects(point__geo_with={"type": "Polygon", |         loc.objects(point__geo_within={"type": "Polygon", | ||||||
|                                  "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]}) |                                  "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]}) | ||||||
|  |  | ||||||
| * ``geo_within_box`` - simplified geo_within searching with a box eg:: | * ``geo_within_box`` - simplified geo_within searching with a box eg:: | ||||||
|   | |||||||
| @@ -2,6 +2,12 @@ | |||||||
| Upgrading | Upgrading | ||||||
| ######### | ######### | ||||||
|  |  | ||||||
|  | 0.8.7 | ||||||
|  | ***** | ||||||
|  |  | ||||||
|  | Calling reload on deleted / nonexistant documents now raises a DoesNotExist | ||||||
|  | exception. | ||||||
|  |  | ||||||
|  |  | ||||||
| 0.8.2 to 0.8.3 | 0.8.2 to 0.8.3 | ||||||
| ************** | ************** | ||||||
| @@ -270,7 +276,7 @@ queryset you should upgrade to use count:: | |||||||
|     len(Animal.objects(type="mammal")) |     len(Animal.objects(type="mammal")) | ||||||
|  |  | ||||||
|     # New code |     # New code | ||||||
|     Animal.objects(type="mammal").count()) |     Animal.objects(type="mammal").count() | ||||||
|  |  | ||||||
|  |  | ||||||
| .only() now inline with .exclude() | .only() now inline with .exclude() | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ import django | |||||||
| __all__ = (list(document.__all__) + fields.__all__ + connection.__all__ + | __all__ = (list(document.__all__) + fields.__all__ + connection.__all__ + | ||||||
|            list(queryset.__all__) + signals.__all__ + list(errors.__all__)) |            list(queryset.__all__) + signals.__all__ + list(errors.__all__)) | ||||||
|  |  | ||||||
| VERSION = (0, 8, 6) | VERSION = (0, 8, 7) | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_version(): | def get_version(): | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ from django.contrib.auth.models import AnonymousUser | |||||||
| from django.utils.translation import ugettext_lazy as _ | from django.utils.translation import ugettext_lazy as _ | ||||||
|  |  | ||||||
| from .utils import datetime_now | from .utils import datetime_now | ||||||
| from .mongo_auth.models import get_user_document |  | ||||||
|  |  | ||||||
| REDIRECT_FIELD_NAME = 'next' | REDIRECT_FIELD_NAME = 'next' | ||||||
|  |  | ||||||
| @@ -382,9 +381,10 @@ class MongoEngineBackend(object): | |||||||
|     supports_object_permissions = False |     supports_object_permissions = False | ||||||
|     supports_anonymous_user = False |     supports_anonymous_user = False | ||||||
|     supports_inactive_user = False |     supports_inactive_user = False | ||||||
|  |     _user_doc = False | ||||||
|  |  | ||||||
|     def authenticate(self, username=None, password=None): |     def authenticate(self, username=None, password=None): | ||||||
|         user = get_user_document().objects(username=username).first() |         user = self.user_document.objects(username=username).first() | ||||||
|         if user: |         if user: | ||||||
|             if password and user.check_password(password): |             if password and user.check_password(password): | ||||||
|                 backend = auth.get_backends()[0] |                 backend = auth.get_backends()[0] | ||||||
| @@ -393,8 +393,14 @@ class MongoEngineBackend(object): | |||||||
|         return None |         return None | ||||||
|  |  | ||||||
|     def get_user(self, user_id): |     def get_user(self, user_id): | ||||||
|         return get_user_document().objects.with_id(user_id) |         return self.user_document.objects.with_id(user_id) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def user_document(self): | ||||||
|  |         if self._user_doc is False: | ||||||
|  |             from .mongo_auth.models import get_user_document | ||||||
|  |             self._user_doc = get_user_document() | ||||||
|  |         return self._user_doc | ||||||
|  |  | ||||||
| def get_user(userid): | def get_user(userid): | ||||||
|     """Returns a User object from an id (User.id). Django's equivalent takes |     """Returns a User object from an id (User.id). Django's equivalent takes | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ from mongoengine.common import _import_class | |||||||
| from mongoengine.base import (DocumentMetaclass, TopLevelDocumentMetaclass, | from mongoengine.base import (DocumentMetaclass, TopLevelDocumentMetaclass, | ||||||
|                               BaseDocument, BaseDict, BaseList, |                               BaseDocument, BaseDict, BaseList, | ||||||
|                               ALLOW_INHERITANCE, get_document) |                               ALLOW_INHERITANCE, get_document) | ||||||
|  | from mongoengine.errors import ValidationError | ||||||
| from mongoengine.queryset import OperationError, NotUniqueError, QuerySet | from mongoengine.queryset import OperationError, NotUniqueError, QuerySet | ||||||
| from mongoengine.connection import get_db, DEFAULT_CONNECTION_NAME | from mongoengine.connection import get_db, DEFAULT_CONNECTION_NAME | ||||||
| from mongoengine.context_managers import switch_db, switch_collection | from mongoengine.context_managers import switch_db, switch_collection | ||||||
| @@ -280,7 +281,9 @@ class Document(BaseDocument): | |||||||
|                     kwargs.update(cascade_kwargs) |                     kwargs.update(cascade_kwargs) | ||||||
|                 kwargs['_refs'] = _refs |                 kwargs['_refs'] = _refs | ||||||
|                 self.cascade_save(**kwargs) |                 self.cascade_save(**kwargs) | ||||||
|  |         except pymongo.errors.DuplicateKeyError, err: | ||||||
|  |             message = u'Tried to save duplicate unique keys (%s)' | ||||||
|  |             raise NotUniqueError(message % unicode(err)) | ||||||
|         except pymongo.errors.OperationFailure, err: |         except pymongo.errors.OperationFailure, err: | ||||||
|             message = 'Could not save document (%s)' |             message = 'Could not save document (%s)' | ||||||
|             if re.match('^E1100[01] duplicate key', unicode(err)): |             if re.match('^E1100[01] duplicate key', unicode(err)): | ||||||
| @@ -450,14 +453,16 @@ class Document(BaseDocument): | |||||||
|         .. versionadded:: 0.1.2 |         .. versionadded:: 0.1.2 | ||||||
|         .. versionchanged:: 0.6  Now chainable |         .. versionchanged:: 0.6  Now chainable | ||||||
|         """ |         """ | ||||||
|  |         if not self.pk: | ||||||
|  |             raise self.DoesNotExist("Document does not exist") | ||||||
|         obj = self._qs.read_preference(ReadPreference.PRIMARY).filter( |         obj = self._qs.read_preference(ReadPreference.PRIMARY).filter( | ||||||
|                     **self._object_key).limit(1).select_related(max_depth=max_depth) |                     **self._object_key).limit(1).select_related(max_depth=max_depth) | ||||||
|  |  | ||||||
|  |  | ||||||
|         if obj: |         if obj: | ||||||
|             obj = obj[0] |             obj = obj[0] | ||||||
|         else: |         else: | ||||||
|             msg = "Reloaded document has been deleted" |             raise self.DoesNotExist("Document does not exist") | ||||||
|             raise OperationError(msg) |  | ||||||
|         for field in self._fields_ordered: |         for field in self._fields_ordered: | ||||||
|             setattr(self, field, self._reload(field, obj[field])) |             setattr(self, field, self._reload(field, obj[field])) | ||||||
|         self._changed_fields = obj._changed_fields |         self._changed_fields = obj._changed_fields | ||||||
| @@ -547,6 +552,8 @@ class Document(BaseDocument): | |||||||
|         index_cls = cls._meta.get('index_cls', True) |         index_cls = cls._meta.get('index_cls', True) | ||||||
|  |  | ||||||
|         collection = cls._get_collection() |         collection = cls._get_collection() | ||||||
|  |         if collection.read_preference > 1: | ||||||
|  |             return | ||||||
|  |  | ||||||
|         # determine if an index which we are creating includes |         # determine if an index which we are creating includes | ||||||
|         # _cls as its first field; if so, we can avoid creating |         # _cls as its first field; if so, we can avoid creating | ||||||
|   | |||||||
| @@ -153,7 +153,7 @@ class EmailField(StringField): | |||||||
|     EMAIL_REGEX = re.compile( |     EMAIL_REGEX = re.compile( | ||||||
|         r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom |         r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom | ||||||
|         r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"'  # quoted-string |         r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"'  # quoted-string | ||||||
|         r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,253}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE  # domain |         r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,253}[A-Z0-9])?\.)+[A-Z]{2,6}$', re.IGNORECASE  # domain | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     def validate(self, value): |     def validate(self, value): | ||||||
|   | |||||||
| @@ -302,8 +302,11 @@ class BaseQuerySet(object): | |||||||
|         signals.pre_bulk_insert.send(self._document, documents=docs) |         signals.pre_bulk_insert.send(self._document, documents=docs) | ||||||
|         try: |         try: | ||||||
|             ids = self._collection.insert(raw, **write_concern) |             ids = self._collection.insert(raw, **write_concern) | ||||||
|  |         except pymongo.errors.DuplicateKeyError, err: | ||||||
|  |             message = 'Could not save document (%s)'; | ||||||
|  |             raise NotUniqueError(message % unicode(err)) | ||||||
|         except pymongo.errors.OperationFailure, err: |         except pymongo.errors.OperationFailure, err: | ||||||
|             message = 'Could not save document (%s)' |             message = 'Could not save document (%s)'; | ||||||
|             if re.match('^E1100[01] duplicate key', unicode(err)): |             if re.match('^E1100[01] duplicate key', unicode(err)): | ||||||
|                 # E11000 - duplicate key error index |                 # E11000 - duplicate key error index | ||||||
|                 # E11001 - duplicate key on update |                 # E11001 - duplicate key on update | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| %define srcname mongoengine | %define srcname mongoengine | ||||||
|  |  | ||||||
| Name:           python-%{srcname} | Name:           python-%{srcname} | ||||||
| Version:        0.8.6 | Version:        0.8.7 | ||||||
| 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 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -491,7 +491,7 @@ class IndexesTest(unittest.TestCase): | |||||||
|  |  | ||||||
|         def invalid_index_2(): |         def invalid_index_2(): | ||||||
|             return BlogPost.objects.hint(('tags', 1)) |             return BlogPost.objects.hint(('tags', 1)) | ||||||
|         self.assertRaises(TypeError, invalid_index_2) |         self.assertRaises(Exception, invalid_index_2) | ||||||
|  |  | ||||||
|     def test_unique(self): |     def test_unique(self): | ||||||
|         """Ensure that uniqueness constraints are applied to fields. |         """Ensure that uniqueness constraints are applied to fields. | ||||||
|   | |||||||
| @@ -409,6 +409,27 @@ class InstanceTest(unittest.TestCase): | |||||||
|         self.assertEqual(len(doc.embedded_field.list_field), 4) |         self.assertEqual(len(doc.embedded_field.list_field), 4) | ||||||
|         self.assertEqual(len(doc.embedded_field.dict_field), 2) |         self.assertEqual(len(doc.embedded_field.dict_field), 2) | ||||||
|  |  | ||||||
|  |     def test_reload_doesnt_exist(self): | ||||||
|  |         class Foo(Document): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         f = Foo() | ||||||
|  |         try: | ||||||
|  |             f.reload() | ||||||
|  |         except Foo.DoesNotExist: | ||||||
|  |             pass | ||||||
|  |         except Exception as ex: | ||||||
|  |             self.assertFalse("Threw wrong exception") | ||||||
|  |  | ||||||
|  |         f.save() | ||||||
|  |         f.delete() | ||||||
|  |         try: | ||||||
|  |             f.reload() | ||||||
|  |         except Foo.DoesNotExist: | ||||||
|  |             pass | ||||||
|  |         except Exception as ex: | ||||||
|  |             self.assertFalse("Threw wrong exception") | ||||||
|  |  | ||||||
|     def test_dictionary_access(self): |     def test_dictionary_access(self): | ||||||
|         """Ensure that dictionary-style field access works properly. |         """Ensure that dictionary-style field access works properly. | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -2499,6 +2499,9 @@ class FieldTest(unittest.TestCase): | |||||||
|         user = User(email="ross@example.com") |         user = User(email="ross@example.com") | ||||||
|         self.assertTrue(user.validate() is None) |         self.assertTrue(user.validate() is None) | ||||||
|  |  | ||||||
|  |         user = User(email="ross@example.co.uk") | ||||||
|  |         self.assertTrue(user.validate() is None) | ||||||
|  |  | ||||||
|         user = User(email=("Kofq@rhom0e4klgauOhpbpNdogawnyIKvQS0wk2mjqrgGQ5S" |         user = User(email=("Kofq@rhom0e4klgauOhpbpNdogawnyIKvQS0wk2mjqrgGQ5S" | ||||||
|                            "ucictfqpdkK9iS1zeFw8sg7s7cwAF7suIfUfeyueLpfosjn3" |                            "ucictfqpdkK9iS1zeFw8sg7s7cwAF7suIfUfeyueLpfosjn3" | ||||||
|                            "aJIazqqWkm7.net")) |                            "aJIazqqWkm7.net")) | ||||||
| @@ -2507,6 +2510,9 @@ class FieldTest(unittest.TestCase): | |||||||
|         user = User(email='me@localhost') |         user = User(email='me@localhost') | ||||||
|         self.assertRaises(ValidationError, user.validate) |         self.assertRaises(ValidationError, user.validate) | ||||||
|  |  | ||||||
|  |         user = User(email="ross@example.com.") | ||||||
|  |         self.assertRaises(ValidationError, user.validate) | ||||||
|  |  | ||||||
|     def test_email_field_honors_regex(self): |     def test_email_field_honors_regex(self): | ||||||
|         class User(Document): |         class User(Document): | ||||||
|             email = EmailField(regex=r'\w+@example.com') |             email = EmailField(regex=r'\w+@example.com') | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ settings.configure( | |||||||
|     USE_TZ=True, |     USE_TZ=True, | ||||||
|     INSTALLED_APPS=('django.contrib.auth', 'mongoengine.django.mongo_auth'), |     INSTALLED_APPS=('django.contrib.auth', 'mongoengine.django.mongo_auth'), | ||||||
|     AUTH_USER_MODEL=('mongo_auth.MongoUser'), |     AUTH_USER_MODEL=('mongo_auth.MongoUser'), | ||||||
|  |     AUTHENTICATION_BACKENDS = ('mongoengine.django.auth.MongoEngineBackend',) | ||||||
| ) | ) | ||||||
|  |  | ||||||
| try: | try: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user