diff --git a/README.rst b/README.rst index ea4b5050..cc4524ae 100644 --- a/README.rst +++ b/README.rst @@ -8,6 +8,9 @@ MongoEngine .. image:: https://secure.travis-ci.org/MongoEngine/mongoengine.png?branch=master :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 ===== @@ -92,4 +95,4 @@ Community Contributing ============ -We welcome contributions! see the`Contribution guidelines `_ +We welcome contributions! see the `Contribution guidelines `_ diff --git a/docs/apireference.rst b/docs/apireference.rst index 9057de55..6c42d40a 100644 --- a/docs/apireference.rst +++ b/docs/apireference.rst @@ -38,6 +38,7 @@ Context Managers ================ .. autoclass:: mongoengine.context_managers.switch_db +.. autoclass:: mongoengine.context_managers.switch_collection .. autoclass:: mongoengine.context_managers.no_dereference .. autoclass:: mongoengine.context_managers.query_counter diff --git a/docs/changelog.rst b/docs/changelog.rst index 3d8c3307..51134238 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,12 @@ 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 ================ - Fix django auth import (#531) diff --git a/docs/guide/connecting.rst b/docs/guide/connecting.rst index f681aada..dd89938b 100644 --- a/docs/guide/connecting.rst +++ b/docs/guide/connecting.rst @@ -100,3 +100,18 @@ access to the same User document across databases:: .. note:: Make sure any aliases have been registered with :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 diff --git a/docs/guide/querying.rst b/docs/guide/querying.rst index f50985b5..32cbb94e 100644 --- a/docs/guide/querying.rst +++ b/docs/guide/querying.rst @@ -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 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_with={"type": "Polygon", + loc.objects(point__geo_within=[[[40, 5], [40, 6], [41, 6], [40, 5]]]) + loc.objects(point__geo_within={"type": "Polygon", "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]}) * ``geo_within_box`` - simplified geo_within searching with a box eg:: diff --git a/docs/upgrade.rst b/docs/upgrade.rst index a1fccea2..b55fe312 100644 --- a/docs/upgrade.rst +++ b/docs/upgrade.rst @@ -2,6 +2,12 @@ Upgrading ######### +0.8.7 +***** + +Calling reload on deleted / nonexistant documents now raises a DoesNotExist +exception. + 0.8.2 to 0.8.3 ************** @@ -270,7 +276,7 @@ queryset you should upgrade to use count:: len(Animal.objects(type="mammal")) # New code - Animal.objects(type="mammal").count()) + Animal.objects(type="mammal").count() .only() now inline with .exclude() diff --git a/mongoengine/__init__.py b/mongoengine/__init__.py index 1537c444..ac33ad2b 100644 --- a/mongoengine/__init__.py +++ b/mongoengine/__init__.py @@ -15,7 +15,7 @@ import django __all__ = (list(document.__all__) + fields.__all__ + connection.__all__ + list(queryset.__all__) + signals.__all__ + list(errors.__all__)) -VERSION = (0, 8, 6) +VERSION = (0, 8, 7) def get_version(): diff --git a/mongoengine/django/auth.py b/mongoengine/django/auth.py index 89ea003c..0a309c4c 100644 --- a/mongoengine/django/auth.py +++ b/mongoengine/django/auth.py @@ -9,7 +9,6 @@ from django.contrib.auth.models import AnonymousUser from django.utils.translation import ugettext_lazy as _ from .utils import datetime_now -from .mongo_auth.models import get_user_document REDIRECT_FIELD_NAME = 'next' @@ -382,9 +381,10 @@ class MongoEngineBackend(object): supports_object_permissions = False supports_anonymous_user = False supports_inactive_user = False + _user_doc = False 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 password and user.check_password(password): backend = auth.get_backends()[0] @@ -393,8 +393,14 @@ class MongoEngineBackend(object): return None 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): """Returns a User object from an id (User.id). Django's equivalent takes diff --git a/mongoengine/document.py b/mongoengine/document.py index 59bea359..114778eb 100644 --- a/mongoengine/document.py +++ b/mongoengine/document.py @@ -12,6 +12,7 @@ from mongoengine.common import _import_class from mongoengine.base import (DocumentMetaclass, TopLevelDocumentMetaclass, BaseDocument, BaseDict, BaseList, ALLOW_INHERITANCE, get_document) +from mongoengine.errors import ValidationError from mongoengine.queryset import OperationError, NotUniqueError, QuerySet from mongoengine.connection import get_db, DEFAULT_CONNECTION_NAME from mongoengine.context_managers import switch_db, switch_collection @@ -280,7 +281,9 @@ class Document(BaseDocument): kwargs.update(cascade_kwargs) kwargs['_refs'] = _refs 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: message = 'Could not save document (%s)' if re.match('^E1100[01] duplicate key', unicode(err)): @@ -450,14 +453,16 @@ class Document(BaseDocument): .. versionadded:: 0.1.2 .. versionchanged:: 0.6 Now chainable """ + if not self.pk: + raise self.DoesNotExist("Document does not exist") 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: obj = obj[0] else: - msg = "Reloaded document has been deleted" - raise OperationError(msg) + raise self.DoesNotExist("Document does not exist") for field in self._fields_ordered: setattr(self, field, self._reload(field, obj[field])) self._changed_fields = obj._changed_fields @@ -547,6 +552,8 @@ class Document(BaseDocument): index_cls = cls._meta.get('index_cls', True) collection = cls._get_collection() + if collection.read_preference > 1: + return # determine if an index which we are creating includes # _cls as its first field; if so, we can avoid creating diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index d57021fe..c2ad027e 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -302,8 +302,11 @@ class BaseQuerySet(object): signals.pre_bulk_insert.send(self._document, documents=docs) try: 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: - message = 'Could not save document (%s)' + message = 'Could not save document (%s)'; if re.match('^E1100[01] duplicate key', unicode(err)): # E11000 - duplicate key error index # E11001 - duplicate key on update diff --git a/python-mongoengine.spec b/python-mongoengine.spec index 07b169dc..eddb488d 100644 --- a/python-mongoengine.spec +++ b/python-mongoengine.spec @@ -5,7 +5,7 @@ %define srcname mongoengine Name: python-%{srcname} -Version: 0.8.6 +Version: 0.8.7 Release: 1%{?dist} Summary: A Python Document-Object Mapper for working with MongoDB diff --git a/tests/document/indexes.py b/tests/document/indexes.py index ccf84636..cf6122a3 100644 --- a/tests/document/indexes.py +++ b/tests/document/indexes.py @@ -491,7 +491,7 @@ class IndexesTest(unittest.TestCase): def invalid_index_2(): return BlogPost.objects.hint(('tags', 1)) - self.assertRaises(TypeError, invalid_index_2) + self.assertRaises(Exception, invalid_index_2) def test_unique(self): """Ensure that uniqueness constraints are applied to fields. diff --git a/tests/document/instance.py b/tests/document/instance.py index 982b1e6c..07db85a0 100644 --- a/tests/document/instance.py +++ b/tests/document/instance.py @@ -409,6 +409,27 @@ class InstanceTest(unittest.TestCase): self.assertEqual(len(doc.embedded_field.list_field), 4) 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): """Ensure that dictionary-style field access works properly. """ diff --git a/tests/test_django.py b/tests/test_django.py index 46568ac8..56a61640 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -16,6 +16,7 @@ settings.configure( USE_TZ=True, INSTALLED_APPS=('django.contrib.auth', 'mongoengine.django.mongo_auth'), AUTH_USER_MODEL=('mongo_auth.MongoUser'), + AUTHENTICATION_BACKENDS = ('mongoengine.django.auth.MongoEngineBackend',) ) try: