Compare commits

...

40 Commits

Author SHA1 Message Date
Emmanuel Leblond
a20d40618f Bump to v0.10.6 2016-01-25 01:42:19 +01:00
Emmanuel Leblond
b4af8ec751 Fix travis for python 3.2 2016-01-22 08:38:42 +01:00
Emmanuel Leblond
f4fa39c70e Revert "Force pip version to 7.1.2 in tox for py32 (support dropped for latter versions)"
This reverts commit 7b7165f5d8.
2016-01-20 13:07:06 +01:00
Emmanuel Leblond
7b7165f5d8 Force pip version to 7.1.2 in tox for py32 (support dropped for latter versions) 2016-01-20 11:48:31 +01:00
Emmanuel Leblond
13897db6d3 Fix mongomock url prefix error during connection 2016-01-20 11:06:45 +01:00
Emmanuel Leblond
c4afdb7198 Merge pull request #1123 from Cykooz/master
Fixed detection of shared connections
2016-01-19 18:38:03 +01:00
Emmanuel Leblond
0284975f3f Correct test_reload_of_non_strict_with_special_field_name for pymongo<2.9 2016-01-19 15:34:38 +01:00
Omer Katz
269e3d1303 Merge pull request #1205 from Zephor5/patch-1
add highlight for python code
2016-01-13 15:59:08 +02:00
Zephor
8c81f7ece9 add highlight for python code 2016-01-06 12:00:48 +08:00
Omer Katz
f6e0593774 Merge pull request #1198 from rusnassonov/patch-1
fix missing quote in /docs/guide/mongomock.rst
2015-12-27 13:54:58 +02:00
Ruslan Nassonov
3d80e549cb fix missing quote in /docs/guide/mongomock.rst 2015-12-25 15:52:35 +05:00
Omer Katz
0372e07eb0 Merge pull request #1114 from gmacon/sparse-compound
Allow sparse compound indexes
2015-12-10 07:27:22 +02:00
George Macon
00221e3410 Allow sparse compound indexes 2015-12-09 18:38:28 -05:00
Omer Katz
31d7f70e27 Merge pull request #1153 from AWhetter/fixWindowsTest
Fixed not being able to run tests on Windows
2015-12-08 20:29:28 +02:00
Ashley Whetter
04e8b83d45 Fixed being unable to run tests on Windows 2015-12-08 18:08:10 +00:00
Omer Katz
e87bf71f20 Merge pull request #1170 from hhstore/master
fix for docs.code.tumblelog.py
2015-12-08 07:16:59 +02:00
Omer Katz
2dd70c8d62 Merge pull request #1180 from moonso/add-mongomock-docs
Added page for documenting mongomock. Updated docs/guide/index.rst
2015-12-06 13:23:04 +02:00
moonso
a3886702a3 Added page for documenting mongomock. Updated docs/guide/index.rst 2015-12-06 11:02:26 +01:00
Omer Katz
713af133a0 Moved #1151 changelog entry to the correct version. 2015-12-06 07:54:25 +02:00
Omer Katz
057ffffbf2 Merge pull request #1151 from RussellLuo/feature-support-mocking
Add support for mocking MongoEngine based on mongomock
2015-12-06 07:53:00 +02:00
RussellLuo
a81d6d124b Update AUTHORS and add changelog entry for #1151 2015-12-06 11:11:46 +08:00
RussellLuo
23f07fde5e Add support for mocking MongoEngine based on mongomock
Using `mongomock://` scheme in URI enables the mocking. Fix #1045.
2015-12-06 11:08:00 +08:00
Omer Katz
b42b760393 Merge branch 'fix-reloading-strict' of https://github.com/paularmand/mongoengine into fix-reloading-strict and bumped version.
# Conflicts:
#	AUTHORS
2015-11-30 12:13:47 +02:00
Omer Katz
bf6f4c48c0 Merge pull request #1167 from BeardedSteve/upsert_one
Upsert one
2015-11-30 12:08:04 +02:00
Paul-Armand Verhaegen
6133f04841 Manual merge conflicts in AUTHORS 2015-11-27 23:55:55 +01:00
Paul-Armand Verhaegen
3c18f79ea4 Added test for reloading of strict with special fields #1156 2015-11-27 23:45:25 +01:00
hhstore
2af8342fea bugfix - two small bugs. 2015-11-26 12:01:42 +08:00
srossiter
fc3db7942d updated changelog and version tuple 2015-11-24 12:56:59 +00:00
srossiter
164e2b2678 Docstring change and rename variable to avoid clash with kwargs 2015-11-24 12:53:09 +00:00
srossiter
b7b28390df Added upsert_one method on BaseQuerySet and modified test_upsert_one 2015-11-24 12:46:38 +00:00
Omer Katz
a6e996d921 Added #1165 to the changelog. 2015-11-24 07:06:54 +02:00
Omer Katz
07e666345d Merge pull request #1165 from touilleMan/bug-1164
Add SaveConditionError to __all__
2015-11-24 07:04:54 +02:00
Omer Katz
007f10d29d Merge pull request #1161 from AWhetter/docFix
Fixed a couple of documentation typos
2015-11-24 07:01:49 +02:00
Emmanuel Leblond
0a1ba7c434 Add SaveConditionError to __all__ 2015-11-21 10:25:11 +01:00
Ashley Whetter
cceef33fef Fixed a couple of documentation typos 2015-11-17 14:22:10 +00:00
Paul-Armand Verhaegen
ed8174fe36 Added Paul-Armand Verhaegen to contributor list 2015-11-15 15:32:26 +01:00
Paul-Armand Verhaegen
3c8906494f Added #1156 to changelog 2015-11-15 15:31:22 +01:00
Paul-Armand Verhaegen
6e745e9882 fixed wrong indentation style 2015-11-10 21:13:24 +01:00
Paul-Armand Verhaegen
fb4e9c3772 fix for reloading of strict with special fields 2015-11-10 20:43:49 +01:00
Kirill Kuzminykh
11024deaae Fixed detection of shared connections 2015-10-05 22:40:44 +03:00
18 changed files with 204 additions and 23 deletions

View File

@@ -25,7 +25,8 @@ install:
- sudo apt-get install python-dev python3-dev libopenjpeg-dev zlib1g-dev libjpeg-turbo8-dev - sudo apt-get install python-dev python3-dev libopenjpeg-dev zlib1g-dev libjpeg-turbo8-dev
libtiff4-dev libjpeg8-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev libtiff4-dev libjpeg8-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev
python-tk python-tk
- travis_retry pip install tox>=1.9 coveralls # virtualenv>=14.0.0 has dropped Python 3.2 support
- travis_retry pip install "virtualenv<14.0.0" "tox>=1.9" coveralls
- travis_retry tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -e test - travis_retry tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -e test
script: script:
- tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- --with-coverage - tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- --with-coverage

View File

@@ -231,3 +231,6 @@ that much better:
* Lars Butler (https://github.com/larsbutler) * Lars Butler (https://github.com/larsbutler)
* George Macon (https://github.com/gmacon) * George Macon (https://github.com/gmacon)
* Ashley Whetter (https://github.com/AWhetter) * Ashley Whetter (https://github.com/AWhetter)
* Paul-Armand Verhaegen (https://github.com/paularmand)
* Steven Rossiter (https://github.com/BeardedSteve)
* Luo Peng (https://github.com/RussellLuo)

View File

@@ -48,7 +48,9 @@ Optional Dependencies
Examples Examples
======== ========
Some simple examples of what MongoEngine code looks like:: Some simple examples of what MongoEngine code looks like:
.. code :: python
class BlogPost(Document): class BlogPost(Document):
title = StringField(required=True, max_length=200) title = StringField(required=True, max_length=200)

View File

@@ -2,6 +2,21 @@
Changelog Changelog
========= =========
Changes in 0.10.6
=================
- Add support for mocking MongoEngine based on mongomock. #1151
- Fixed not being able to run tests on Windows. #1153
- Allow creation of sparse compound indexes. #1114
Changes in 0.10.5
=================
- Fix for reloading of strict with special fields. #1156
Changes in 0.10.4
=================
- SaveConditionError is now importable from the top level package. #1165
- upsert_one method added. #1157
Changes in 0.10.3 Changes in 0.10.3
================= =================
- Fix `read_preference` (it had chaining issues with PyMongo 2.x and it didn't work at all with PyMongo 3.x) #1042 - Fix `read_preference` (it had chaining issues with PyMongo 2.x and it didn't work at all with PyMongo 3.x) #1042
@@ -13,7 +28,7 @@ Changes in 0.10.2
- ReferenceFields now support abstract document types. #837 - ReferenceFields now support abstract document types. #837
Changes in 0.10.1 Changes in 0.10.1
======================= =================
- Fix infinite recursion with CASCADE delete rules under specific conditions. #1046 - Fix infinite recursion with CASCADE delete rules under specific conditions. #1046
- Fix CachedReferenceField bug when loading cached docs as DBRef but failing to save them. #1047 - Fix CachedReferenceField bug when loading cached docs as DBRef but failing to save them. #1047
- Fix ignored chained options #842 - Fix ignored chained options #842

View File

@@ -17,6 +17,10 @@ class Post(Document):
tags = ListField(StringField(max_length=30)) tags = ListField(StringField(max_length=30))
comments = ListField(EmbeddedDocumentField(Comment)) comments = ListField(EmbeddedDocumentField(Comment))
# bugfix
meta = {'allow_inheritance': True}
class TextPost(Post): class TextPost(Post):
content = StringField() content = StringField()
@@ -45,7 +49,8 @@ print 'ALL POSTS'
print print
for post in Post.objects: for post in Post.objects:
print post.title print post.title
print '=' * post.title.count() #print '=' * post.title.count()
print "=" * 20
if isinstance(post, TextPost): if isinstance(post, TextPost):
print post.content print post.content

View File

@@ -13,3 +13,4 @@ User Guide
gridfs gridfs
signals signals
text-indexes text-indexes
mongomock

21
docs/guide/mongomock.rst Normal file
View File

@@ -0,0 +1,21 @@
==============================
Use mongomock for testing
==============================
`mongomock <https://github.com/vmalloc/mongomock/>`_ is a package to do just
what the name implies, mocking a mongo database.
To use with mongoengine, simply specify mongomock when connecting with
mongoengine:
.. code-block:: python
connect('mongoenginetest', host='mongomock://localhost')
conn = get_connection()
or with an alias:
.. code-block:: python
connect('mongoenginetest', host='mongomock://localhost', alias='testdb')
conn = get_connection('testdb')

View File

@@ -14,7 +14,7 @@ import errors
__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, 10, 1) VERSION = (0, 10, 6)
def get_version(): def get_version():

View File

@@ -839,10 +839,6 @@ class BaseDocument(object):
if index_list: if index_list:
spec['fields'] = index_list spec['fields'] = index_list
if spec.get('sparse', False) and len(spec['fields']) > 1:
raise ValueError(
'Sparse indexes can only have one field in them. '
'See https://jira.mongodb.org/browse/SERVER-2193')
return spec return spec

View File

@@ -38,8 +38,11 @@ def register_connection(alias, name=None, host=None, port=None,
:param username: username to authenticate with :param username: username to authenticate with
:param password: password to authenticate with :param password: password to authenticate with
:param authentication_source: database to authenticate against :param authentication_source: database to authenticate against
:param is_mock: explicitly use mongomock for this connection
(can also be done by using `mongomock://` as db host prefix)
:param kwargs: allow ad-hoc parameters to be passed into the pymongo driver :param kwargs: allow ad-hoc parameters to be passed into the pymongo driver
.. versionchanged:: 0.10.6 - added mongomock support
""" """
global _connection_settings global _connection_settings
@@ -54,8 +57,13 @@ def register_connection(alias, name=None, host=None, port=None,
} }
# Handle uri style connections # Handle uri style connections
if "://" in conn_settings['host']: conn_host = conn_settings['host']
uri_dict = uri_parser.parse_uri(conn_settings['host']) if conn_host.startswith('mongomock://'):
conn_settings['is_mock'] = True
# `mongomock://` is not a valid url prefix and must be replaced by `mongodb://`
conn_settings['host'] = conn_host.replace('mongomock://', 'mongodb://', 1)
elif '://' in conn_host:
uri_dict = uri_parser.parse_uri(conn_host)
conn_settings.update({ conn_settings.update({
'name': uri_dict.get('database') or name, 'name': uri_dict.get('database') or name,
'username': uri_dict.get('username'), 'username': uri_dict.get('username'),
@@ -106,7 +114,19 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False):
conn_settings.pop('password', None) conn_settings.pop('password', None)
conn_settings.pop('authentication_source', None) conn_settings.pop('authentication_source', None)
connection_class = MongoClient is_mock = conn_settings.pop('is_mock', None)
if is_mock:
# Use MongoClient from mongomock
try:
import mongomock
except ImportError:
raise RuntimeError('You need mongomock installed '
'to mock MongoEngine.')
connection_class = mongomock.MongoClient
else:
# Use MongoClient from pymongo
connection_class = MongoClient
if 'replicaSet' in conn_settings: if 'replicaSet' in conn_settings:
# Discard port since it can't be used on MongoReplicaSetClient # Discard port since it can't be used on MongoReplicaSetClient
conn_settings.pop('port', None) conn_settings.pop('port', None)
@@ -126,6 +146,7 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False):
connection_settings.pop('name', None) connection_settings.pop('name', None)
connection_settings.pop('username', None) connection_settings.pop('username', None)
connection_settings.pop('password', None) connection_settings.pop('password', None)
connection_settings.pop('authentication_source', None)
if conn_settings == connection_settings and _connections.get(db_alias, None): if conn_settings == connection_settings and _connections.get(db_alias, None):
connection = _connections[db_alias] connection = _connections[db_alias]
break break

View File

@@ -217,7 +217,7 @@ class Document(BaseDocument):
Returns True if the document has been updated or False if the document Returns True if the document has been updated or False if the document
in the database doesn't match the query. in the database doesn't match the query.
.. note:: All unsaved changes that has been made to the document are .. note:: All unsaved changes that have been made to the document are
rejected if the method returns True. rejected if the method returns True.
:param query: the update will be performed only if the document in the :param query: the update will be performed only if the document in the
@@ -407,7 +407,7 @@ class Document(BaseDocument):
def cascade_save(self, *args, **kwargs): def cascade_save(self, *args, **kwargs):
"""Recursively saves any references / """Recursively saves any references /
generic references on an objects""" generic references on the document"""
_refs = kwargs.get('_refs', []) or [] _refs = kwargs.get('_refs', []) or []
ReferenceField = _import_class('ReferenceField') ReferenceField = _import_class('ReferenceField')
@@ -604,11 +604,16 @@ class Document(BaseDocument):
if not fields or field in fields: if not fields or field in fields:
try: try:
setattr(self, field, self._reload(field, obj[field])) setattr(self, field, self._reload(field, obj[field]))
except KeyError: except (KeyError, AttributeError):
# If field is removed from the database while the object try:
# is in memory, a reload would cause a KeyError # If field is a special field, e.g. items is stored as _reserved_items,
# i.e. obj.update(unset__field=1) followed by obj.reload() # an KeyError is thrown. So try to retrieve the field from _data
delattr(self, field) setattr(self, field, self._reload(field, obj._data.get(field)))
except KeyError:
# If field is removed from the database while the object
# is in memory, a reload would cause a KeyError
# i.e. obj.update(unset__field=1) followed by obj.reload()
delattr(self, field)
self._changed_fields = obj._changed_fields self._changed_fields = obj._changed_fields
self._created = False self._created = False

View File

@@ -6,7 +6,7 @@ from mongoengine.python_support import txt_type
__all__ = ('NotRegistered', 'InvalidDocumentError', 'LookUpError', __all__ = ('NotRegistered', 'InvalidDocumentError', 'LookUpError',
'DoesNotExist', 'MultipleObjectsReturned', 'InvalidQueryError', 'DoesNotExist', 'MultipleObjectsReturned', 'InvalidQueryError',
'OperationError', 'NotUniqueError', 'FieldDoesNotExist', 'OperationError', 'NotUniqueError', 'FieldDoesNotExist',
'ValidationError') 'ValidationError', 'SaveConditionError')
class NotRegistered(Exception): class NotRegistered(Exception):

View File

@@ -471,6 +471,32 @@ class BaseQuerySet(object):
raise OperationError(message) raise OperationError(message)
raise OperationError(u'Update failed (%s)' % unicode(err)) raise OperationError(u'Update failed (%s)' % unicode(err))
def upsert_one(self, write_concern=None, **update):
"""Overwrite or add the first document matched by the query.
:param write_concern: Extra keyword arguments are passed down which
will be used as options for the resultant
``getLastError`` command. For example,
``save(..., write_concern={w: 2, fsync: True}, ...)`` will
wait until at least two servers have recorded the write and
will force an fsync on the primary server.
:param update: Django-style update keyword arguments
:returns the new or overwritten document
.. versionadded:: 0.10.2
"""
atomic_update = self.update(multi=False, upsert=True, write_concern=write_concern,
full_result=True,**update)
if atomic_update['updatedExisting']:
document = self.get()
else:
document = self._document.objects.with_id(atomic_update['upserted'])
return document
def update_one(self, upsert=False, write_concern=None, **update): def update_one(self, upsert=False, write_concern=None, **update):
"""Perform an atomic update on the fields of the first document """Perform an atomic update on the fields of the first document
matched by the query. matched by the query.

View File

@@ -863,6 +863,20 @@ class IndexesTest(unittest.TestCase):
self.assertTrue([('provider_ids.foo', 1)] in info) self.assertTrue([('provider_ids.foo', 1)] in info)
self.assertTrue([('provider_ids.bar', 1)] in info) self.assertTrue([('provider_ids.bar', 1)] in info)
def test_sparse_compound_indexes(self):
class MyDoc(Document):
provider_ids = DictField()
meta = {
"indexes": [{'fields': ("provider_ids.foo", "provider_ids.bar"),
'sparse': True}],
}
info = MyDoc.objects._collection.index_information()
self.assertEqual([('provider_ids.foo', 1), ('provider_ids.bar', 1)],
info['provider_ids.foo_1_provider_ids.bar_1']['key'])
self.assertTrue(info['provider_ids.foo_1_provider_ids.bar_1']['sparse'])
def test_text_indexes(self): def test_text_indexes(self):
class Book(Document): class Book(Document):

View File

@@ -571,6 +571,28 @@ class InstanceTest(unittest.TestCase):
except Exception: except Exception:
self.assertFalse("Threw wrong exception") self.assertFalse("Threw wrong exception")
def test_reload_of_non_strict_with_special_field_name(self):
"""Ensures reloading works for documents with meta strict == False
"""
class Post(Document):
meta = {
'strict': False
}
title = StringField()
items = ListField()
Post.drop_collection()
Post._get_collection().insert({
"title": "Items eclipse",
"items": ["more lorem", "even more ipsum"]
})
post = Post.objects.first()
post.reload()
self.assertEqual(post.title, "Items eclipse")
self.assertEqual(post.items, ["more lorem", "even more ipsum"])
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.
""" """

View File

@@ -680,12 +680,21 @@ class QuerySetTest(unittest.TestCase):
def test_upsert_one(self): def test_upsert_one(self):
self.Person.drop_collection() self.Person.drop_collection()
self.Person.objects(name="Bob", age=30).update_one(upsert=True) bob = self.Person.objects(name="Bob", age=30).upsert_one()
bob = self.Person.objects.first()
self.assertEqual("Bob", bob.name) self.assertEqual("Bob", bob.name)
self.assertEqual(30, bob.age) self.assertEqual(30, bob.age)
bob.name = "Bobby"
bob.save()
bobby = self.Person.objects(name="Bobby", age=30).upsert_one()
self.assertEqual("Bobby", bobby.name)
self.assertEqual(30, bobby.age)
self.assertEqual(bob.id, bobby.id)
def test_set_on_insert(self): def test_set_on_insert(self):
self.Person.drop_collection() self.Person.drop_collection()

View File

@@ -8,6 +8,7 @@ try:
import unittest2 as unittest import unittest2 as unittest
except ImportError: except ImportError:
import unittest import unittest
from nose.plugins.skip import SkipTest
import pymongo import pymongo
from bson.tz_util import utc from bson.tz_util import utc
@@ -51,6 +52,42 @@ class ConnectionTest(unittest.TestCase):
conn = get_connection('testdb') conn = get_connection('testdb')
self.assertTrue(isinstance(conn, pymongo.mongo_client.MongoClient)) self.assertTrue(isinstance(conn, pymongo.mongo_client.MongoClient))
def test_connect_in_mocking(self):
"""Ensure that the connect() method works properly in mocking.
"""
try:
import mongomock
except ImportError:
raise SkipTest('you need mongomock installed to run this testcase')
connect('mongoenginetest', host='mongomock://localhost')
conn = get_connection()
self.assertTrue(isinstance(conn, mongomock.MongoClient))
connect('mongoenginetest2', host='mongomock://localhost', alias='testdb2')
conn = get_connection('testdb2')
self.assertTrue(isinstance(conn, mongomock.MongoClient))
connect('mongoenginetest3', host='mongodb://localhost', is_mock=True, alias='testdb3')
conn = get_connection('testdb3')
self.assertTrue(isinstance(conn, mongomock.MongoClient))
connect('mongoenginetest4', is_mock=True, alias='testdb4')
conn = get_connection('testdb4')
self.assertTrue(isinstance(conn, mongomock.MongoClient))
connect(host='mongodb://localhost:27017/mongoenginetest5', is_mock=True, alias='testdb5')
conn = get_connection('testdb5')
self.assertTrue(isinstance(conn, mongomock.MongoClient))
connect(host='mongomock://localhost:27017/mongoenginetest6', alias='testdb6')
conn = get_connection('testdb6')
self.assertTrue(isinstance(conn, mongomock.MongoClient))
connect(host='mongomock://localhost:27017/mongoenginetest7', is_mock=True, alias='testdb7')
conn = get_connection('testdb7')
self.assertTrue(isinstance(conn, mongomock.MongoClient))
def test_disconnect(self): def test_disconnect(self):
"""Ensure that the disconnect() method works properly """Ensure that the disconnect() method works properly
""" """
@@ -151,7 +188,7 @@ class ConnectionTest(unittest.TestCase):
self.assertRaises(ConnectionError, get_db, 'test1') self.assertRaises(ConnectionError, get_db, 'test1')
# Authentication succeeds with "authSource" # Authentication succeeds with "authSource"
test_conn2 = connect( connect(
'mongoenginetest', alias='test2', 'mongoenginetest', alias='test2',
host=('mongodb://username2:password@localhost/' host=('mongodb://username2:password@localhost/'
'mongoenginetest?authSource=admin') 'mongoenginetest?authSource=admin')

View File

@@ -12,3 +12,6 @@ deps =
mg28: PyMongo>=2.8,<3.0 mg28: PyMongo>=2.8,<3.0
mg30: PyMongo>=3.0 mg30: PyMongo>=3.0
mgdev: https://github.com/mongodb/mongo-python-driver/tarball/master mgdev: https://github.com/mongodb/mongo-python-driver/tarball/master
setenv =
PYTHON_EGG_CACHE = {envdir}/python-eggs
passenv = windir