diff --git a/.gitignore b/.gitignore index a97178f5..ec7a437c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ dist/ .tox MANIFEST + + +.env +.idea \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 1e5b7da9..40e990ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,18 +12,28 @@ python: - 2.7 env: - - DJANGO_VERSION=1.4 - - DJANGO_VERSION=1.5 - - DJANGO_VERSION=1.6 + - DJANGO_VERSION=1.4 PYMONGO_VERSION=2.8.1 + - DJANGO_VERSION=1.4 PYMONGO_VERSION=3.0.2 + - DJANGO_VERSION=1.5 PYMONGO_VERSION=2.8.1 + - DJANGO_VERSION=1.5 PYMONGO_VERSION=3.0.2 + - DJANGO_VERSION=1.6 PYMONGO_VERSION=2.8.1 + - DJANGO_VERSION=1.6 PYMONGO_VERSION=3.0.2 + - DJANGO_VERSION=1.7 PYMONGO_VERSION=2.8.1 + - DJANGO_VERSION=1.7 PYMONGO_VERSION=3.0.2 + matrix: allow_failures: - - env: DJANGO_VERSION=1.4 + - env: DJANGO_VERSION=1.4 PYMONGO_VERSION=2.8.1 + - env: DJANGO_VERSION=1.4 PYMONGO_VERSION=3.0.2 + - env: DJANGO_VERSION=1.7 PYMONGO_VERSION=2.8.1 + - env: DJANGO_VERSION=1.7 PYMONGO_VERSION=3.0.2 install: - pip install git+http://github.com/django-nonrel/django@nonrel-$DJANGO_VERSION - pip install git+http://github.com/django-nonrel/django-dbindexer@master - pip install git+http://github.com/django-nonrel/djangotoolbox@master + - pip install pymongo==$PYMONGO_VERSION --upgrade - python setup.py install script: cd tests && python runtests.py diff --git a/AUTHORS.rst b/AUTHORS.rst index 7a3bd943..9fb21035 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -23,4 +23,4 @@ Contributions by * Brandon Pedersen (https://github.com/bpedman) (For an up-to-date list of contributors, see -https://github.com/django-mongodb-engine/mongodb-engine/contributors.) +https://github.com/django-nonrel/mongodb-engine/contributors.) diff --git a/django_mongodb_engine/base.py b/django_mongodb_engine/base.py index 68c1d886..12ddd2e1 100644 --- a/django_mongodb_engine/base.py +++ b/django_mongodb_engine/base.py @@ -39,6 +39,8 @@ class DatabaseFeatures(NonrelDatabaseFeatures): supports_microsecond_precision = False supports_long_model_names = False + can_rollback_ddl = True + class DatabaseOperations(NonrelDatabaseOperations): compiler_module = __name__.rsplit('.', 1)[0] + '.compiler' @@ -60,6 +62,9 @@ def sql_flush(self, style, tables, sequence_list, allow_cascade=False): drop all `tables`. No SQL in MongoDB, so just clear all tables here and return an empty list. """ + + + for table in tables: if table.startswith('system.'): # Do not try to drop system collections. @@ -246,11 +251,6 @@ def pop(name, default=None): warnings.warn("slave_okay has been deprecated. " "Please use read_preference instead.") - if replicaset: - connection_class = MongoReplicaSetClient - else: - connection_class = MongoClient - conn_options = dict( host=host, port=int(port), @@ -259,6 +259,11 @@ def pop(name, default=None): ) conn_options.update(options) + if replicaset: + connection_class = MongoReplicaSetClient + else: + connection_class = MongoClient + try: self.connection = connection_class(**conn_options) self.database = self.connection[db_name] diff --git a/django_mongodb_engine/compiler.py b/django_mongodb_engine/compiler.py index a26c0af1..aac1c596 100644 --- a/django_mongodb_engine/compiler.py +++ b/django_mongodb_engine/compiler.py @@ -136,7 +136,7 @@ def get_cursor(self): return [] fields = get_selected_fields(self.query) - cursor = self.collection.find(self.mongo_query, fields=fields) + cursor = self.collection.find(self.mongo_query, fields) if self.ordering: cursor.sort(self.ordering) if self.query.low_mark > 0: diff --git a/django_mongodb_engine/utils.py b/django_mongodb_engine/utils.py index 6974936b..77aa120e 100644 --- a/django_mongodb_engine/utils.py +++ b/django_mongodb_engine/utils.py @@ -70,8 +70,6 @@ def log(self, op, duration, args, kwargs=None): logger.debug(msg, extra={'duration': duration}) def find(self, *args, **kwargs): - if not 'slave_okay' in kwargs and self.collection.slave_okay: - kwargs['slave_okay'] = True return DebugCursor(self, self.collection, *args, **kwargs) def logging_wrapper(method): diff --git a/docs/source/reference/settings.rst b/docs/source/reference/settings.rst index e23ca372..e0ff5458 100644 --- a/docs/source/reference/settings.rst +++ b/docs/source/reference/settings.rst @@ -3,9 +3,9 @@ Settings .. TODO fix highlighting -Connection Settings +Client Settings ------------------- -Additional flags may be passed to :class:`pymongo.Connection` using the +Additional flags may be passed to :class:`pymongo.MongoClient` using the ``OPTIONS`` dictionary:: DATABASES = { @@ -14,9 +14,7 @@ Additional flags may be passed to :class:`pymongo.Connection` using the 'NAME' : 'my_database', ... 'OPTIONS' : { - 'slave_okay' : True, - 'tz_aware' : True, - 'network_timeout' : 42, + 'socketTimeoutMS' : 500, ... } } @@ -24,17 +22,17 @@ Additional flags may be passed to :class:`pymongo.Connection` using the All of these settings directly mirror PyMongo settings. In fact, all Django MongoDB Engine does is lower-casing the names before passing the flags to -:class:`~pymongo.Connection`. For a list of possible options head over to the -`PyMongo documentation on connection options`_. +:class:`~pymongo.MongoClient`. For a list of possible options head over to the +`PyMongo documentation on client options`_. .. _operations-setting: -Safe Operations (``getLastError``) ----------------------------------- +Acknowledged Operations +----------------------- Use the ``OPERATIONS`` dict to specify extra flags passed to :meth:`Collection.save `, :meth:`~pymongo.collection.Collection.update` or -:meth:`~pymongo.collection.Collection.remove` (and thus to ``getLastError``): +:meth:`~pymongo.collection.Collection.remove` (and thus included in the write concern): .. code-block:: python @@ -43,11 +41,7 @@ Use the ``OPERATIONS`` dict to specify extra flags passed to ... } -Since any options to ``getLastError`` imply ``safe=True``, -this configuration passes ``safe=True, w=3`` as keyword arguments to each of -:meth:`~pymongo.collection.Collection.save`, -:meth:`~pymongo.collection.Collection.update` and -:meth:`~pymongo.collection.Collection.remove`. + Get a more fine-grained setup by introducing another layer to this dict: @@ -55,9 +49,9 @@ Get a more fine-grained setup by introducing another layer to this dict: 'OPTIONS' : { 'OPERATIONS' : { - 'save' : {'safe' : True}, + 'save' : {'w' : 3}, 'update' : {}, - 'delete' : {'fsync' : True} + 'delete' : {'j' : True} }, ... } @@ -69,10 +63,10 @@ Get a more fine-grained setup by introducing another layer to this dict: "`insert vs. update`" into `save`. -A full list of ``getLastError`` flags may be found in the -`MongoDB documentation `_. +A full list of write concern flags may be found in the +`MongoDB documentation `_. .. _Similar to Django's built-in backends: http://docs.djangoproject.com/en/dev/ref/settings/#std:setting-OPTIONS -.. _PyMongo documentation on connection options: - http://api.mongodb.org/python/current/api/pymongo/connection.html +.. _PyMongo documentation on client options: + http://api.mongodb.org/python/current/api/pymongo/mongo_client.html diff --git a/requirements.txt b/requirements.txt index 5af5bbd6..2d702cc1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ -pymongo<3.0 + +pymongo>=2.8 + https://github.com/django-nonrel/djangotoolbox/tarball/master https://github.com/django-nonrel/django/tarball/nonrel-1.5 https://github.com/django-nonrel/django-dbindexer/tarball/master diff --git a/setup.py b/setup.py index bfc84115..d917dca6 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,9 @@ license='2-clause BSD', description=DESCRIPTION, long_description=LONG_DESCRIPTION, - install_requires=['pymongo<3.0', 'djangotoolbox>=1.6.0'], + + install_requires=['pymongo>=2.8', 'djangotoolbox>=1.6.0'], + packages=find_packages(exclude=['tests', 'tests.*']), zip_safe=False, classifiers=[ diff --git a/tests/contrib/tests.py b/tests/contrib/tests.py index 6d5ca9f1..0f4c7abc 100644 --- a/tests/contrib/tests.py +++ b/tests/contrib/tests.py @@ -121,7 +121,6 @@ def test_map_reduce_with_custom_primary_key(self, inline=False): class RawQueryTests(TestCase): def setUp(self): - MapReduceModel.objects.all().delete() for i in xrange(10): MapReduceModel.objects.create(n=i, m=i * 2) @@ -158,6 +157,10 @@ def test_raw_update(self): # TODO: Line breaks. class FullTextTest(TestCase): + def setUp(self): + Post.objects.all().delete() + + def test_simple_fulltext(self): blog = Post(content="simple, full text.... search? test") blog.save() diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index a988276c..b97a770a 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -2,7 +2,6 @@ from operator import attrgetter from django.core.exceptions import FieldError -from django.db import connection from django.db.utils import DatabaseError from django.test import TestCase, skipUnlessDBFeature @@ -18,10 +17,6 @@ class LookupTests(TestCase): def setUp(self): - # Create a few Authors. - Author.objects.all().delete() - Article.objects.all().delete() - Tag.objects.all().delete() self.au1 = Author(name='Author 1') self.au1.save() diff --git a/tests/mongodb/tests.py b/tests/mongodb/tests.py index 62a1dd31..1b773b9c 100644 --- a/tests/mongodb/tests.py +++ b/tests/mongodb/tests.py @@ -3,16 +3,15 @@ from django.core.management import call_command from django.contrib.sites.models import Site -from django.db import connection, connections +from django.db import connection from django.db.utils import DatabaseError, IntegrityError from django.db.models import Q - from gridfs import GridOut -from pymongo import ASCENDING, DESCENDING - +from pymongo import ASCENDING, DESCENDING, ReadPreference, version_tuple as pymongo_version from django_mongodb_engine.base import DatabaseWrapper - from models import * + + from utils import * @@ -57,18 +56,17 @@ def test_generic_field(self): def test_databasewrapper_api(self): from pymongo.mongo_client import MongoClient from pymongo.database import Database - from pymongo.collection import Collection from random import shuffle + from pymongo.collection import Collection if settings.DEBUG: - from django_mongodb_engine.utils import \ - CollectionDebugWrapper as Collection + from django_mongodb_engine.utils import CollectionDebugWrapper as Collection + for wrapper in [connection, DatabaseWrapper(connection.settings_dict)]: calls = [ - lambda: self.assertIsInstance(wrapper.get_collection('foo'), - Collection), + lambda: self.assertIsInstance(wrapper.get_collection('foo'), Collection), lambda: self.assertIsInstance(wrapper.database, Database), lambda: self.assertIsInstance(wrapper.connection, MongoClient), ] @@ -179,6 +177,10 @@ def test_issue_89(self): class DatabaseOptionTests(TestCase): """Tests for MongoDB-specific database options.""" + def setUp(self): + Post.objects.all().delete() + RawModel.objects.all().delete() + class custom_database_wrapper(object): def __init__(self, settings, **kwargs): @@ -193,7 +195,7 @@ def __enter__(self): return self.new_wrapper def __exit__(self, *exc_info): - self.new_wrapper.connection.disconnect() + self.new_wrapper.connection.close() connections._connections.default = self._old_connection def test_pymongo_connection_args(self): @@ -203,23 +205,29 @@ class foodict(dict): with self.custom_database_wrapper({ 'OPTIONS': { - 'SLAVE_OKAY': True, + 'READ_PREFERENCE': ReadPreference.SECONDARY, 'TZ_AWARE': True, 'DOCUMENT_CLASS': foodict, - }}) as connection: - for name, value in connection.settings_dict[ - 'OPTIONS'].iteritems(): - name = '_Connection__%s' % name.lower() - if name not in connection.connection.__dict__: - # slave_okay was moved into BaseObject in PyMongo 2.0. - name = name.replace('Connection', 'BaseObject') - if name not in connection.connection.__dict__: - # document_class was moved into MongoClient in PyMongo 2.4. - name = name.replace('BaseObject', 'MongoClient') - self.assertEqual(connection.connection.__dict__[name], value) + }}) as db: + + connection = db.connection + + if pymongo_version[0] >= 3: + tz_aware = connection.codec_options.tz_aware + document_class = connection.codec_options.document_class + else: + tz_aware = connection.tz_aware + document_class = connection.document_class + + self.assertEqual(ReadPreference.SECONDARY, connection.read_preference) + + self.assertEqual(True, tz_aware) + self.assertEqual(foodict, document_class) + def test_operation_flags(self): def test_setup(flags, **method_kwargs): + cls_code = [ 'from pymongo.collection import Collection', 'class Collection(Collection):', @@ -237,35 +245,21 @@ def test_setup(flags, **method_kwargs): exec '\n'.join(cls_code) in locals() options = {'OPTIONS': {'OPERATIONS': flags}} - with self.custom_database_wrapper(options, - collection_class=Collection): + with self.custom_database_wrapper(options, collection_class=Collection): RawModel.objects.create(raw='foo') update_count = RawModel.objects.update(raw='foo'), \ RawModel.objects.count() RawModel.objects.all().delete() for name in method_kwargs: - self.assertEqual(method_kwargs[name], - Collection._method_kwargs[name]) - + self.assertEqual(method_kwargs[name], Collection._method_kwargs[name]) self.assertEqual(*update_count) test_setup({}, save={}, update={'multi': True}, remove={}) - test_setup({}, - save={}, - update={'multi': True}, - remove={}) - test_setup({ - 'delete': {}, 'update': {}}, - save={}, - update={'multi': True}, - remove={}) - test_setup({ - 'insert': {'fsync': True}, 'delete': {'fsync': True}}, - save={}, - update={'multi': True}, - remove={'fsync': True}) + test_setup({}, save={}, update={'multi': True}, remove={}) + test_setup({'delete': {}, 'update': {}}, save={}, update={'multi': True}, remove={}) + test_setup({ 'insert': {'fsync': True}, 'delete': {'fsync': True}}, save={}, update={'multi': True}, remove={'fsync': True}) def test_unique_safe(self): @@ -338,9 +332,12 @@ def assertHaveIndex(self, key, **properties): info = get_collection(NewStyleIndexesTestModel).index_information() index_name = '_'.join('%s_%s' % pair for pair in key) default_properties = {'key': self.order_doesnt_matter(key), 'v': 1} + self.assertIn(index_name, info) - self.assertEqual(info[index_name], - dict(default_properties, **properties)) + + for key, value in dict(default_properties, **properties).iteritems(): + self.assertEqual(info[index_name][key], value) + def test_indexes(self): self.assertHaveIndex([('db_index', 1)]) diff --git a/tests/settings/settings_base.py b/tests/settings/settings_base.py index 4fe67c3e..d9cb6f5b 100644 --- a/tests/settings/settings_base.py +++ b/tests/settings/settings_base.py @@ -2,11 +2,11 @@ 'default': { 'ENGINE': 'django_mongodb_engine', 'NAME': 'test', - 'OPTIONS': {'OPERATIONS':{}}, + 'OPTIONS': {'OPERATIONS': {}} }, 'other': { 'ENGINE': 'django_mongodb_engine', - 'NAME': 'test2', + 'NAME': 'test2' }, } diff --git a/tests/utils.py b/tests/utils.py index fada71c8..51505a06 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,7 +4,6 @@ from django.test import TestCase from django.utils.unittest import skip - class TestCase(TestCase): def setUp(self):