Compare commits
	
		
			35 Commits
		
	
	
		
			master
			...
			ci-test-ag
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2626eca58e | ||
|  | d5b1914158 | ||
|  | e20a34857d | ||
|  | 7d0b533920 | ||
|  | 6a9e02635d | ||
|  | 5a1fdadf8b | ||
|  | 70871f671a | ||
|  | e6821d8656 | ||
|  | 4f85547333 | ||
|  | deeb6d026d | ||
|  | 97d9a7cf43 | ||
|  | 7850e47bf7 | ||
|  | a94ec06275 | ||
|  | 4acd7a5b75 | ||
|  | e6dcb39036 | ||
|  | 4ed65f8589 | ||
|  | 39586e83cd | ||
|  | 5992fdd408 | ||
|  | 70faaf445e | ||
|  | 71ccfeac3c | ||
|  | d39f5197cb | ||
|  | 5b4eb02683 | ||
|  | d0442d7578 | ||
|  | 098a90ee96 | ||
|  | d7d48e4c21 | ||
|  | cb62b457bc | ||
|  | d29c1c1094 | ||
|  | d6af8cb157 | ||
|  | 8e2e5f506d | ||
|  | 0ea1dfddaa | ||
|  | ce7142eb4f | ||
|  | 5e17fc6d47 | ||
|  | ee7bd1bdae | ||
|  | d75dc02614 | ||
|  | c08ee77041 | 
							
								
								
									
										23
									
								
								.install_mongodb_on_travis.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.install_mongodb_on_travis.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 | ||||
|  | ||||
| if [ "$MONGODB" = "2.4" ]; then | ||||
|     echo "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen" | sudo tee /etc/apt/sources.list.d/mongodb.list | ||||
|     sudo apt-get update | ||||
|     sudo apt-get install mongodb-10gen=2.4.14 | ||||
|     sudo service mongodb start | ||||
| elif [ "$MONGODB" = "2.6" ]; then | ||||
|     echo "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen" | sudo tee /etc/apt/sources.list.d/mongodb.list | ||||
|     sudo apt-get update | ||||
|     sudo apt-get install mongodb-org-server=2.6.12 | ||||
|     # service should be started automatically | ||||
| elif [ "$MONGODB" = "3.0" ]; then | ||||
|     echo "deb http://repo.mongodb.org/apt/ubuntu precise/mongodb-org/3.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb.list | ||||
|     sudo apt-get update | ||||
|     sudo apt-get install mongodb-org-server=3.0.14 | ||||
|     # service should be started automatically | ||||
| else | ||||
|     echo "Invalid MongoDB version, expected 2.4, 2.6, or 3.0." | ||||
|     exit 1 | ||||
| fi; | ||||
							
								
								
									
										67
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,28 +1,48 @@ | ||||
| # For full coverage, we'd have to test all supported Python, MongoDB, and | ||||
| # PyMongo combinations. However, that would result in an overly long build | ||||
| # with a very large number of jobs, hence we only test a subset of all the | ||||
| # combinations: | ||||
| # * MongoDB v2.4 & v3.0 are only tested against Python v2.7 & v3.5. | ||||
| # * MongoDB v2.4 is tested against PyMongo v2.7 & v3.x. | ||||
| # * MongoDB v3.0 is tested against PyMongo v3.x. | ||||
| # * MongoDB v2.6 is currently the "main" version tested against Python v2.7, | ||||
| #   v3.5, PyPy & PyPy3, and PyMongo v2.7, v2.8 & v3.x. | ||||
| # | ||||
| # Reminder: Update README.rst if you change MongoDB versions we test. | ||||
|  | ||||
| language: python | ||||
|  | ||||
| python: | ||||
| - '2.7' | ||||
| - '3.3' | ||||
| - '3.4' | ||||
| - '3.5' | ||||
| - 2.7 | ||||
| - 3.5 | ||||
| - pypy | ||||
| - pypy3 | ||||
|  | ||||
| env: | ||||
| - PYMONGO=2.7 | ||||
| - PYMONGO=2.8 | ||||
| - PYMONGO=3.0 | ||||
| - PYMONGO=dev | ||||
| - MONGODB=2.6 PYMONGO=2.7 | ||||
| - MONGODB=2.6 PYMONGO=2.8 | ||||
| - MONGODB=2.6 PYMONGO=3.0 | ||||
|  | ||||
| matrix: | ||||
|   # Finish the build as soon as one job fails | ||||
|   fast_finish: true | ||||
|  | ||||
|   include: | ||||
|   - python: 2.7 | ||||
|     env: MONGODB=2.4 PYMONGO=2.7 | ||||
|   - python: 2.7 | ||||
|     env: MONGODB=2.4 PYMONGO=3.0 | ||||
|   - python: 2.7 | ||||
|     env: MONGODB=3.0 PYMONGO=3.0 | ||||
|   - python: 3.5 | ||||
|     env: MONGODB=2.4 PYMONGO=2.7 | ||||
|   - python: 3.5 | ||||
|     env: MONGODB=2.4 PYMONGO=3.0 | ||||
|   - python: 3.5 | ||||
|     env: MONGODB=3.0 PYMONGO=3.0 | ||||
|  | ||||
| before_install: | ||||
| - travis_retry sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 | ||||
| - echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | | ||||
|   sudo tee /etc/apt/sources.list.d/mongodb.list | ||||
| - travis_retry sudo apt-get update | ||||
| - travis_retry sudo apt-get install mongodb-org-server | ||||
| - bash .install_mongodb_on_travis.sh | ||||
|  | ||||
| install: | ||||
| - sudo apt-get install python-dev python3-dev libopenjpeg-dev zlib1g-dev libjpeg-turbo8-dev | ||||
| @@ -30,14 +50,17 @@ install: | ||||
|   python-tk | ||||
| - travis_retry pip install --upgrade pip | ||||
| - travis_retry pip install coveralls | ||||
| - travis_retry pip install flake8 | ||||
| - travis_retry pip install flake8 flake8-import-order | ||||
| - travis_retry pip install tox>=1.9 | ||||
| - travis_retry pip install "virtualenv<14.0.0"  # virtualenv>=14.0.0 has dropped Python 3.2 support (and pypy3 is based on py32) | ||||
| - travis_retry tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -e test | ||||
|  | ||||
| # Cache dependencies installed via pip | ||||
| cache: pip | ||||
|  | ||||
| # Run flake8 for py27 | ||||
| before_script: | ||||
| - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then tox -e flake8; fi | ||||
| - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then flake8 .; else echo "flake8 only runs on py27"; fi | ||||
|  | ||||
| script: | ||||
| - tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- --with-coverage | ||||
| @@ -45,22 +68,34 @@ script: | ||||
| # For now only submit coveralls for Python v2.7. Python v3.x currently shows | ||||
| # 0% coverage. That's caused by 'use_2to3', which builds the py3-compatible | ||||
| # code in a separate dir and runs tests on that. | ||||
| after_script: | ||||
| after_success: | ||||
| - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coveralls --verbose; fi | ||||
|  | ||||
| notifications: | ||||
|   irc: irc.freenode.org#mongoengine | ||||
|  | ||||
| # Only run builds on the master branch and GitHub releases (tagged as vX.Y.Z) | ||||
| branches: | ||||
|   only: | ||||
|   - master | ||||
|   - /^v.*$/ | ||||
|  | ||||
| # Whenever a new release is created via GitHub, publish it on PyPI. | ||||
| deploy: | ||||
|   provider: pypi | ||||
|   user: the_drow | ||||
|   password: | ||||
|     secure: QMyatmWBnC6ZN3XLW2+fTBDU4LQcp1m/LjR2/0uamyeUzWKdlOoh/Wx5elOgLwt/8N9ppdPeG83ose1jOz69l5G0MUMjv8n/RIcMFSpCT59tGYqn3kh55b0cIZXFT9ar+5cxlif6a5rS72IHm5li7QQyxexJIII6Uxp0kpvUmek= | ||||
|  | ||||
|   # create a source distribution and a pure python wheel for faster installs | ||||
|   distributions: "sdist bdist_wheel" | ||||
|  | ||||
|   # only deploy on tagged commits (aka GitHub releases) and only for the | ||||
|   # parent repo's builds running Python 2.7 along with dev PyMongo (we run | ||||
|   # Travis against many different Python and PyMongo versions and we don't | ||||
|   # want the deploy to occur multiple times). | ||||
|   on: | ||||
|     tags: true | ||||
|     repo: MongoEngine/mongoengine | ||||
|     condition: "$PYMONGO = 3.0" | ||||
|     python: 2.7 | ||||
|   | ||||
| @@ -29,19 +29,20 @@ Style Guide | ||||
| ----------- | ||||
|  | ||||
| MongoEngine aims to follow `PEP8 <http://www.python.org/dev/peps/pep-0008/>`_ | ||||
| including 4 space indents. When possible we try to stick to 79 character line limits. | ||||
| However, screens got bigger and an ORM has a strong focus on readability and | ||||
| if it can help, we accept 119 as maximum line length, in a similar way as | ||||
| `django does <https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/#python-style>`_ | ||||
| including 4 space indents. When possible we try to stick to 79 character line | ||||
| limits. However, screens got bigger and an ORM has a strong focus on | ||||
| readability and if it can help, we accept 119 as maximum line length, in a | ||||
| similar way as `django does | ||||
| <https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/#python-style>`_ | ||||
|  | ||||
| Testing | ||||
| ------- | ||||
|  | ||||
| All tests are run on `Travis <http://travis-ci.org/MongoEngine/mongoengine>`_ | ||||
| and any pull requests are automatically tested by Travis. Any pull requests | ||||
| without tests will take longer to be integrated and might be refused. | ||||
| and any pull requests are automatically tested. Any pull requests without | ||||
| tests will take longer to be integrated and might be refused. | ||||
|  | ||||
| You may also submit a simple failing test as a PullRequest if you don't know | ||||
| You may also submit a simple failing test as a pull request if you don't know | ||||
| how to fix it, it will be easier for other people to work on it and it may get | ||||
| fixed faster. | ||||
|  | ||||
| @@ -49,13 +50,18 @@ General Guidelines | ||||
| ------------------ | ||||
|  | ||||
| - Avoid backward breaking changes if at all possible. | ||||
| - If you *have* to introduce a breaking change, make it very clear in your | ||||
|   pull request's description. Also, describe how users of this package | ||||
|   should adapt to the breaking change in docs/upgrade.rst. | ||||
| - Write inline documentation for new classes and methods. | ||||
| - Write tests and make sure they pass (make sure you have a mongod | ||||
|   running on the default port, then execute ``python setup.py nosetests`` | ||||
|   from the cmd line to run the test suite). | ||||
| - Ensure tests pass on every Python and PyMongo versions. | ||||
|   You can test on these versions locally by executing ``tox`` | ||||
| - Add enhancements or problematic bug fixes to docs/changelog.rst | ||||
| - Ensure tests pass on all supported Python, PyMongo, and MongoDB versions. | ||||
|   You can test various Python and PyMongo versions locally by executing | ||||
|   ``tox``. For different MongoDB versions, you can rely on our automated | ||||
|   Travis tests. | ||||
| - Add enhancements or problematic bug fixes to docs/changelog.rst. | ||||
| - Add yourself to AUTHORS :) | ||||
|  | ||||
| Documentation | ||||
| @@ -69,3 +75,6 @@ just make your changes to the inline documentation of the appropriate | ||||
| branch and submit a `pull request <https://help.github.com/articles/using-pull-requests>`_. | ||||
| You might also use the github `Edit <https://github.com/blog/844-forking-with-the-edit-button>`_ | ||||
| button. | ||||
|  | ||||
| If you want to test your documentation changes locally, you need to install | ||||
| the ``sphinx`` package. | ||||
|   | ||||
							
								
								
									
										45
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								README.rst
									
									
									
									
									
								
							| @@ -19,23 +19,31 @@ MongoEngine | ||||
| About | ||||
| ===== | ||||
| MongoEngine is a Python Object-Document Mapper for working with MongoDB. | ||||
| Documentation available at https://mongoengine-odm.readthedocs.io - there is currently | ||||
| a `tutorial <https://mongoengine-odm.readthedocs.io/tutorial.html>`_, a `user guide | ||||
| <https://mongoengine-odm.readthedocs.io/guide/index.html>`_ and an `API reference | ||||
| <https://mongoengine-odm.readthedocs.io/apireference.html>`_. | ||||
| Documentation is available at https://mongoengine-odm.readthedocs.io - there | ||||
| is currently a `tutorial <https://mongoengine-odm.readthedocs.io/tutorial.html>`_, | ||||
| a `user guide <https://mongoengine-odm.readthedocs.io/guide/index.html>`_, and | ||||
| an `API reference <https://mongoengine-odm.readthedocs.io/apireference.html>`_. | ||||
|  | ||||
| Supported MongoDB Versions | ||||
| ========================== | ||||
| MongoEngine is currently tested against MongoDB v2.4, v2.6, and v3.0. Future | ||||
| versions should be supported as well, but aren't actively tested at the moment. | ||||
| Make sure to open an issue or submit a pull request if you experience any | ||||
| problems with MongoDB v3.2+. | ||||
|  | ||||
| Installation | ||||
| ============ | ||||
| We recommend the use of `virtualenv <https://virtualenv.pypa.io/>`_ and of | ||||
| `pip <https://pip.pypa.io/>`_. You can then use ``pip install -U mongoengine``. | ||||
| You may also have `setuptools <http://peak.telecommunity.com/DevCenter/setuptools>`_ and thus | ||||
| you can use ``easy_install -U mongoengine``. Otherwise, you can download the | ||||
| You may also have `setuptools <http://peak.telecommunity.com/DevCenter/setuptools>`_ | ||||
| and thus you can use ``easy_install -U mongoengine``. Otherwise, you can download the | ||||
| source from `GitHub <http://github.com/MongoEngine/mongoengine>`_ and run ``python | ||||
| setup.py install``. | ||||
|  | ||||
| Dependencies | ||||
| ============ | ||||
| All of the dependencies can easily be installed via `pip <https://pip.pypa.io/>`_. At the very least, you'll need these two packages to use MongoEngine: | ||||
| All of the dependencies can easily be installed via `pip <https://pip.pypa.io/>`_. | ||||
| At the very least, you'll need these two packages to use MongoEngine: | ||||
|  | ||||
| - pymongo>=2.7.1 | ||||
| - six>=1.10.0 | ||||
| @@ -48,10 +56,6 @@ If you need to use an ``ImageField`` or ``ImageGridFsProxy``: | ||||
|  | ||||
| - Pillow>=2.0.0 | ||||
|  | ||||
| If you want to generate the documentation (e.g. to contribute to it): | ||||
|  | ||||
| - sphinx | ||||
|  | ||||
| Examples | ||||
| ======== | ||||
| Some simple examples of what MongoEngine code looks like: | ||||
| @@ -110,11 +114,11 @@ Some simple examples of what MongoEngine code looks like: | ||||
| Tests | ||||
| ===== | ||||
| To run the test suite, ensure you are running a local instance of MongoDB on | ||||
| the standard port and have ``nose`` installed. Then, run: ``python setup.py nosetests``. | ||||
| the standard port and have ``nose`` installed. Then, run ``python setup.py nosetests``. | ||||
|  | ||||
| To run the test suite on every supported Python version and every supported PyMongo version, | ||||
| you can use ``tox``. | ||||
| tox and each supported Python version should be installed in your environment: | ||||
| To run the test suite on every supported Python and PyMongo version, you can | ||||
| use ``tox``. You'll need to make sure you have each supported Python version | ||||
| installed in your environment and then: | ||||
|  | ||||
| .. code-block:: shell | ||||
|  | ||||
| @@ -123,13 +127,16 @@ tox and each supported Python version should be installed in your environment: | ||||
|     # Run the test suites | ||||
|     $ tox | ||||
|  | ||||
| If you wish to run one single or selected tests, use the nosetest convention. It will find the folder, | ||||
| eventually the file, go to the TestClass specified after the colon and eventually right to the single test. | ||||
| Also use the -s argument if you want to print out whatever or access pdb while testing. | ||||
| If you wish to run a subset of tests, use the nosetests convention: | ||||
|  | ||||
| .. code-block:: shell | ||||
|  | ||||
|     $ python setup.py nosetests --tests tests/fields/fields.py:FieldTest.test_cls_field -s | ||||
|     # Run all the tests in a particular test file | ||||
|     $ python setup.py nosetests --tests tests/fields/fields.py | ||||
|     # Run only particular test class in that file | ||||
|     $ python setup.py nosetests --tests tests/fields/fields.py:FieldTest | ||||
|     # Use the -s option if you want to print some debug statements or use pdb | ||||
|     $ python setup.py nosetests --tests tests/fields/fields.py:FieldTest -s | ||||
|  | ||||
| Community | ||||
| ========= | ||||
|   | ||||
| @@ -2,14 +2,14 @@ | ||||
| import unittest | ||||
| import sys | ||||
|  | ||||
|  | ||||
| import pymongo | ||||
|  | ||||
| from nose.plugins.skip import SkipTest | ||||
| from datetime import datetime | ||||
| import pymongo | ||||
|  | ||||
| from mongoengine import * | ||||
| from mongoengine.connection import get_db, get_connection | ||||
| from mongoengine.connection import get_db | ||||
|  | ||||
| from tests.utils import get_mongodb_version, needs_mongodb_v26 | ||||
|  | ||||
| __all__ = ("IndexesTest", ) | ||||
|  | ||||
| @@ -494,8 +494,7 @@ class IndexesTest(unittest.TestCase): | ||||
|         obj = Test(a=1) | ||||
|         obj.save() | ||||
|  | ||||
|         connection = get_connection() | ||||
|         IS_MONGODB_3 = connection.server_info()['versionArray'][0] >= 3 | ||||
|         IS_MONGODB_3 = get_mongodb_version()[0] >= 3 | ||||
|  | ||||
|         # Need to be explicit about covered indexes as mongoDB doesn't know if | ||||
|         # the documents returned might have more keys in that here. | ||||
| @@ -733,14 +732,6 @@ class IndexesTest(unittest.TestCase): | ||||
|  | ||||
|         Log.drop_collection() | ||||
|  | ||||
|         if pymongo.version_tuple[0] < 2 and pymongo.version_tuple[1] < 3: | ||||
|             raise SkipTest('pymongo needs to be 2.3 or higher for this test') | ||||
|  | ||||
|         connection = get_connection() | ||||
|         version_array = connection.server_info()['versionArray'] | ||||
|         if version_array[0] < 2 and version_array[1] < 2: | ||||
|             raise SkipTest('MongoDB needs to be 2.2 or higher for this test') | ||||
|  | ||||
|         # Indexes are lazy so use list() to perform query | ||||
|         list(Log.objects) | ||||
|         info = Log.objects._collection.index_information() | ||||
| @@ -874,8 +865,8 @@ class IndexesTest(unittest.TestCase): | ||||
|                          info['provider_ids.foo_1_provider_ids.bar_1']['key']) | ||||
|         self.assertTrue(info['provider_ids.foo_1_provider_ids.bar_1']['sparse']) | ||||
|  | ||||
|     @needs_mongodb_v26 | ||||
|     def test_text_indexes(self): | ||||
|  | ||||
|         class Book(Document): | ||||
|             title = DictField() | ||||
|             meta = { | ||||
|   | ||||
| @@ -1,105 +1,139 @@ | ||||
| from datetime import datetime, timedelta | ||||
| import datetime | ||||
| import unittest | ||||
|  | ||||
| from pymongo.errors import OperationFailure | ||||
| from mongoengine import * | ||||
| from mongoengine.connection import get_connection | ||||
| from nose.plugins.skip import SkipTest | ||||
|  | ||||
| from tests.utils import MongoDBTestCase, needs_mongodb_v3 | ||||
|  | ||||
|  | ||||
| __all__ = ("GeoQueriesTest",) | ||||
|  | ||||
|  | ||||
| class GeoQueriesTest(unittest.TestCase): | ||||
| class GeoQueriesTest(MongoDBTestCase): | ||||
|  | ||||
|     def setUp(self): | ||||
|         connect(db='mongoenginetest') | ||||
|  | ||||
|     def test_geospatial_operators(self): | ||||
|         """Ensure that geospatial queries are working. | ||||
|         """ | ||||
|     def _create_event_data(self, point_field_class=GeoPointField): | ||||
|         """Create some sample data re-used in many of the tests below.""" | ||||
|         class Event(Document): | ||||
|             title = StringField() | ||||
|             date = DateTimeField() | ||||
|             location = GeoPointField() | ||||
|             location = point_field_class() | ||||
|  | ||||
|             def __unicode__(self): | ||||
|                 return self.title | ||||
|  | ||||
|         self.Event = Event | ||||
|  | ||||
|         Event.drop_collection() | ||||
|  | ||||
|         event1 = Event(title="Coltrane Motion @ Double Door", | ||||
|                        date=datetime.now() - timedelta(days=1), | ||||
|                        location=[-87.677137, 41.909889]).save() | ||||
|         event2 = Event(title="Coltrane Motion @ Bottom of the Hill", | ||||
|                        date=datetime.now() - timedelta(days=10), | ||||
|                        location=[-122.4194155, 37.7749295]).save() | ||||
|         event3 = Event(title="Coltrane Motion @ Empty Bottle", | ||||
|                        date=datetime.now(), | ||||
|                        location=[-87.686638, 41.900474]).save() | ||||
|         event1 = Event.objects.create( | ||||
|             title="Coltrane Motion @ Double Door", | ||||
|             date=datetime.datetime.now() - datetime.timedelta(days=1), | ||||
|             location=[-87.677137, 41.909889]) | ||||
|         event2 = Event.objects.create( | ||||
|             title="Coltrane Motion @ Bottom of the Hill", | ||||
|             date=datetime.datetime.now() - datetime.timedelta(days=10), | ||||
|             location=[-122.4194155, 37.7749295]) | ||||
|         event3 = Event.objects.create( | ||||
|             title="Coltrane Motion @ Empty Bottle", | ||||
|             date=datetime.datetime.now(), | ||||
|             location=[-87.686638, 41.900474]) | ||||
|  | ||||
|         return event1, event2, event3 | ||||
|  | ||||
|     def test_near(self): | ||||
|         """Make sure the "near" operator works.""" | ||||
|         event1, event2, event3 = self._create_event_data() | ||||
|  | ||||
|         # find all events "near" pitchfork office, chicago. | ||||
|         # note that "near" will show the san francisco event, too, | ||||
|         # although it sorts to last. | ||||
|         events = Event.objects(location__near=[-87.67892, 41.9120459]) | ||||
|         events = self.Event.objects(location__near=[-87.67892, 41.9120459]) | ||||
|         self.assertEqual(events.count(), 3) | ||||
|         self.assertEqual(list(events), [event1, event3, event2]) | ||||
|  | ||||
|         # ensure ordering is respected by "near" | ||||
|         events = self.Event.objects(location__near=[-87.67892, 41.9120459]) | ||||
|         events = events.order_by("-date") | ||||
|         self.assertEqual(events.count(), 3) | ||||
|         self.assertEqual(list(events), [event3, event1, event2]) | ||||
|  | ||||
|     def test_near_and_max_distance(self): | ||||
|         """Ensure the "max_distance" operator works alongside the "near" | ||||
|         operator. | ||||
|         """ | ||||
|         event1, event2, event3 = self._create_event_data() | ||||
|  | ||||
|         # find events within 10 degrees of san francisco | ||||
|         point = [-122.415579, 37.7566023] | ||||
|         events = self.Event.objects(location__near=point, | ||||
|                                     location__max_distance=10) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0], event2) | ||||
|  | ||||
|     # $minDistance was added in MongoDB v2.6, but continued being buggy | ||||
|     # until v3.0; skip for older versions | ||||
|     @needs_mongodb_v3 | ||||
|     def test_near_and_min_distance(self): | ||||
|         """Ensure the "min_distance" operator works alongside the "near" | ||||
|         operator. | ||||
|         """ | ||||
|         event1, event2, event3 = self._create_event_data() | ||||
|  | ||||
|         # find events at least 10 degrees away of san francisco | ||||
|         point = [-122.415579, 37.7566023] | ||||
|         events = self.Event.objects(location__near=point, | ||||
|                                     location__min_distance=10) | ||||
|         self.assertEqual(events.count(), 2) | ||||
|  | ||||
|     def test_within_distance(self): | ||||
|         """Make sure the "within_distance" operator works.""" | ||||
|         event1, event2, event3 = self._create_event_data() | ||||
|  | ||||
|         # find events within 5 degrees of pitchfork office, chicago | ||||
|         point_and_distance = [[-87.67892, 41.9120459], 5] | ||||
|         events = Event.objects(location__within_distance=point_and_distance) | ||||
|         events = self.Event.objects( | ||||
|             location__within_distance=point_and_distance) | ||||
|         self.assertEqual(events.count(), 2) | ||||
|         events = list(events) | ||||
|         self.assertTrue(event2 not in events) | ||||
|         self.assertTrue(event1 in events) | ||||
|         self.assertTrue(event3 in events) | ||||
|  | ||||
|         # ensure ordering is respected by "near" | ||||
|         events = Event.objects(location__near=[-87.67892, 41.9120459]) | ||||
|         events = events.order_by("-date") | ||||
|         self.assertEqual(events.count(), 3) | ||||
|         self.assertEqual(list(events), [event3, event1, event2]) | ||||
|  | ||||
|         # find events within 10 degrees of san francisco | ||||
|         point = [-122.415579, 37.7566023] | ||||
|         events = Event.objects(location__near=point, location__max_distance=10) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0], event2) | ||||
|  | ||||
|         # find events at least 10 degrees away of san francisco | ||||
|         point = [-122.415579, 37.7566023] | ||||
|         events = Event.objects(location__near=point, location__min_distance=10) | ||||
|         # The following real test passes on MongoDB 3 but minDistance seems | ||||
|         # buggy on older MongoDB versions | ||||
|         if get_connection().server_info()['versionArray'][0] > 2: | ||||
|             self.assertEqual(events.count(), 2) | ||||
|         else: | ||||
|             self.assertTrue(events.count() >= 2) | ||||
|  | ||||
|         # find events within 10 degrees of san francisco | ||||
|         point_and_distance = [[-122.415579, 37.7566023], 10] | ||||
|         events = Event.objects(location__within_distance=point_and_distance) | ||||
|         events = self.Event.objects( | ||||
|             location__within_distance=point_and_distance) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0], event2) | ||||
|  | ||||
|         # find events within 1 degree of greenpoint, broolyn, nyc, ny | ||||
|         point_and_distance = [[-73.9509714, 40.7237134], 1] | ||||
|         events = Event.objects(location__within_distance=point_and_distance) | ||||
|         events = self.Event.objects( | ||||
|             location__within_distance=point_and_distance) | ||||
|         self.assertEqual(events.count(), 0) | ||||
|  | ||||
|         # ensure ordering is respected by "within_distance" | ||||
|         point_and_distance = [[-87.67892, 41.9120459], 10] | ||||
|         events = Event.objects(location__within_distance=point_and_distance) | ||||
|         events = self.Event.objects( | ||||
|             location__within_distance=point_and_distance) | ||||
|         events = events.order_by("-date") | ||||
|         self.assertEqual(events.count(), 2) | ||||
|         self.assertEqual(events[0], event3) | ||||
|  | ||||
|     def test_within_box(self): | ||||
|         """Ensure the "within_box" operator works.""" | ||||
|         event1, event2, event3 = self._create_event_data() | ||||
|  | ||||
|         # check that within_box works | ||||
|         box = [(-125.0, 35.0), (-100.0, 40.0)] | ||||
|         events = Event.objects(location__within_box=box) | ||||
|         events = self.Event.objects(location__within_box=box) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0].id, event2.id) | ||||
|  | ||||
|     def test_within_polygon(self): | ||||
|         """Ensure the "within_polygon" operator works.""" | ||||
|         event1, event2, event3 = self._create_event_data() | ||||
|  | ||||
|         polygon = [ | ||||
|             (-87.694445, 41.912114), | ||||
|             (-87.69084, 41.919395), | ||||
| @@ -107,7 +141,7 @@ class GeoQueriesTest(unittest.TestCase): | ||||
|             (-87.654276, 41.911731), | ||||
|             (-87.656164, 41.898061), | ||||
|         ] | ||||
|         events = Event.objects(location__within_polygon=polygon) | ||||
|         events = self.Event.objects(location__within_polygon=polygon) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0].id, event1.id) | ||||
|  | ||||
| @@ -116,13 +150,151 @@ class GeoQueriesTest(unittest.TestCase): | ||||
|             (-1.225891, 52.792797), | ||||
|             (-4.40094, 53.389881) | ||||
|         ] | ||||
|         events = Event.objects(location__within_polygon=polygon2) | ||||
|         events = self.Event.objects(location__within_polygon=polygon2) | ||||
|         self.assertEqual(events.count(), 0) | ||||
|  | ||||
|     def test_geo_spatial_embedded(self): | ||||
|     def test_2dsphere_near(self): | ||||
|         """Make sure the "near" operator works with a PointField, which | ||||
|         corresponds to a 2dsphere index. | ||||
|         """ | ||||
|         event1, event2, event3 = self._create_event_data( | ||||
|             point_field_class=PointField | ||||
|         ) | ||||
|  | ||||
|         # find all events "near" pitchfork office, chicago. | ||||
|         # note that "near" will show the san francisco event, too, | ||||
|         # although it sorts to last. | ||||
|         events = self.Event.objects(location__near=[-87.67892, 41.9120459]) | ||||
|         self.assertEqual(events.count(), 3) | ||||
|         self.assertEqual(list(events), [event1, event3, event2]) | ||||
|  | ||||
|         # ensure ordering is respected by "near" | ||||
|         events = self.Event.objects(location__near=[-87.67892, 41.9120459]) | ||||
|         events = events.order_by("-date") | ||||
|         self.assertEqual(events.count(), 3) | ||||
|         self.assertEqual(list(events), [event3, event1, event2]) | ||||
|  | ||||
|     def test_2dsphere_near_and_max_distance(self): | ||||
|         """Ensure the "max_distance" operator works alongside the "near" | ||||
|         operator with a 2dsphere index. | ||||
|         """ | ||||
|         event1, event2, event3 = self._create_event_data( | ||||
|             point_field_class=PointField | ||||
|         ) | ||||
|  | ||||
|         # find events within 10km of san francisco | ||||
|         point = [-122.415579, 37.7566023] | ||||
|         events = self.Event.objects(location__near=point, | ||||
|                                     location__max_distance=10000) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0], event2) | ||||
|  | ||||
|         # find events within 1km of greenpoint, broolyn, nyc, ny | ||||
|         events = self.Event.objects(location__near=[-73.9509714, 40.7237134], | ||||
|                                     location__max_distance=1000) | ||||
|         self.assertEqual(events.count(), 0) | ||||
|  | ||||
|         # ensure ordering is respected by "near" | ||||
|         events = self.Event.objects( | ||||
|             location__near=[-87.67892, 41.9120459], | ||||
|             location__max_distance=10000 | ||||
|         ).order_by("-date") | ||||
|         self.assertEqual(events.count(), 2) | ||||
|         self.assertEqual(events[0], event3) | ||||
|  | ||||
|     def test_2dsphere_geo_within_box(self): | ||||
|         """Ensure the "geo_within_box" operator works with a 2dsphere | ||||
|         index. | ||||
|         """ | ||||
|         event1, event2, event3 = self._create_event_data( | ||||
|             point_field_class=PointField | ||||
|         ) | ||||
|  | ||||
|         # check that within_box works | ||||
|         box = [(-125.0, 35.0), (-100.0, 40.0)] | ||||
|         events = self.Event.objects(location__geo_within_box=box) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0].id, event2.id) | ||||
|  | ||||
|     def test_2dsphere_geo_within_polygon(self): | ||||
|         """Ensure the "geo_within_polygon" operator works with a | ||||
|         2dsphere index. | ||||
|         """ | ||||
|         event1, event2, event3 = self._create_event_data( | ||||
|             point_field_class=PointField | ||||
|         ) | ||||
|  | ||||
|         polygon = [ | ||||
|             (-87.694445, 41.912114), | ||||
|             (-87.69084, 41.919395), | ||||
|             (-87.681742, 41.927186), | ||||
|             (-87.654276, 41.911731), | ||||
|             (-87.656164, 41.898061), | ||||
|         ] | ||||
|         events = self.Event.objects(location__geo_within_polygon=polygon) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0].id, event1.id) | ||||
|  | ||||
|         polygon2 = [ | ||||
|             (-1.742249, 54.033586), | ||||
|             (-1.225891, 52.792797), | ||||
|             (-4.40094, 53.389881) | ||||
|         ] | ||||
|         events = self.Event.objects(location__geo_within_polygon=polygon2) | ||||
|         self.assertEqual(events.count(), 0) | ||||
|  | ||||
|     # $minDistance was added in MongoDB v2.6, but continued being buggy | ||||
|     # until v3.0; skip for older versions | ||||
|     @needs_mongodb_v3 | ||||
|     def test_2dsphere_near_and_min_max_distance(self): | ||||
|         """Ensure "min_distace" and "max_distance" operators work well | ||||
|         together with the "near" operator in a 2dsphere index. | ||||
|         """ | ||||
|         event1, event2, event3 = self._create_event_data( | ||||
|             point_field_class=PointField | ||||
|         ) | ||||
|  | ||||
|         # ensure min_distance and max_distance combine well | ||||
|         events = self.Event.objects( | ||||
|             location__near=[-87.67892, 41.9120459], | ||||
|             location__min_distance=1000, | ||||
|             location__max_distance=10000 | ||||
|         ).order_by("-date") | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0], event3) | ||||
|  | ||||
|         # ensure ordering is respected by "near" with "min_distance" | ||||
|         events = self.Event.objects( | ||||
|             location__near=[-87.67892, 41.9120459], | ||||
|             location__min_distance=10000 | ||||
|         ).order_by("-date") | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0], event2) | ||||
|  | ||||
|     def test_2dsphere_geo_within_center(self): | ||||
|         """Make sure the "geo_within_center" operator works with a | ||||
|         2dsphere index. | ||||
|         """ | ||||
|         event1, event2, event3 = self._create_event_data( | ||||
|             point_field_class=PointField | ||||
|         ) | ||||
|  | ||||
|         # find events within 5 degrees of pitchfork office, chicago | ||||
|         point_and_distance = [[-87.67892, 41.9120459], 2] | ||||
|         events = self.Event.objects( | ||||
|             location__geo_within_center=point_and_distance) | ||||
|         self.assertEqual(events.count(), 2) | ||||
|         events = list(events) | ||||
|         self.assertTrue(event2 not in events) | ||||
|         self.assertTrue(event1 in events) | ||||
|         self.assertTrue(event3 in events) | ||||
|  | ||||
|     def _test_embedded(self, point_field_class): | ||||
|         """Helper test method ensuring given point field class works | ||||
|         well in an embedded document. | ||||
|         """ | ||||
|         class Venue(EmbeddedDocument): | ||||
|             location = GeoPointField() | ||||
|             location = point_field_class() | ||||
|             name = StringField() | ||||
|  | ||||
|         class Event(Document): | ||||
| @@ -148,16 +320,18 @@ class GeoQueriesTest(unittest.TestCase): | ||||
|         self.assertEqual(events.count(), 3) | ||||
|         self.assertEqual(list(events), [event1, event3, event2]) | ||||
|  | ||||
|     def test_spherical_geospatial_operators(self): | ||||
|         """Ensure that spherical geospatial queries are working | ||||
|         """ | ||||
|         # Needs MongoDB > 2.6.4 https://jira.mongodb.org/browse/SERVER-14039 | ||||
|         connection = get_connection() | ||||
|         info = connection.test.command('buildInfo') | ||||
|         mongodb_version = tuple([int(i) for i in info['version'].split('.')]) | ||||
|         if mongodb_version < (2, 6, 4): | ||||
|             raise SkipTest("Need MongoDB version 2.6.4+") | ||||
|     def test_geo_spatial_embedded(self): | ||||
|         """Make sure GeoPointField works properly in an embedded document.""" | ||||
|         self._test_embedded(point_field_class=GeoPointField) | ||||
|  | ||||
|     def test_2dsphere_point_embedded(self): | ||||
|         """Make sure PointField works properly in an embedded document.""" | ||||
|         self._test_embedded(point_field_class=PointField) | ||||
|  | ||||
|     # Needs MongoDB > 2.6.4 https://jira.mongodb.org/browse/SERVER-14039 | ||||
|     @needs_mongodb_v3 | ||||
|     def test_spherical_geospatial_operators(self): | ||||
|         """Ensure that spherical geospatial queries are working.""" | ||||
|         class Point(Document): | ||||
|             location = GeoPointField() | ||||
|  | ||||
| @@ -177,7 +351,10 @@ class GeoQueriesTest(unittest.TestCase): | ||||
|  | ||||
|         # Same behavior for _within_spherical_distance | ||||
|         points = Point.objects( | ||||
|             location__within_spherical_distance=[[-122, 37.5], 60 / earth_radius] | ||||
|             location__within_spherical_distance=[ | ||||
|                 [-122, 37.5], | ||||
|                 60 / earth_radius | ||||
|             ] | ||||
|         ) | ||||
|         self.assertEqual(points.count(), 2) | ||||
|  | ||||
| @@ -194,14 +371,9 @@ class GeoQueriesTest(unittest.TestCase): | ||||
|         # Test query works with min_distance, being farer from one point | ||||
|         points = Point.objects(location__near_sphere=[-122, 37.8], | ||||
|                                location__min_distance=60 / earth_radius) | ||||
|         # The following real test passes on MongoDB 3 but minDistance seems | ||||
|         # buggy on older MongoDB versions | ||||
|         if get_connection().server_info()['versionArray'][0] > 2: | ||||
|             self.assertEqual(points.count(), 1) | ||||
|             far_point = points.first() | ||||
|             self.assertNotEqual(close_point, far_point) | ||||
|         else: | ||||
|             self.assertTrue(points.count() >= 1) | ||||
|         self.assertEqual(points.count(), 1) | ||||
|         far_point = points.first() | ||||
|         self.assertNotEqual(close_point, far_point) | ||||
|  | ||||
|         # Finds both points, but orders the north point first because it's | ||||
|         # closer to the reference point to the north. | ||||
| @@ -220,141 +392,15 @@ class GeoQueriesTest(unittest.TestCase): | ||||
|         # Finds only one point because only the first point is within 60km of | ||||
|         # the reference point to the south. | ||||
|         points = Point.objects( | ||||
|             location__within_spherical_distance=[[-122, 36.5], 60/earth_radius]) | ||||
|             location__within_spherical_distance=[ | ||||
|                 [-122, 36.5], | ||||
|                 60 / earth_radius | ||||
|             ] | ||||
|         ) | ||||
|         self.assertEqual(points.count(), 1) | ||||
|         self.assertEqual(points[0].id, south_point.id) | ||||
|  | ||||
|     def test_2dsphere_point(self): | ||||
|  | ||||
|         class Event(Document): | ||||
|             title = StringField() | ||||
|             date = DateTimeField() | ||||
|             location = PointField() | ||||
|  | ||||
|             def __unicode__(self): | ||||
|                 return self.title | ||||
|  | ||||
|         Event.drop_collection() | ||||
|  | ||||
|         event1 = Event(title="Coltrane Motion @ Double Door", | ||||
|                        date=datetime.now() - timedelta(days=1), | ||||
|                        location=[-87.677137, 41.909889]) | ||||
|         event1.save() | ||||
|         event2 = Event(title="Coltrane Motion @ Bottom of the Hill", | ||||
|                        date=datetime.now() - timedelta(days=10), | ||||
|                        location=[-122.4194155, 37.7749295]).save() | ||||
|         event3 = Event(title="Coltrane Motion @ Empty Bottle", | ||||
|                        date=datetime.now(), | ||||
|                        location=[-87.686638, 41.900474]).save() | ||||
|  | ||||
|         # find all events "near" pitchfork office, chicago. | ||||
|         # note that "near" will show the san francisco event, too, | ||||
|         # although it sorts to last. | ||||
|         events = Event.objects(location__near=[-87.67892, 41.9120459]) | ||||
|         self.assertEqual(events.count(), 3) | ||||
|         self.assertEqual(list(events), [event1, event3, event2]) | ||||
|  | ||||
|         # find events within 5 degrees of pitchfork office, chicago | ||||
|         point_and_distance = [[-87.67892, 41.9120459], 2] | ||||
|         events = Event.objects(location__geo_within_center=point_and_distance) | ||||
|         self.assertEqual(events.count(), 2) | ||||
|         events = list(events) | ||||
|         self.assertTrue(event2 not in events) | ||||
|         self.assertTrue(event1 in events) | ||||
|         self.assertTrue(event3 in events) | ||||
|  | ||||
|         # ensure ordering is respected by "near" | ||||
|         events = Event.objects(location__near=[-87.67892, 41.9120459]) | ||||
|         events = events.order_by("-date") | ||||
|         self.assertEqual(events.count(), 3) | ||||
|         self.assertEqual(list(events), [event3, event1, event2]) | ||||
|  | ||||
|         # find events within 10km of san francisco | ||||
|         point = [-122.415579, 37.7566023] | ||||
|         events = Event.objects(location__near=point, location__max_distance=10000) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0], event2) | ||||
|  | ||||
|         # find events within 1km of greenpoint, broolyn, nyc, ny | ||||
|         events = Event.objects(location__near=[-73.9509714, 40.7237134], location__max_distance=1000) | ||||
|         self.assertEqual(events.count(), 0) | ||||
|  | ||||
|         # ensure ordering is respected by "near" | ||||
|         events = Event.objects(location__near=[-87.67892, 41.9120459], | ||||
|                                location__max_distance=10000).order_by("-date") | ||||
|         self.assertEqual(events.count(), 2) | ||||
|         self.assertEqual(events[0], event3) | ||||
|  | ||||
|         # ensure min_distance and max_distance combine well | ||||
|         events = Event.objects(location__near=[-87.67892, 41.9120459], | ||||
|                                location__min_distance=1000, | ||||
|                                location__max_distance=10000).order_by("-date") | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0], event3) | ||||
|  | ||||
|         # ensure ordering is respected by "near" | ||||
|         events = Event.objects(location__near=[-87.67892, 41.9120459], | ||||
|                                # location__min_distance=10000 | ||||
|                                location__min_distance=10000).order_by("-date") | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0], event2) | ||||
|  | ||||
|         # check that within_box works | ||||
|         box = [(-125.0, 35.0), (-100.0, 40.0)] | ||||
|         events = Event.objects(location__geo_within_box=box) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0].id, event2.id) | ||||
|  | ||||
|         polygon = [ | ||||
|             (-87.694445, 41.912114), | ||||
|             (-87.69084, 41.919395), | ||||
|             (-87.681742, 41.927186), | ||||
|             (-87.654276, 41.911731), | ||||
|             (-87.656164, 41.898061), | ||||
|         ] | ||||
|         events = Event.objects(location__geo_within_polygon=polygon) | ||||
|         self.assertEqual(events.count(), 1) | ||||
|         self.assertEqual(events[0].id, event1.id) | ||||
|  | ||||
|         polygon2 = [ | ||||
|             (-1.742249, 54.033586), | ||||
|             (-1.225891, 52.792797), | ||||
|             (-4.40094, 53.389881) | ||||
|         ] | ||||
|         events = Event.objects(location__geo_within_polygon=polygon2) | ||||
|         self.assertEqual(events.count(), 0) | ||||
|  | ||||
|     def test_2dsphere_point_embedded(self): | ||||
|  | ||||
|         class Venue(EmbeddedDocument): | ||||
|             location = GeoPointField() | ||||
|             name = StringField() | ||||
|  | ||||
|         class Event(Document): | ||||
|             title = StringField() | ||||
|             venue = EmbeddedDocumentField(Venue) | ||||
|  | ||||
|         Event.drop_collection() | ||||
|  | ||||
|         venue1 = Venue(name="The Rock", location=[-87.677137, 41.909889]) | ||||
|         venue2 = Venue(name="The Bridge", location=[-122.4194155, 37.7749295]) | ||||
|  | ||||
|         event1 = Event(title="Coltrane Motion @ Double Door", | ||||
|                        venue=venue1).save() | ||||
|         event2 = Event(title="Coltrane Motion @ Bottom of the Hill", | ||||
|                        venue=venue2).save() | ||||
|         event3 = Event(title="Coltrane Motion @ Empty Bottle", | ||||
|                        venue=venue1).save() | ||||
|  | ||||
|         # find all events "near" pitchfork office, chicago. | ||||
|         # note that "near" will show the san francisco event, too, | ||||
|         # although it sorts to last. | ||||
|         events = Event.objects(venue__location__near=[-87.67892, 41.9120459]) | ||||
|         self.assertEqual(events.count(), 3) | ||||
|         self.assertEqual(list(events), [event1, event3, event2]) | ||||
|  | ||||
|     def test_linestring(self): | ||||
|  | ||||
|         class Road(Document): | ||||
|             name = StringField() | ||||
|             line = LineStringField() | ||||
| @@ -410,7 +456,6 @@ class GeoQueriesTest(unittest.TestCase): | ||||
|         self.assertEqual(1, roads) | ||||
|  | ||||
|     def test_polygon(self): | ||||
|  | ||||
|         class Road(Document): | ||||
|             name = StringField() | ||||
|             poly = PolygonField() | ||||
| @@ -507,5 +552,6 @@ class GeoQueriesTest(unittest.TestCase): | ||||
|         loc = Location.objects.as_pymongo()[0] | ||||
|         self.assertEqual(loc["poly"], {"type": "Polygon", "coordinates": [[[40, 4], [40, 6], [41, 6], [40, 4]]]}) | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|   | ||||
| @@ -19,6 +19,9 @@ from mongoengine.python_support import IS_PYMONGO_3 | ||||
| from mongoengine.queryset import (DoesNotExist, MultipleObjectsReturned, | ||||
|                                   QuerySet, QuerySetManager, queryset_manager) | ||||
|  | ||||
| from tests.utils import needs_mongodb_v26, skip_pymongo3 | ||||
|  | ||||
|  | ||||
| __all__ = ("QuerySetTest",) | ||||
|  | ||||
|  | ||||
| @@ -32,37 +35,6 @@ class db_ops_tracker(query_counter): | ||||
|         return list(self.db.system.profile.find(ignore_query)) | ||||
|  | ||||
|  | ||||
| def skip_older_mongodb(f): | ||||
|     def _inner(*args, **kwargs): | ||||
|         connection = get_connection() | ||||
|         info = connection.test.command('buildInfo') | ||||
|         mongodb_version = tuple([int(i) for i in info['version'].split('.')]) | ||||
|  | ||||
|         if mongodb_version < (2, 6): | ||||
|             raise SkipTest("Need MongoDB version 2.6+") | ||||
|  | ||||
|         return f(*args, **kwargs) | ||||
|  | ||||
|     _inner.__name__ = f.__name__ | ||||
|     _inner.__doc__ = f.__doc__ | ||||
|  | ||||
|     return _inner | ||||
|  | ||||
|  | ||||
| def skip_pymongo3(f): | ||||
|     def _inner(*args, **kwargs): | ||||
|  | ||||
|         if IS_PYMONGO_3: | ||||
|             raise SkipTest("Useless with PyMongo 3+") | ||||
|  | ||||
|         return f(*args, **kwargs) | ||||
|  | ||||
|     _inner.__name__ = f.__name__ | ||||
|     _inner.__doc__ = f.__doc__ | ||||
|  | ||||
|     return _inner | ||||
|  | ||||
|  | ||||
| class QuerySetTest(unittest.TestCase): | ||||
|  | ||||
|     def setUp(self): | ||||
| @@ -599,16 +571,23 @@ class QuerySetTest(unittest.TestCase): | ||||
|         self.assertEqual(post.comments[0].by, 'joe') | ||||
|         self.assertEqual(post.comments[0].votes.score, 4) | ||||
|  | ||||
|     @needs_mongodb_v26 | ||||
|     def test_update_min_max(self): | ||||
|         class Scores(Document): | ||||
|             high_score = IntField() | ||||
|             low_score = IntField() | ||||
|         scores = Scores(high_score=800, low_score=200) | ||||
|         scores.save() | ||||
|  | ||||
|         scores = Scores.objects.create(high_score=800, low_score=200) | ||||
|  | ||||
|         Scores.objects(id=scores.id).update(min__low_score=150) | ||||
|         self.assertEqual(Scores.objects(id=scores.id).get().low_score, 150) | ||||
|         self.assertEqual(Scores.objects.get(id=scores.id).low_score, 150) | ||||
|         Scores.objects(id=scores.id).update(min__low_score=250) | ||||
|         self.assertEqual(Scores.objects(id=scores.id).get().low_score, 150) | ||||
|         self.assertEqual(Scores.objects.get(id=scores.id).low_score, 150) | ||||
|  | ||||
|         Scores.objects(id=scores.id).update(max__high_score=1000) | ||||
|         self.assertEqual(Scores.objects.get(id=scores.id).high_score, 1000) | ||||
|         Scores.objects(id=scores.id).update(max__high_score=500) | ||||
|         self.assertEqual(Scores.objects.get(id=scores.id).high_score, 1000) | ||||
|  | ||||
|     def test_updates_can_have_match_operators(self): | ||||
|  | ||||
| @@ -1012,7 +991,7 @@ class QuerySetTest(unittest.TestCase): | ||||
|         self.assertEqual(person.name, "User A") | ||||
|         self.assertEqual(person.age, 20) | ||||
|  | ||||
|     @skip_older_mongodb | ||||
|     @needs_mongodb_v26 | ||||
|     @skip_pymongo3 | ||||
|     def test_cursor_args(self): | ||||
|         """Ensures the cursor args can be set as expected | ||||
| @@ -3129,7 +3108,7 @@ class QuerySetTest(unittest.TestCase): | ||||
|  | ||||
|         self.assertEqual(Foo.objects.distinct("bar"), [bar]) | ||||
|  | ||||
|     @skip_older_mongodb | ||||
|     @needs_mongodb_v26 | ||||
|     def test_text_indexes(self): | ||||
|         class News(Document): | ||||
|             title = StringField() | ||||
| @@ -3216,7 +3195,7 @@ class QuerySetTest(unittest.TestCase): | ||||
|             'brasil').order_by('$text_score').first() | ||||
|         self.assertEqual(item.get_text_score(), max_text_score) | ||||
|  | ||||
|     @skip_older_mongodb | ||||
|     @needs_mongodb_v26 | ||||
|     def test_distinct_handles_references_to_alias(self): | ||||
|         register_connection('testdb', 'mongoenginetest2') | ||||
|  | ||||
| @@ -4891,6 +4870,7 @@ class QuerySetTest(unittest.TestCase): | ||||
|             self.assertTrue(Person.objects._has_data(), | ||||
|                             'Cursor has data and returned False') | ||||
|  | ||||
|     @needs_mongodb_v26 | ||||
|     def test_queryset_aggregation_framework(self): | ||||
|         class Person(Document): | ||||
|             name = StringField() | ||||
| @@ -4925,17 +4905,13 @@ class QuerySetTest(unittest.TestCase): | ||||
|             {'_id': p1.pk, 'name': "ISABELLA LUANNA"} | ||||
|         ]) | ||||
|  | ||||
|         data = Person.objects( | ||||
|             age__gte=17, age__lte=40).order_by('-age').aggregate( | ||||
|                 {'$group': { | ||||
|                     '_id': None, | ||||
|                     'total': {'$sum': 1}, | ||||
|                     'avg': {'$avg': '$age'} | ||||
|                 } | ||||
|                 } | ||||
|  | ||||
|         ) | ||||
|  | ||||
|         data = Person.objects(age__gte=17, age__lte=40).order_by('-age').aggregate({ | ||||
|             '$group': { | ||||
|                 '_id': None, | ||||
|                 'total': {'$sum': 1}, | ||||
|                 'avg': {'$avg': '$age'} | ||||
|             } | ||||
|         }) | ||||
|         self.assertEqual(list(data), [ | ||||
|             {'_id': None, 'avg': 29, 'total': 2} | ||||
|         ]) | ||||
| @@ -4976,11 +4952,13 @@ class QuerySetTest(unittest.TestCase): | ||||
|         self.assertEquals(Animal.objects(folded_ears=True).count(), 1) | ||||
|         self.assertEquals(Animal.objects(whiskers_length=5.1).count(), 1) | ||||
|  | ||||
|     def test_loop_via_invalid_id_does_not_crash(self): | ||||
|     def test_loop_over_invalid_id_does_not_crash(self): | ||||
|         class Person(Document): | ||||
|             name = StringField() | ||||
|         Person.objects.delete() | ||||
|         Person._get_collection().update({"name": "a"}, {"$set": {"_id": ""}}, upsert=True) | ||||
|  | ||||
|         Person.drop_collection() | ||||
|  | ||||
|         Person._get_collection().insert({'name': 'a', 'id': ''}) | ||||
|         for p in Person.objects(): | ||||
|             self.assertEqual(p.name, 'a') | ||||
|  | ||||
|   | ||||
| @@ -35,8 +35,7 @@ class ConnectionTest(unittest.TestCase): | ||||
|         mongoengine.connection._dbs = {} | ||||
|  | ||||
|     def test_connect(self): | ||||
|         """Ensure that the connect() method works properly. | ||||
|         """ | ||||
|         """Ensure that the connect() method works properly.""" | ||||
|         connect('mongoenginetest') | ||||
|  | ||||
|         conn = get_connection() | ||||
| @@ -146,8 +145,7 @@ class ConnectionTest(unittest.TestCase): | ||||
|         self.assertEqual(expected_connection, actual_connection) | ||||
|  | ||||
|     def test_connect_uri(self): | ||||
|         """Ensure that the connect() method works properly with uri's | ||||
|         """ | ||||
|         """Ensure that the connect() method works properly with URIs.""" | ||||
|         c = connect(db='mongoenginetest', alias='admin') | ||||
|         c.admin.system.users.remove({}) | ||||
|         c.mongoenginetest.system.users.remove({}) | ||||
| @@ -227,9 +225,8 @@ class ConnectionTest(unittest.TestCase): | ||||
|         self.assertRaises(OperationFailure, get_db) | ||||
|  | ||||
|     def test_connect_uri_with_authsource(self): | ||||
|         """Ensure that the connect() method works well with | ||||
|         the option `authSource` in URI. | ||||
|         This feature was introduced in MongoDB 2.4 and removed in 2.6 | ||||
|         """Ensure that the connect() method works well with `authSource` | ||||
|         option in the URI. | ||||
|         """ | ||||
|         # Create users | ||||
|         c = connect('mongoenginetest') | ||||
| @@ -238,30 +235,31 @@ class ConnectionTest(unittest.TestCase): | ||||
|  | ||||
|         # Authentication fails without "authSource" | ||||
|         if IS_PYMONGO_3: | ||||
|             test_conn = connect('mongoenginetest', alias='test1', | ||||
|                                 host='mongodb://username2:password@localhost/mongoenginetest') | ||||
|             test_conn = connect( | ||||
|                 'mongoenginetest', alias='test1', | ||||
|                 host='mongodb://username2:password@localhost/mongoenginetest' | ||||
|             ) | ||||
|             self.assertRaises(OperationFailure, test_conn.server_info) | ||||
|         else: | ||||
|             self.assertRaises( | ||||
|                 MongoEngineConnectionError, connect, 'mongoenginetest', | ||||
|                 alias='test1', | ||||
|                 MongoEngineConnectionError, | ||||
|                 connect, 'mongoenginetest', alias='test1', | ||||
|                 host='mongodb://username2:password@localhost/mongoenginetest' | ||||
|             ) | ||||
|             self.assertRaises(MongoEngineConnectionError, get_db, 'test1') | ||||
|  | ||||
|         # Authentication succeeds with "authSource" | ||||
|         connect( | ||||
|         authd_conn = connect( | ||||
|             'mongoenginetest', alias='test2', | ||||
|             host=('mongodb://username2:password@localhost/' | ||||
|                   'mongoenginetest?authSource=admin') | ||||
|         ) | ||||
|         # This will fail starting from MongoDB 2.6+ | ||||
|         db = get_db('test2') | ||||
|         self.assertTrue(isinstance(db, pymongo.database.Database)) | ||||
|         self.assertEqual(db.name, 'mongoenginetest') | ||||
|  | ||||
|         # Clear all users | ||||
|         c.admin.system.users.remove({}) | ||||
|         authd_conn.admin.system.users.remove({}) | ||||
|  | ||||
|     def test_register_connection(self): | ||||
|         """Ensure that connections with different aliases may be registered. | ||||
|   | ||||
| @@ -1,7 +1,11 @@ | ||||
| import unittest | ||||
|  | ||||
| from nose.plugins.skip import SkipTest | ||||
|  | ||||
| from mongoengine import connect | ||||
| from mongoengine.connection import get_db | ||||
| from mongoengine.connection import get_db, get_connection | ||||
| from mongoengine.python_support import IS_PYMONGO_3 | ||||
|  | ||||
|  | ||||
| MONGO_TEST_DB = 'mongoenginetest' | ||||
|  | ||||
| @@ -20,3 +24,55 @@ class MongoDBTestCase(unittest.TestCase): | ||||
|     @classmethod | ||||
|     def tearDownClass(cls): | ||||
|         cls._connection.drop_database(MONGO_TEST_DB) | ||||
|  | ||||
|  | ||||
| def get_mongodb_version(): | ||||
|     """Return the version tuple of the MongoDB server that the default | ||||
|     connection is connected to. | ||||
|     """ | ||||
|     return tuple(get_connection().server_info()['versionArray']) | ||||
|  | ||||
| def _decorated_with_ver_requirement(func, ver_tuple): | ||||
|     """Return a given function decorated with the version requirement | ||||
|     for a particular MongoDB version tuple. | ||||
|     """ | ||||
|     def _inner(*args, **kwargs): | ||||
|         mongodb_ver = get_mongodb_version() | ||||
|         if mongodb_ver >= ver_tuple: | ||||
|             return func(*args, **kwargs) | ||||
|  | ||||
|         raise SkipTest('Needs MongoDB v{}+'.format( | ||||
|             '.'.join([str(v) for v in ver_tuple]) | ||||
|         )) | ||||
|  | ||||
|     _inner.__name__ = func.__name__ | ||||
|     _inner.__doc__ = func.__doc__ | ||||
|  | ||||
|     return _inner | ||||
|  | ||||
| def needs_mongodb_v26(func): | ||||
|     """Raise a SkipTest exception if we're working with MongoDB version | ||||
|     lower than v2.6. | ||||
|     """ | ||||
|     return _decorated_with_ver_requirement(func, (2, 6)) | ||||
|  | ||||
| def needs_mongodb_v3(func): | ||||
|     """Raise a SkipTest exception if we're working with MongoDB version | ||||
|     lower than v3.0. | ||||
|     """ | ||||
|     return _decorated_with_ver_requirement(func, (3, 0)) | ||||
|  | ||||
| def skip_pymongo3(f): | ||||
|     """Raise a SkipTest exception if we're running a test against | ||||
|     PyMongo v3.x. | ||||
|     """ | ||||
|     def _inner(*args, **kwargs): | ||||
|         if IS_PYMONGO_3: | ||||
|             raise SkipTest("Useless with PyMongo 3+") | ||||
|         return f(*args, **kwargs) | ||||
|  | ||||
|     _inner.__name__ = f.__name__ | ||||
|     _inner.__doc__ = f.__doc__ | ||||
|  | ||||
|     return _inner | ||||
|  | ||||
|   | ||||
							
								
								
									
										13
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								tox.ini
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| [tox] | ||||
| envlist = {py26,py27,py33,py34,py35,pypy,pypy3}-{mg27,mg28},flake8 | ||||
| envlist = {py27,py35,pypy,pypy3}-{mg27,mg28,mg30} | ||||
|  | ||||
| [testenv] | ||||
| commands = | ||||
| @@ -7,16 +7,7 @@ commands = | ||||
| deps = | ||||
|     nose | ||||
|     mg27: PyMongo<2.8 | ||||
|     mg28: PyMongo>=2.8,<3.0 | ||||
|     mg28: PyMongo>=2.8,<2.9 | ||||
|     mg30: PyMongo>=3.0 | ||||
|     mgdev: https://github.com/mongodb/mongo-python-driver/tarball/master | ||||
| setenv = | ||||
|     PYTHON_EGG_CACHE = {envdir}/python-eggs | ||||
| passenv = windir | ||||
|  | ||||
| [testenv:flake8] | ||||
| deps = | ||||
|     flake8 | ||||
|     flake8-import-order | ||||
| commands = | ||||
|    flake8 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user