Compare commits
224 Commits
misleading
...
v0.20.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3db9d58dac | ||
|
|
3fbe9c3cdd | ||
|
|
130e9c519c | ||
|
|
78c9e9745d | ||
|
|
38ebb5abf4 | ||
|
|
9b73be26ab | ||
|
|
fd0095b73f | ||
|
|
226049f66a | ||
|
|
dc1cf88ca6 | ||
|
|
f5f8b730b5 | ||
|
|
e8f6b42316 | ||
|
|
49b0d73654 | ||
|
|
394da67cf1 | ||
|
|
ef7da36ac6 | ||
|
|
1312100bc7 | ||
|
|
4085bc2152 | ||
|
|
f4d7e72426 | ||
|
|
ece63ad071 | ||
|
|
a9550b8243 | ||
|
|
43724e40b2 | ||
|
|
1bfa40e926 | ||
|
|
d493f71c4e | ||
|
|
87f4d1a323 | ||
|
|
0a0e6114f5 | ||
|
|
41d36fa3bf | ||
|
|
707923e3f5 | ||
|
|
d9b9581df2 | ||
|
|
463e7c66af | ||
|
|
2be28a22a7 | ||
|
|
d73f0bb1af | ||
|
|
ce74978b1e | ||
|
|
2b0157aecd | ||
|
|
f49baf5d90 | ||
|
|
7cc964c7d8 | ||
|
|
bc77322c2f | ||
|
|
8913a74a86 | ||
|
|
af35b25d15 | ||
|
|
476b07af6e | ||
|
|
e2b9a02531 | ||
|
|
6cc6229066 | ||
|
|
4c62a060f0 | ||
|
|
3d80637fa4 | ||
|
|
68be9fe979 | ||
|
|
547cd4a3ae | ||
|
|
ee2d50b2d1 | ||
|
|
15c3ddece8 | ||
|
|
beaa9744b7 | ||
|
|
8eb51790b5 | ||
|
|
aadc6262ed | ||
|
|
00ae6298d4 | ||
|
|
ad0669a326 | ||
|
|
85df76c623 | ||
|
|
87512246cb | ||
|
|
a3f9016ae9 | ||
|
|
4e58e9f8d1 | ||
|
|
7c533394fd | ||
|
|
333e014f13 | ||
|
|
c0c0efce18 | ||
|
|
beabaee345 | ||
|
|
c937af3919 | ||
|
|
aa4a6ae023 | ||
|
|
b57946ec98 | ||
|
|
1e110a2c41 | ||
|
|
b234aa48e4 | ||
|
|
8086576677 | ||
|
|
03e34299f0 | ||
|
|
421e3f324f | ||
|
|
a0b803959c | ||
|
|
ff4d57032a | ||
|
|
ba34589065 | ||
|
|
a4d11eef46 | ||
|
|
fda2e2b47a | ||
|
|
d287f480e5 | ||
|
|
d85f0e6226 | ||
|
|
cfb4943986 | ||
|
|
b453a96211 | ||
|
|
81f9b351b3 | ||
|
|
4bca3de42f | ||
|
|
235b1a3679 | ||
|
|
450658d7ac | ||
|
|
8e17e42e26 | ||
|
|
2d6a4c4b90 | ||
|
|
38703acc29 | ||
|
|
095217e797 | ||
|
|
86e965f854 | ||
|
|
57db68dc04 | ||
|
|
72de6d67c7 | ||
|
|
b2c3acd025 | ||
|
|
605de59bd0 | ||
|
|
e0565ddac5 | ||
|
|
18b68f1b80 | ||
|
|
ea88806630 | ||
|
|
d738462139 | ||
|
|
9490ad2bf7 | ||
|
|
705c55ce24 | ||
|
|
59fbd505a0 | ||
|
|
1cc20c9770 | ||
|
|
f8f267a880 | ||
|
|
80ea1f6883 | ||
|
|
75ee282a3d | ||
|
|
4edad4601c | ||
|
|
152b51fd33 | ||
|
|
66a0fca4ad | ||
|
|
e7c7a66cd1 | ||
|
|
b3dbb87c3c | ||
|
|
3d45538998 | ||
|
|
8df9d3fef9 | ||
|
|
99e660c66d | ||
|
|
aa02f87b69 | ||
|
|
f0d1ee2cb4 | ||
|
|
ca4967311d | ||
|
|
65eb6ab611 | ||
|
|
1cb2f7814c | ||
|
|
b5485b16e6 | ||
|
|
62c8597a3b | ||
|
|
488604ff2e | ||
|
|
bd88a17b8e | ||
|
|
8e892dccfe | ||
|
|
c22eb34017 | ||
|
|
dcf3edb03e | ||
|
|
c85b59d3b5 | ||
|
|
1170de1e8e | ||
|
|
332bd767d4 | ||
|
|
0053b30237 | ||
|
|
d44533d956 | ||
|
|
12d8bd5a22 | ||
|
|
ae326678ec | ||
|
|
8d31f165c0 | ||
|
|
cfd4d6a161 | ||
|
|
329f030a41 | ||
|
|
68dc2925fb | ||
|
|
0d4e61d489 | ||
|
|
dc7b96a569 | ||
|
|
50882e5bb0 | ||
|
|
280a73af3b | ||
|
|
d8c0631dab | ||
|
|
9166ba91d7 | ||
|
|
6bc4e602bb | ||
|
|
45a7520fc3 | ||
|
|
64c0cace85 | ||
|
|
82af5e4a19 | ||
|
|
7e0ba1b335 | ||
|
|
44b7f792fe | ||
|
|
a3e432eb68 | ||
|
|
009f9a2b14 | ||
|
|
2ca905b6e5 | ||
|
|
3b099f936a | ||
|
|
4d6ddb070e | ||
|
|
b205314424 | ||
|
|
e83132f32c | ||
|
|
1b38309d70 | ||
|
|
6e8196d475 | ||
|
|
90fecc56dd | ||
|
|
d3d7f0e670 | ||
|
|
37ffeafeff | ||
|
|
abc159b7b9 | ||
|
|
648b28876d | ||
|
|
5b9f2bac87 | ||
|
|
17151f67c2 | ||
|
|
5f14d958ac | ||
|
|
bd6c52e025 | ||
|
|
cb77bb6b69 | ||
|
|
78b240b740 | ||
|
|
7e30f00178 | ||
|
|
35310dbc73 | ||
|
|
af82c07acc | ||
|
|
3f75f30f26 | ||
|
|
f7f0e10d4d | ||
|
|
091238a2cf | ||
|
|
0458ef869e | ||
|
|
0bf08db7b9 | ||
|
|
d3420918cd | ||
|
|
138e759161 | ||
|
|
f1d6ce7d12 | ||
|
|
ff749a7a0a | ||
|
|
bff78ca8dd | ||
|
|
81647d67a0 | ||
|
|
d8924ed892 | ||
|
|
799cdafae6 | ||
|
|
bc0c55e49a | ||
|
|
c61c6a8525 | ||
|
|
3e764d068c | ||
|
|
ac25f4b98b | ||
|
|
aa6ff8c84a | ||
|
|
37ca79e9c5 | ||
|
|
6040b4b494 | ||
|
|
51ea3e3c6f | ||
|
|
5a16dda50d | ||
|
|
bbfa978861 | ||
|
|
54ca7bf09f | ||
|
|
8bf5370b6c | ||
|
|
ecefa05e03 | ||
|
|
e013494fb2 | ||
|
|
4853f74dbf | ||
|
|
6f45ee6813 | ||
|
|
c60ed32f3a | ||
|
|
178851589d | ||
|
|
5bcc679194 | ||
|
|
1e17b5ac66 | ||
|
|
19f12f3f2f | ||
|
|
71e8d9a490 | ||
|
|
e3cd553f82 | ||
|
|
b61c8cd104 | ||
|
|
8f288fe458 | ||
|
|
02a920feea | ||
|
|
be2c4f2b3c | ||
|
|
7ac74b1c1f | ||
|
|
933cb1d5c7 | ||
|
|
6203e30152 | ||
|
|
7d94af0e31 | ||
|
|
564a2b5f1e | ||
|
|
1dbe7a3163 | ||
|
|
47f8a126ca | ||
|
|
693195f70b | ||
|
|
2267b7e7d7 | ||
|
|
47c67ecc99 | ||
|
|
4c4b7cbeae | ||
|
|
ddececbfea | ||
|
|
71a6f3d1a4 | ||
|
|
fbb3bf869c | ||
|
|
b887ea9623 | ||
|
|
c68e3e1238 | ||
|
|
c5080e4030 | ||
|
|
6a4c342e45 |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,8 +1,15 @@
|
|||||||
.*
|
|
||||||
!.gitignore
|
!.gitignore
|
||||||
*~
|
*~
|
||||||
*.py[co]
|
*.py[co]
|
||||||
.*.sw[po]
|
.*.sw[po]
|
||||||
|
.cache/
|
||||||
|
.coverage
|
||||||
|
.coveragerc
|
||||||
|
.env
|
||||||
|
.idea/
|
||||||
|
.pytest_cache/
|
||||||
|
.tox/
|
||||||
|
.eggs/
|
||||||
*.egg
|
*.egg
|
||||||
docs/.build
|
docs/.build
|
||||||
docs/_build
|
docs/_build
|
||||||
@@ -13,8 +20,6 @@ env/
|
|||||||
.settings
|
.settings
|
||||||
.project
|
.project
|
||||||
.pydevproject
|
.pydevproject
|
||||||
tests/test_bugfix.py
|
|
||||||
htmlcov/
|
htmlcov/
|
||||||
venv
|
venv
|
||||||
venv3
|
venv3
|
||||||
scratchpad
|
|
||||||
|
|||||||
@@ -5,17 +5,12 @@ pylint:
|
|||||||
|
|
||||||
options:
|
options:
|
||||||
additional-builtins:
|
additional-builtins:
|
||||||
# add xrange and long as valid built-ins. In Python 3, xrange is
|
# add long as valid built-ins.
|
||||||
# translated into range and long is translated into int via 2to3 (see
|
|
||||||
# "use_2to3" in setup.py). This should be removed when we drop Python
|
|
||||||
# 2 support (which probably won't happen any time soon).
|
|
||||||
- xrange
|
|
||||||
- long
|
- long
|
||||||
|
|
||||||
pyflakes:
|
pyflakes:
|
||||||
disable:
|
disable:
|
||||||
# undefined variables are already covered by pylint (and exclude
|
# undefined variables are already covered by pylint (and exclude long)
|
||||||
# xrange & long)
|
|
||||||
- F821
|
- F821
|
||||||
|
|
||||||
ignore-paths:
|
ignore-paths:
|
||||||
|
|||||||
12
.pre-commit-config.yaml
Normal file
12
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fail_fast: false
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/ambv/black
|
||||||
|
rev: 19.10b0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
|
rev: 3.8.0a2
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
additional_dependencies:
|
||||||
|
- flake8-import-order
|
||||||
66
.travis.yml
66
.travis.yml
@@ -1,13 +1,10 @@
|
|||||||
# For full coverage, we'd have to test all supported Python, MongoDB, and
|
# For full coverage, we'd have to test all supported Python, MongoDB, and
|
||||||
# PyMongo combinations. However, that would result in an overly long build
|
# 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
|
# with a very large number of jobs, hence we only test a subset of all the
|
||||||
# combinations:
|
# combinations.
|
||||||
# * MongoDB v3.4 & the latest PyMongo v3.x is currently the "main" setup,
|
# * Python3.7, MongoDB v3.4 & the latest PyMongo v3.x is currently the "main" setup,
|
||||||
# tested against Python v2.7, v3.5, v3.6, and PyPy.
|
# Other combinations are tested. See below for the details or check the travis jobs
|
||||||
# * Besides that, we test the lowest actively supported Python/MongoDB/PyMongo
|
|
||||||
# combination: MongoDB v3.4, PyMongo v3.4, Python v2.7.
|
|
||||||
# * MongoDB v3.6 is tested against Python v3.6, and PyMongo v3.6, v3.7, v3.8.
|
|
||||||
#
|
|
||||||
# We should periodically check MongoDB Server versions supported by MongoDB
|
# We should periodically check MongoDB Server versions supported by MongoDB
|
||||||
# Inc., add newly released versions to the test matrix, and remove versions
|
# Inc., add newly released versions to the test matrix, and remove versions
|
||||||
# which have reached their End of Life. See:
|
# which have reached their End of Life. See:
|
||||||
@@ -16,38 +13,43 @@
|
|||||||
#
|
#
|
||||||
# Reminder: Update README.rst if you change MongoDB versions we test.
|
# Reminder: Update README.rst if you change MongoDB versions we test.
|
||||||
|
|
||||||
|
|
||||||
language: python
|
language: python
|
||||||
|
dist: xenial
|
||||||
python:
|
python:
|
||||||
- 2.7
|
|
||||||
- 3.5
|
- 3.5
|
||||||
- 3.6
|
- 3.6
|
||||||
- 3.7
|
- 3.7
|
||||||
- pypy
|
- 3.8
|
||||||
- pypy3
|
- pypy3
|
||||||
|
|
||||||
dist: xenial
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- MONGODB_3_4=3.4.17
|
- MONGODB_3_4=3.4.17
|
||||||
- MONGODB_3_6=3.6.12
|
- MONGODB_3_6=3.6.12
|
||||||
- PYMONGO_3_6=3.6
|
- MONGODB_4_0=4.0.13
|
||||||
|
|
||||||
- PYMONGO_3_4=3.4
|
- PYMONGO_3_4=3.4
|
||||||
|
- PYMONGO_3_6=3.6
|
||||||
|
- PYMONGO_3_9=3.9
|
||||||
|
- PYMONGO_3_10=3.10
|
||||||
|
|
||||||
|
- MAIN_PYTHON_VERSION=3.7
|
||||||
matrix:
|
matrix:
|
||||||
- MONGODB=${MONGODB_3_4} PYMONGO=${PYMONGO_3_6}
|
- MONGODB=${MONGODB_3_4} PYMONGO=${PYMONGO_3_10}
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
|
||||||
# Finish the build as soon as one job fails
|
# Finish the build as soon as one job fails
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- python: 2.7
|
|
||||||
env: MONGODB=${MONGODB_3_4} PYMONGO=${PYMONGO_3_4}
|
|
||||||
- python: 3.7
|
- python: 3.7
|
||||||
env: MONGODB=${MONGODB_3_6} PYMONGO=${PYMONGO_3_6}
|
env: MONGODB=${MONGODB_3_6} PYMONGO=${PYMONGO_3_6}
|
||||||
|
- python: 3.7
|
||||||
|
env: MONGODB=${MONGODB_3_6} PYMONGO=${PYMONGO_3_9}
|
||||||
|
- python: 3.7
|
||||||
|
env: MONGODB=${MONGODB_3_6} PYMONGO=${PYMONGO_3_10}
|
||||||
|
- python: 3.8
|
||||||
|
env: MONGODB=${MONGODB_4_0} PYMONGO=${PYMONGO_3_10}
|
||||||
|
|
||||||
install:
|
install:
|
||||||
# Install Mongo
|
# Install Mongo
|
||||||
@@ -57,29 +59,23 @@ install:
|
|||||||
# Install Python dependencies.
|
# Install Python dependencies.
|
||||||
- pip install --upgrade pip
|
- pip install --upgrade pip
|
||||||
- pip install coveralls
|
- pip install coveralls
|
||||||
- pip install flake8 flake8-import-order
|
- pip install pre-commit
|
||||||
- pip install tox # tox 3.11.0 has requirement virtualenv>=14.0.0
|
- pip install tox
|
||||||
- pip install virtualenv # virtualenv>=14.0.0 has dropped Python 3.2 support (and pypy3 is based on py32)
|
# tox dryrun to setup the tox venv (we run a mock test).
|
||||||
# Install the tox venv.
|
- tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -a "-k=test_ci_placeholder"
|
||||||
- tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -e test
|
|
||||||
# Install black for Python v3.7 only.
|
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then pip install black; fi
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- mkdir ${PWD}/mongodb-linux-x86_64-${MONGODB}/data
|
- mkdir ${PWD}/mongodb-linux-x86_64-${MONGODB}/data
|
||||||
- ${PWD}/mongodb-linux-x86_64-${MONGODB}/bin/mongod --dbpath ${PWD}/mongodb-linux-x86_64-${MONGODB}/data --logpath ${PWD}/mongodb-linux-x86_64-${MONGODB}/mongodb.log --fork
|
- ${PWD}/mongodb-linux-x86_64-${MONGODB}/bin/mongod --dbpath ${PWD}/mongodb-linux-x86_64-${MONGODB}/data --logpath ${PWD}/mongodb-linux-x86_64-${MONGODB}/mongodb.log --fork
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then flake8 .; else echo "flake8 only runs on py27"; fi # Run flake8 for Python 2.7 only
|
# Run pre-commit hooks (black, flake8, etc) on entire codebase
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then black --check .; else echo "black only runs on py37"; fi # Run black for Python 3.7 only
|
- if [[ $TRAVIS_PYTHON_VERSION == $MAIN_PYTHON_VERSION ]]; then pre-commit run -a; else echo "pre-commit checks only runs on py37"; fi
|
||||||
- mongo --eval 'db.version();' # Make sure mongo is awake
|
- mongo --eval 'db.version();' # Make sure mongo is awake
|
||||||
|
|
||||||
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/') -- -a "--cov=mongoengine"
|
||||||
|
|
||||||
# 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_success:
|
after_success:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coveralls --verbose; fi
|
- - if [[ $TRAVIS_PYTHON_VERSION == $MAIN_PYTHON_VERSION ]]; then coveralls --verbose; else echo "coveralls only sent for py37"; fi
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
irc: irc.freenode.org#mongoengine
|
irc: irc.freenode.org#mongoengine
|
||||||
@@ -101,11 +97,11 @@ deploy:
|
|||||||
distributions: "sdist bdist_wheel"
|
distributions: "sdist bdist_wheel"
|
||||||
|
|
||||||
# Only deploy on tagged commits (aka GitHub releases) and only for the parent
|
# Only deploy on tagged commits (aka GitHub releases) and only for the parent
|
||||||
# repo's builds running Python v2.7 along with PyMongo v3.x and MongoDB v3.4.
|
# repo's builds running Python v3.7 along with PyMongo v3.x and MongoDB v3.4.
|
||||||
# We run Travis against many different Python, PyMongo, and MongoDB versions
|
# We run Travis against many different Python, PyMongo, and MongoDB versions
|
||||||
# and we don't want the deploy to occur multiple times).
|
# and we don't want the deploy to occur multiple times).
|
||||||
on:
|
on:
|
||||||
tags: true
|
tags: true
|
||||||
repo: MongoEngine/mongoengine
|
repo: MongoEngine/mongoengine
|
||||||
condition: ($PYMONGO = ${PYMONGO_3_6}) && ($MONGODB = ${MONGODB_3_4})
|
condition: ($PYMONGO = ${PYMONGO_3_10}) && ($MONGODB = ${MONGODB_3_4})
|
||||||
python: 2.7
|
python: 3.7
|
||||||
|
|||||||
5
AUTHORS
5
AUTHORS
@@ -252,3 +252,8 @@ that much better:
|
|||||||
* Paulo Amaral (https://github.com/pauloAmaral)
|
* Paulo Amaral (https://github.com/pauloAmaral)
|
||||||
* Gaurav Dadhania (https://github.com/GVRV)
|
* Gaurav Dadhania (https://github.com/GVRV)
|
||||||
* Yurii Andrieiev (https://github.com/yandrieiev)
|
* Yurii Andrieiev (https://github.com/yandrieiev)
|
||||||
|
* Filip Kucharczyk (https://github.com/Pacu2)
|
||||||
|
* Eric Timmons (https://github.com/daewok)
|
||||||
|
* Matthew Simpson (https://github.com/mcsimps2)
|
||||||
|
* Leonardo Domingues (https://github.com/leodmgs)
|
||||||
|
* Agustin Barto (https://github.com/abarto)
|
||||||
|
|||||||
@@ -20,19 +20,43 @@ post to the `user group <http://groups.google.com/group/mongoengine-users>`
|
|||||||
Supported Interpreters
|
Supported Interpreters
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
MongoEngine supports CPython 2.7 and newer. Language
|
MongoEngine supports CPython 3.5 and newer as well as Pypy3.
|
||||||
features not supported by all interpreters can not be used.
|
Language features not supported by all interpreters can not be used.
|
||||||
The codebase is written in python 2 so you must be using python 2
|
|
||||||
when developing new features. Compatibility of the library with Python 3
|
Python3 codebase
|
||||||
relies on the 2to3 package that gets executed as part of the installation
|
----------------------
|
||||||
build. You should ensure that your code is properly converted by
|
|
||||||
`2to3 <http://docs.python.org/library/2to3.html>`_.
|
Since 0.20, the codebase is exclusively Python 3.
|
||||||
|
|
||||||
|
Earlier versions were exclusively Python2, and were relying on 2to3 to support Python3 installs.
|
||||||
|
Travis runs the tests against the main Python 3.x versions.
|
||||||
|
|
||||||
|
|
||||||
Style Guide
|
Style Guide
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
MongoEngine uses `black <https://github.com/python/black>`_ for code
|
MongoEngine's codebase is formatted with `black <https://github.com/python/black>`_, other tools like
|
||||||
formatting.
|
flake8 are also used. Those tools will run as part of the CI and will fail in case the code is not formatted properly.
|
||||||
|
|
||||||
|
To install all development tools, simply run the following commands:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ python -m pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
|
||||||
|
You can install `pre-commit <https://pre-commit.com/>`_ into your git hooks,
|
||||||
|
to automatically check and fix any formatting issue before creating a
|
||||||
|
git commit.
|
||||||
|
|
||||||
|
To enable ``pre-commit`` simply run:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pre-commit install
|
||||||
|
|
||||||
|
See the ``.pre-commit-config.yaml`` configuration file for more information
|
||||||
|
on how it works.
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
-------
|
-------
|
||||||
@@ -54,7 +78,7 @@ General Guidelines
|
|||||||
should adapt to the breaking change in docs/upgrade.rst.
|
should adapt to the breaking change in docs/upgrade.rst.
|
||||||
- Write inline documentation for new classes and methods.
|
- Write inline documentation for new classes and methods.
|
||||||
- Write tests and make sure they pass (make sure you have a mongod
|
- Write tests and make sure they pass (make sure you have a mongod
|
||||||
running on the default port, then execute ``python setup.py nosetests``
|
running on the default port, then execute ``python setup.py test``
|
||||||
from the cmd line to run the test suite).
|
from the cmd line to run the test suite).
|
||||||
- Ensure tests pass on all supported Python, PyMongo, and MongoDB versions.
|
- Ensure tests pass on all supported Python, PyMongo, and MongoDB versions.
|
||||||
You can test various Python and PyMongo versions locally by executing
|
You can test various Python and PyMongo versions locally by executing
|
||||||
|
|||||||
35
README.rst
35
README.rst
@@ -26,15 +26,15 @@ an `API reference <https://mongoengine-odm.readthedocs.io/apireference.html>`_.
|
|||||||
|
|
||||||
Supported MongoDB Versions
|
Supported MongoDB Versions
|
||||||
==========================
|
==========================
|
||||||
MongoEngine is currently tested against MongoDB v3.4 and v3.6. Future versions
|
MongoEngine is currently tested against MongoDB v3.4, v3.6 and v4.0. Future versions
|
||||||
should be supported as well, but aren't actively tested at the moment. Make
|
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
|
sure to open an issue or submit a pull request if you experience any problems
|
||||||
with MongoDB version > 3.6.
|
with MongoDB version > 4.0.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
We recommend the use of `virtualenv <https://virtualenv.pypa.io/>`_ and of
|
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``.
|
`pip <https://pip.pypa.io/>`_. You can then use ``python -m pip install -U mongoengine``.
|
||||||
You may also have `setuptools <http://peak.telecommunity.com/DevCenter/setuptools>`_
|
You may also have `setuptools <http://peak.telecommunity.com/DevCenter/setuptools>`_
|
||||||
and thus you can use ``easy_install -U mongoengine``. Another option is
|
and thus you can use ``easy_install -U mongoengine``. Another option is
|
||||||
`pipenv <https://docs.pipenv.org/>`_. You can then use ``pipenv install mongoengine``
|
`pipenv <https://docs.pipenv.org/>`_. You can then use ``pipenv install mongoengine``
|
||||||
@@ -42,13 +42,14 @@ to both create the virtual environment and install the package. Otherwise, you c
|
|||||||
download the source from `GitHub <http://github.com/MongoEngine/mongoengine>`_ and
|
download the source from `GitHub <http://github.com/MongoEngine/mongoengine>`_ and
|
||||||
run ``python setup.py install``.
|
run ``python setup.py install``.
|
||||||
|
|
||||||
|
The support for Python2 was dropped with MongoEngine 0.20.0
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
============
|
============
|
||||||
All of the dependencies can easily be installed via `pip <https://pip.pypa.io/>`_.
|
All of the dependencies can easily be installed via `python -m pip <https://pip.pypa.io/>`_.
|
||||||
At the very least, you'll need these two packages to use MongoEngine:
|
At the very least, you'll need these two packages to use MongoEngine:
|
||||||
|
|
||||||
- pymongo>=3.4
|
- pymongo>=3.4
|
||||||
- six>=1.10.0
|
|
||||||
|
|
||||||
If you utilize a ``DateTimeField``, you might also use a more flexible date parser:
|
If you utilize a ``DateTimeField``, you might also use a more flexible date parser:
|
||||||
|
|
||||||
@@ -58,6 +59,10 @@ If you need to use an ``ImageField`` or ``ImageGridFsProxy``:
|
|||||||
|
|
||||||
- Pillow>=2.0.0
|
- Pillow>=2.0.0
|
||||||
|
|
||||||
|
If you need to use signals:
|
||||||
|
|
||||||
|
- blinker>=1.3
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
========
|
========
|
||||||
Some simple examples of what MongoEngine code looks like:
|
Some simple examples of what MongoEngine code looks like:
|
||||||
@@ -91,12 +96,11 @@ Some simple examples of what MongoEngine code looks like:
|
|||||||
|
|
||||||
# Iterate over all posts using the BlogPost superclass
|
# Iterate over all posts using the BlogPost superclass
|
||||||
>>> for post in BlogPost.objects:
|
>>> for post in BlogPost.objects:
|
||||||
... print '===', post.title, '==='
|
... print('===', post.title, '===')
|
||||||
... if isinstance(post, TextPost):
|
... if isinstance(post, TextPost):
|
||||||
... print post.content
|
... print(post.content)
|
||||||
... elif isinstance(post, LinkPost):
|
... elif isinstance(post, LinkPost):
|
||||||
... print 'Link:', post.url
|
... print('Link:', post.url)
|
||||||
... print
|
|
||||||
...
|
...
|
||||||
|
|
||||||
# Count all blog posts and its subtypes
|
# Count all blog posts and its subtypes
|
||||||
@@ -116,7 +120,8 @@ Some simple examples of what MongoEngine code looks like:
|
|||||||
Tests
|
Tests
|
||||||
=====
|
=====
|
||||||
To run the test suite, ensure you are running a local instance of MongoDB on
|
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 ``pytest`` installed. Then, run ``python setup.py test``
|
||||||
|
or simply ``pytest``.
|
||||||
|
|
||||||
To run the test suite on every supported Python and PyMongo version, you can
|
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
|
use ``tox``. You'll need to make sure you have each supported Python version
|
||||||
@@ -125,20 +130,18 @@ installed in your environment and then:
|
|||||||
.. code-block:: shell
|
.. code-block:: shell
|
||||||
|
|
||||||
# Install tox
|
# Install tox
|
||||||
$ pip install tox
|
$ python -m pip install tox
|
||||||
# Run the test suites
|
# Run the test suites
|
||||||
$ tox
|
$ tox
|
||||||
|
|
||||||
If you wish to run a subset of tests, use the nosetests convention:
|
If you wish to run a subset of tests, use the pytest convention:
|
||||||
|
|
||||||
.. code-block:: shell
|
.. code-block:: shell
|
||||||
|
|
||||||
# Run all the tests in a particular test file
|
# Run all the tests in a particular test file
|
||||||
$ python setup.py nosetests --tests tests/fields/fields.py
|
$ pytest tests/fields/test_fields.py
|
||||||
# Run only particular test class in that file
|
# Run only particular test class in that file
|
||||||
$ python setup.py nosetests --tests tests/fields/fields.py:FieldTest
|
$ pytest tests/fields/test_fields.py::TestField
|
||||||
# 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
|
Community
|
||||||
=========
|
=========
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import timeit
|
|||||||
def main():
|
def main():
|
||||||
setup = """
|
setup = """
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
|
|
||||||
connection = MongoClient()
|
connection = MongoClient()
|
||||||
connection.drop_database('mongoengine_benchmark_test')
|
connection.drop_database('mongoengine_benchmark_test')
|
||||||
"""
|
"""
|
||||||
|
|
||||||
stmt = """
|
stmt = """
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
|
|
||||||
connection = MongoClient()
|
connection = MongoClient()
|
||||||
|
|
||||||
db = connection.mongoengine_benchmark_test
|
db = connection.mongoengine_benchmark_test
|
||||||
@@ -56,6 +58,7 @@ myNoddys = noddy.find()
|
|||||||
|
|
||||||
setup = """
|
setup = """
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
|
|
||||||
connection = MongoClient()
|
connection = MongoClient()
|
||||||
connection.drop_database('mongoengine_benchmark_test')
|
connection.drop_database('mongoengine_benchmark_test')
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|||||||
@@ -6,6 +6,34 @@ Changelog
|
|||||||
Development
|
Development
|
||||||
===========
|
===========
|
||||||
- (Fill this out as you fix issues and develop your features).
|
- (Fill this out as you fix issues and develop your features).
|
||||||
|
|
||||||
|
Changes in 0.20.0
|
||||||
|
=================
|
||||||
|
- ATTENTION: Drop support for Python2
|
||||||
|
- Add Mongo 4.0 to Travis
|
||||||
|
- Fix error when setting a string as a ComplexDateTimeField #2253
|
||||||
|
- Bump development Status classifier to Production/Stable #2232
|
||||||
|
- Improve Queryset.get to avoid confusing MultipleObjectsReturned message in case multiple match are found #630
|
||||||
|
- Fixed a bug causing inaccurate query results, while combining ``__raw__`` and regular filters for the same field #2264
|
||||||
|
- Add support for the `elemMatch` projection operator in .fields() (e.g BlogPost.objects.fields(elemMatch__comments="test")) #2267
|
||||||
|
- DictField validate failed without default connection (bug introduced in 0.19.0) #2239
|
||||||
|
- Remove methods that were deprecated years ago:
|
||||||
|
- name parameter in Field constructor e.g `StringField(name="...")`, was replaced by db_field
|
||||||
|
- Queryset.slave_okay() was deprecated since pymongo3
|
||||||
|
- dropDups was dropped with MongoDB3
|
||||||
|
- ``Queryset._ensure_indexes`` and ``Queryset.ensure_indexes``, the right method to use is ``Document.ensure_indexes``
|
||||||
|
- Added pre-commit for development/CI #2212
|
||||||
|
- Renamed requirements-lint.txt to requirements-dev.txt #2212
|
||||||
|
- Support for setting ReadConcern #2255
|
||||||
|
|
||||||
|
Changes in 0.19.1
|
||||||
|
=================
|
||||||
|
- Requires Pillow < 7.0.0 as it dropped Python2 support
|
||||||
|
- DEPRECATION: The interface of ``QuerySet.aggregate`` method was changed, it no longer takes an unpacked list of
|
||||||
|
pipeline steps (*pipeline) but simply takes the pipeline list just like ``pymongo.Collection.aggregate`` does. #2079
|
||||||
|
|
||||||
|
Changes in 0.19.0
|
||||||
|
=================
|
||||||
- BREAKING CHANGE: ``class_check`` and ``read_preference`` keyword arguments are no longer available when filtering a ``QuerySet``. #2112
|
- BREAKING CHANGE: ``class_check`` and ``read_preference`` keyword arguments are no longer available when filtering a ``QuerySet``. #2112
|
||||||
- Instead of ``Doc.objects(foo=bar, read_preference=...)`` use ``Doc.objects(foo=bar).read_preference(...)``.
|
- Instead of ``Doc.objects(foo=bar, read_preference=...)`` use ``Doc.objects(foo=bar).read_preference(...)``.
|
||||||
- Instead of ``Doc.objects(foo=bar, class_check=False)`` use ``Doc.objects(foo=bar).clear_cls_query(...)``.
|
- Instead of ``Doc.objects(foo=bar, class_check=False)`` use ``Doc.objects(foo=bar).clear_cls_query(...)``.
|
||||||
@@ -15,9 +43,23 @@ Development
|
|||||||
- If you catch/use ``MongoEngineConnectionError`` in your code, you'll have to rename it.
|
- If you catch/use ``MongoEngineConnectionError`` in your code, you'll have to rename it.
|
||||||
- BREAKING CHANGE: Positional arguments when instantiating a document are no longer supported. #2103
|
- BREAKING CHANGE: Positional arguments when instantiating a document are no longer supported. #2103
|
||||||
- From now on keyword arguments (e.g. ``Doc(field_name=value)``) are required.
|
- From now on keyword arguments (e.g. ``Doc(field_name=value)``) are required.
|
||||||
|
- BREAKING CHANGE: A ``LazyReferenceField`` is now stored in the ``_data`` field of its parent as a ``DBRef``, ``Document``, or ``EmbeddedDocument`` (``ObjectId`` is no longer allowed). #2182
|
||||||
|
- DEPRECATION: ``Q.empty`` & ``QNode.empty`` are marked as deprecated and will be removed in a next version of MongoEngine. #2210
|
||||||
|
- Added ability to check if Q or QNode are empty by parsing them to bool.
|
||||||
|
- Instead of ``Q(name="John").empty`` use ``not Q(name="John")``.
|
||||||
- Fix updating/modifying/deleting/reloading a document that's sharded by a field with ``db_field`` specified. #2125
|
- Fix updating/modifying/deleting/reloading a document that's sharded by a field with ``db_field`` specified. #2125
|
||||||
|
- Only set no_cursor_timeout when requested (fixes an incompatibility with MongoDB 4.2) #2148
|
||||||
- ``ListField`` now accepts an optional ``max_length`` parameter. #2110
|
- ``ListField`` now accepts an optional ``max_length`` parameter. #2110
|
||||||
|
- Improve error message related to InvalidDocumentError #2180
|
||||||
|
- Added BulkWriteError to replace NotUniqueError which was misleading in bulk write insert #2152
|
||||||
|
- Added ability to compare Q and Q operations #2204
|
||||||
|
- Added ability to use a db alias on query_counter #2194
|
||||||
|
- Added ability to specify collations for querysets with ``Doc.objects.collation`` #2024
|
||||||
|
- Fix updates of a list field by negative index #2094
|
||||||
|
- Switch from nosetest to pytest as test runner #2114
|
||||||
- The codebase is now formatted using ``black``. #2109
|
- The codebase is now formatted using ``black``. #2109
|
||||||
|
- Documentation improvements:
|
||||||
|
- Documented how `pymongo.monitoring` can be used to log all queries issued by MongoEngine to the driver.
|
||||||
|
|
||||||
Changes in 0.18.2
|
Changes in 0.18.2
|
||||||
=================
|
=================
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
# All configuration values have a default; values that are commented out
|
# All configuration values have a default; values that are commented out
|
||||||
# serve to show the default.
|
# serve to show the default.
|
||||||
|
|
||||||
import sys, os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
import sphinx_rtd_theme
|
import sphinx_rtd_theme
|
||||||
|
|
||||||
|
|||||||
12
docs/faq.rst
Normal file
12
docs/faq.rst
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
==========================
|
||||||
|
Frequently Asked Questions
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Does MongoEngine support asynchronous drivers (Motor, TxMongo)?
|
||||||
|
---------------------------------------------------------------
|
||||||
|
|
||||||
|
No, MongoEngine is exclusively based on PyMongo and isn't designed to support other driver.
|
||||||
|
If this is a requirement for your project, check the alternative: `uMongo`_ and `MotorEngine`_.
|
||||||
|
|
||||||
|
.. _uMongo: https://umongo.readthedocs.io/
|
||||||
|
.. _MotorEngine: https://motorengine.readthedocs.io/
|
||||||
@@ -352,7 +352,7 @@ Its value can take any of the following constants:
|
|||||||
Deletion is denied if there still exist references to the object being
|
Deletion is denied if there still exist references to the object being
|
||||||
deleted.
|
deleted.
|
||||||
:const:`mongoengine.NULLIFY`
|
:const:`mongoengine.NULLIFY`
|
||||||
Any object's fields still referring to the object being deleted are removed
|
Any object's fields still referring to the object being deleted are set to None
|
||||||
(using MongoDB's "unset" operation), effectively nullifying the relationship.
|
(using MongoDB's "unset" operation), effectively nullifying the relationship.
|
||||||
:const:`mongoengine.CASCADE`
|
:const:`mongoengine.CASCADE`
|
||||||
Any object containing fields that are referring to the object being deleted
|
Any object containing fields that are referring to the object being deleted
|
||||||
@@ -555,7 +555,6 @@ There are a few top level defaults for all indexes that can be set::
|
|||||||
'index_background': True,
|
'index_background': True,
|
||||||
'index_cls': False,
|
'index_cls': False,
|
||||||
'auto_create_index': True,
|
'auto_create_index': True,
|
||||||
'index_drop_dups': True,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -574,11 +573,6 @@ There are a few top level defaults for all indexes that can be set::
|
|||||||
in systems where indexes are managed separately. Disabling this will improve
|
in systems where indexes are managed separately. Disabling this will improve
|
||||||
performance.
|
performance.
|
||||||
|
|
||||||
:attr:`index_drop_dups` (Optional)
|
|
||||||
Set the default value for if an index should drop duplicates
|
|
||||||
Since MongoDB 3.0 drop_dups is not supported anymore. Raises a Warning
|
|
||||||
and has no effect
|
|
||||||
|
|
||||||
|
|
||||||
Compound Indexes and Indexing sub documents
|
Compound Indexes and Indexing sub documents
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
@@ -744,7 +738,7 @@ Document inheritance
|
|||||||
|
|
||||||
To create a specialised type of a :class:`~mongoengine.Document` you have
|
To create a specialised type of a :class:`~mongoengine.Document` you have
|
||||||
defined, you may subclass it and add any extra fields or methods you may need.
|
defined, you may subclass it and add any extra fields or methods you may need.
|
||||||
As this is new class is not a direct subclass of
|
As this new class is not a direct subclass of
|
||||||
:class:`~mongoengine.Document`, it will not be stored in its own collection; it
|
:class:`~mongoengine.Document`, it will not be stored in its own collection; it
|
||||||
will use the same collection as its superclass uses. This allows for more
|
will use the same collection as its superclass uses. This allows for more
|
||||||
convenient and efficient retrieval of related documents -- all you need do is
|
convenient and efficient retrieval of related documents -- all you need do is
|
||||||
@@ -767,6 +761,27 @@ document.::
|
|||||||
Setting :attr:`allow_inheritance` to True should also be used in
|
Setting :attr:`allow_inheritance` to True should also be used in
|
||||||
:class:`~mongoengine.EmbeddedDocument` class in case you need to subclass it
|
:class:`~mongoengine.EmbeddedDocument` class in case you need to subclass it
|
||||||
|
|
||||||
|
When it comes to querying using :attr:`.objects()`, querying `Page.objects()` will query
|
||||||
|
both `Page` and `DatedPage` whereas querying `DatedPage` will only query the `DatedPage` documents.
|
||||||
|
Behind the scenes, MongoEngine deals with inheritance by adding a :attr:`_cls` attribute that contains
|
||||||
|
the class name in every documents. When a document is loaded, MongoEngine checks
|
||||||
|
it's :attr:`_cls` attribute and use that class to construct the instance.::
|
||||||
|
|
||||||
|
Page(title='a funky title').save()
|
||||||
|
DatedPage(title='another title', date=datetime.utcnow()).save()
|
||||||
|
|
||||||
|
print(Page.objects().count()) # 2
|
||||||
|
print(DatedPage.objects().count()) # 1
|
||||||
|
|
||||||
|
# print documents in their native form
|
||||||
|
# we remove 'id' to avoid polluting the output with unnecessary detail
|
||||||
|
qs = Page.objects.exclude('id').as_pymongo()
|
||||||
|
print(list(qs))
|
||||||
|
# [
|
||||||
|
# {'_cls': u 'Page', 'title': 'a funky title'},
|
||||||
|
# {'_cls': u 'Page.DatedPage', 'title': u 'another title', 'date': datetime.datetime(2019, 12, 13, 20, 16, 59, 993000)}
|
||||||
|
# ]
|
||||||
|
|
||||||
Working with existing data
|
Working with existing data
|
||||||
--------------------------
|
--------------------------
|
||||||
As MongoEngine no longer defaults to needing :attr:`_cls`, you can quickly and
|
As MongoEngine no longer defaults to needing :attr:`_cls`, you can quickly and
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ Writing
|
|||||||
GridFS support comes in the form of the :class:`~mongoengine.fields.FileField` field
|
GridFS support comes in the form of the :class:`~mongoengine.fields.FileField` field
|
||||||
object. This field acts as a file-like object and provides a couple of
|
object. This field acts as a file-like object and provides a couple of
|
||||||
different ways of inserting and retrieving data. Arbitrary metadata such as
|
different ways of inserting and retrieving data. Arbitrary metadata such as
|
||||||
content type can also be stored alongside the files. In the following example,
|
content type can also be stored alongside the files. The object returned when accessing a
|
||||||
a document is created to store details about animals, including a photo::
|
FileField is a proxy to `Pymongo's GridFS <https://api.mongodb.com/python/current/examples/gridfs.html#gridfs-example>`_
|
||||||
|
In the following example, a document is created to store details about animals, including a photo::
|
||||||
|
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
genus = StringField()
|
genus = StringField()
|
||||||
@@ -20,8 +21,8 @@ a document is created to store details about animals, including a photo::
|
|||||||
|
|
||||||
marmot = Animal(genus='Marmota', family='Sciuridae')
|
marmot = Animal(genus='Marmota', family='Sciuridae')
|
||||||
|
|
||||||
marmot_photo = open('marmot.jpg', 'rb')
|
with open('marmot.jpg', 'rb') as fd:
|
||||||
marmot.photo.put(marmot_photo, content_type = 'image/jpeg')
|
marmot.photo.put(fd, content_type = 'image/jpeg')
|
||||||
marmot.save()
|
marmot.save()
|
||||||
|
|
||||||
Retrieval
|
Retrieval
|
||||||
@@ -34,6 +35,20 @@ field. The file can also be retrieved just as easily::
|
|||||||
photo = marmot.photo.read()
|
photo = marmot.photo.read()
|
||||||
content_type = marmot.photo.content_type
|
content_type = marmot.photo.content_type
|
||||||
|
|
||||||
|
.. note:: If you need to read() the content of a file multiple times, you'll need to "rewind"
|
||||||
|
the file-like object using `seek`::
|
||||||
|
|
||||||
|
marmot = Animal.objects(genus='Marmota').first()
|
||||||
|
content1 = marmot.photo.read()
|
||||||
|
assert content1 != ""
|
||||||
|
|
||||||
|
content2 = marmot.photo.read() # will be empty
|
||||||
|
assert content2 == ""
|
||||||
|
|
||||||
|
marmot.photo.seek(0) # rewind the file by setting the current position of the cursor in the file to 0
|
||||||
|
content3 = marmot.photo.read()
|
||||||
|
assert content3 == content1
|
||||||
|
|
||||||
Streaming
|
Streaming
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ User Guide
|
|||||||
gridfs
|
gridfs
|
||||||
signals
|
signals
|
||||||
text-indexes
|
text-indexes
|
||||||
|
logging-monitoring
|
||||||
mongomock
|
mongomock
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ MongoEngine is available on PyPI, so you can use :program:`pip`:
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ pip install mongoengine
|
$ python -m pip install mongoengine
|
||||||
|
|
||||||
Alternatively, if you don't have setuptools installed, `download it from PyPi
|
Alternatively, if you don't have setuptools installed, `download it from PyPi
|
||||||
<http://pypi.python.org/pypi/mongoengine/>`_ and run
|
<http://pypi.python.org/pypi/mongoengine/>`_ and run
|
||||||
|
|||||||
80
docs/guide/logging-monitoring.rst
Normal file
80
docs/guide/logging-monitoring.rst
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
==================
|
||||||
|
Logging/Monitoring
|
||||||
|
==================
|
||||||
|
|
||||||
|
It is possible to use `pymongo.monitoring <https://api.mongodb.com/python/current/api/pymongo/monitoring.html>`_ to monitor
|
||||||
|
the driver events (e.g: queries, connections, etc). This can be handy if you want to monitor the queries issued by
|
||||||
|
MongoEngine to the driver.
|
||||||
|
|
||||||
|
To use `pymongo.monitoring` with MongoEngine, you need to make sure that you are registering the listeners
|
||||||
|
**before** establishing the database connection (i.e calling `connect`):
|
||||||
|
|
||||||
|
The following snippet provides a basic logging of all command events:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from pymongo import monitoring
|
||||||
|
from mongoengine import *
|
||||||
|
|
||||||
|
log = logging.getLogger()
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandLogger(monitoring.CommandListener):
|
||||||
|
|
||||||
|
def started(self, event):
|
||||||
|
log.debug("Command {0.command_name} with request id "
|
||||||
|
"{0.request_id} started on server "
|
||||||
|
"{0.connection_id}".format(event))
|
||||||
|
|
||||||
|
def succeeded(self, event):
|
||||||
|
log.debug("Command {0.command_name} with request id "
|
||||||
|
"{0.request_id} on server {0.connection_id} "
|
||||||
|
"succeeded in {0.duration_micros} "
|
||||||
|
"microseconds".format(event))
|
||||||
|
|
||||||
|
def failed(self, event):
|
||||||
|
log.debug("Command {0.command_name} with request id "
|
||||||
|
"{0.request_id} on server {0.connection_id} "
|
||||||
|
"failed in {0.duration_micros} "
|
||||||
|
"microseconds".format(event))
|
||||||
|
|
||||||
|
monitoring.register(CommandLogger())
|
||||||
|
|
||||||
|
|
||||||
|
class Jedi(Document):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
|
||||||
|
connect()
|
||||||
|
|
||||||
|
|
||||||
|
log.info('GO!')
|
||||||
|
|
||||||
|
log.info('Saving an item through MongoEngine...')
|
||||||
|
Jedi(name='Obi-Wan Kenobii').save()
|
||||||
|
|
||||||
|
log.info('Querying through MongoEngine...')
|
||||||
|
obiwan = Jedi.objects.first()
|
||||||
|
|
||||||
|
log.info('Updating through MongoEngine...')
|
||||||
|
obiwan.name = 'Obi-Wan Kenobi'
|
||||||
|
obiwan.save()
|
||||||
|
|
||||||
|
|
||||||
|
Executing this prints the following output::
|
||||||
|
|
||||||
|
INFO:root:GO!
|
||||||
|
INFO:root:Saving an item through MongoEngine...
|
||||||
|
DEBUG:root:Command insert with request id 1681692777 started on server ('localhost', 27017)
|
||||||
|
DEBUG:root:Command insert with request id 1681692777 on server ('localhost', 27017) succeeded in 562 microseconds
|
||||||
|
INFO:root:Querying through MongoEngine...
|
||||||
|
DEBUG:root:Command find with request id 1714636915 started on server ('localhost', 27017)
|
||||||
|
DEBUG:root:Command find with request id 1714636915 on server ('localhost', 27017) succeeded in 341 microseconds
|
||||||
|
INFO:root:Updating through MongoEngine...
|
||||||
|
DEBUG:root:Command update with request id 1957747793 started on server ('localhost', 27017)
|
||||||
|
DEBUG:root:Command update with request id 1957747793 on server ('localhost', 27017) succeeded in 455 microseconds
|
||||||
|
|
||||||
|
More details can of course be obtained by checking the `event` argument from the `CommandListener`.
|
||||||
@@ -21,7 +21,7 @@ or with an alias:
|
|||||||
conn = get_connection('testdb')
|
conn = get_connection('testdb')
|
||||||
|
|
||||||
Example of test file:
|
Example of test file:
|
||||||
--------
|
---------------------
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
@@ -45,4 +45,4 @@ Example of test file:
|
|||||||
pers.save()
|
pers.save()
|
||||||
|
|
||||||
fresh_pers = Person.objects().first()
|
fresh_pers = Person.objects().first()
|
||||||
self.assertEqual(fresh_pers.name, 'John')
|
assert fresh_pers.name == 'John'
|
||||||
|
|||||||
@@ -222,6 +222,18 @@ keyword argument::
|
|||||||
|
|
||||||
.. versionadded:: 0.4
|
.. versionadded:: 0.4
|
||||||
|
|
||||||
|
Sorting/Ordering results
|
||||||
|
========================
|
||||||
|
It is possible to order the results by 1 or more keys using :meth:`~mongoengine.queryset.QuerySet.order_by`.
|
||||||
|
The order may be specified by prepending each of the keys by "+" or "-". Ascending order is assumed if there's no prefix.::
|
||||||
|
|
||||||
|
# Order by ascending date
|
||||||
|
blogs = BlogPost.objects().order_by('date') # equivalent to .order_by('+date')
|
||||||
|
|
||||||
|
# Order by ascending date first, then descending title
|
||||||
|
blogs = BlogPost.objects().order_by('+date', '-title')
|
||||||
|
|
||||||
|
|
||||||
Limiting and skipping results
|
Limiting and skipping results
|
||||||
=============================
|
=============================
|
||||||
Just as with traditional ORMs, you may limit the number of results returned or
|
Just as with traditional ORMs, you may limit the number of results returned or
|
||||||
@@ -349,9 +361,9 @@ Just as with limiting and skipping results, there is a method on a
|
|||||||
You could technically use ``len(User.objects)`` to get the same result, but it
|
You could technically use ``len(User.objects)`` to get the same result, but it
|
||||||
would be significantly slower than :meth:`~mongoengine.queryset.QuerySet.count`.
|
would be significantly slower than :meth:`~mongoengine.queryset.QuerySet.count`.
|
||||||
When you execute a server-side count query, you let MongoDB do the heavy
|
When you execute a server-side count query, you let MongoDB do the heavy
|
||||||
lifting and you receive a single integer over the wire. Meanwhile, len()
|
lifting and you receive a single integer over the wire. Meanwhile, ``len()``
|
||||||
retrieves all the results, places them in a local cache, and finally counts
|
retrieves all the results, places them in a local cache, and finally counts
|
||||||
them. If we compare the performance of the two operations, len() is much slower
|
them. If we compare the performance of the two operations, ``len()`` is much slower
|
||||||
than :meth:`~mongoengine.queryset.QuerySet.count`.
|
than :meth:`~mongoengine.queryset.QuerySet.count`.
|
||||||
|
|
||||||
Further aggregation
|
Further aggregation
|
||||||
@@ -386,6 +398,25 @@ would be generating "tag-clouds"::
|
|||||||
top_tags = sorted(tag_freqs.items(), key=itemgetter(1), reverse=True)[:10]
|
top_tags = sorted(tag_freqs.items(), key=itemgetter(1), reverse=True)[:10]
|
||||||
|
|
||||||
|
|
||||||
|
MongoDB aggregation API
|
||||||
|
-----------------------
|
||||||
|
If you need to run aggregation pipelines, MongoEngine provides an entry point to `Pymongo's aggregation framework <https://api.mongodb.com/python/current/examples/aggregation.html#aggregation-framework>`_
|
||||||
|
through :meth:`~mongoengine.queryset.QuerySet.aggregate`. Check out Pymongo's documentation for the syntax and pipeline.
|
||||||
|
An example of its use would be::
|
||||||
|
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
Person(name='John').save()
|
||||||
|
Person(name='Bob').save()
|
||||||
|
|
||||||
|
pipeline = [
|
||||||
|
{"$sort" : {"name" : -1}},
|
||||||
|
{"$project": {"_id": 0, "name": {"$toUpper": "$name"}}}
|
||||||
|
]
|
||||||
|
data = Person.objects().aggregate(pipeline)
|
||||||
|
assert data == [{'name': 'BOB'}, {'name': 'JOHN'}]
|
||||||
|
|
||||||
Query efficiency and performance
|
Query efficiency and performance
|
||||||
================================
|
================================
|
||||||
|
|
||||||
@@ -566,7 +597,8 @@ cannot use the `$` syntax in keyword arguments it has been mapped to `S`::
|
|||||||
['database', 'mongodb']
|
['database', 'mongodb']
|
||||||
|
|
||||||
From MongoDB version 2.6, push operator supports $position value which allows
|
From MongoDB version 2.6, push operator supports $position value which allows
|
||||||
to push values with index.
|
to push values with index::
|
||||||
|
|
||||||
>>> post = BlogPost(title="Test", tags=["mongo"])
|
>>> post = BlogPost(title="Test", tags=["mongo"])
|
||||||
>>> post.save()
|
>>> post.save()
|
||||||
>>> post.update(push__tags__0=["database", "code"])
|
>>> post.update(push__tags__0=["database", "code"])
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ MongoDB. To install it, simply run
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ pip install -U mongoengine
|
$ python -m pip install -U mongoengine
|
||||||
|
|
||||||
:doc:`tutorial`
|
:doc:`tutorial`
|
||||||
A quick tutorial building a tumblelog to get you up and running with
|
A quick tutorial building a tumblelog to get you up and running with
|
||||||
@@ -23,9 +23,18 @@ MongoDB. To install it, simply run
|
|||||||
:doc:`upgrade`
|
:doc:`upgrade`
|
||||||
How to upgrade MongoEngine.
|
How to upgrade MongoEngine.
|
||||||
|
|
||||||
|
:doc:`faq`
|
||||||
|
Frequently Asked Questions
|
||||||
|
|
||||||
:doc:`django`
|
:doc:`django`
|
||||||
Using MongoEngine and Django
|
Using MongoEngine and Django
|
||||||
|
|
||||||
|
MongoDB and driver support
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
MongoEngine is based on the PyMongo driver and tested against multiple versions of MongoDB.
|
||||||
|
For further details, please refer to the `readme <https://github.com/MongoEngine/mongoengine#mongoengine>`_.
|
||||||
|
|
||||||
Community
|
Community
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@@ -73,6 +82,7 @@ formats for offline reading.
|
|||||||
apireference
|
apireference
|
||||||
changelog
|
changelog
|
||||||
upgrade
|
upgrade
|
||||||
|
faq
|
||||||
django
|
django
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
@@ -81,4 +91,3 @@ Indices and tables
|
|||||||
* :ref:`genindex`
|
* :ref:`genindex`
|
||||||
* :ref:`modindex`
|
* :ref:`modindex`
|
||||||
* :ref:`search`
|
* :ref:`search`
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ location --- running it locally will be easier, but if that is not an option
|
|||||||
then it may be run on a remote server. If you haven't installed MongoEngine,
|
then it may be run on a remote server. If you haven't installed MongoEngine,
|
||||||
simply use pip to install it like so::
|
simply use pip to install it like so::
|
||||||
|
|
||||||
$ pip install mongoengine
|
$ python -m pip install mongoengine
|
||||||
|
|
||||||
Before we can start using MongoEngine, we need to tell it how to connect to our
|
Before we can start using MongoEngine, we need to tell it how to connect to our
|
||||||
instance of :program:`mongod`. For this we use the :func:`~mongoengine.connect`
|
instance of :program:`mongod`. For this we use the :func:`~mongoengine.connect`
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ rename its occurrences.
|
|||||||
This release includes a major rehaul of MongoEngine's code quality and
|
This release includes a major rehaul of MongoEngine's code quality and
|
||||||
introduces a few breaking changes. It also touches many different parts of
|
introduces a few breaking changes. It also touches many different parts of
|
||||||
the package and although all the changes have been tested and scrutinized,
|
the package and although all the changes have been tested and scrutinized,
|
||||||
you're encouraged to thorougly test the upgrade.
|
you're encouraged to thoroughly test the upgrade.
|
||||||
|
|
||||||
First breaking change involves renaming `ConnectionError` to `MongoEngineConnectionError`.
|
First breaking change involves renaming `ConnectionError` to `MongoEngineConnectionError`.
|
||||||
If you import or catch this exception, you'll need to rename it in your code.
|
If you import or catch this exception, you'll need to rename it in your code.
|
||||||
@@ -85,10 +85,10 @@ by default from now on.
|
|||||||
|
|
||||||
The 0.8.7 package on pypi was corrupted. If upgrading from 0.8.7 to 0.9.0 please follow: ::
|
The 0.8.7 package on pypi was corrupted. If upgrading from 0.8.7 to 0.9.0 please follow: ::
|
||||||
|
|
||||||
pip uninstall pymongo
|
python -m pip uninstall pymongo
|
||||||
pip uninstall mongoengine
|
python -m pip uninstall mongoengine
|
||||||
pip install pymongo==2.8
|
python -m pip install pymongo==2.8
|
||||||
pip install mongoengine
|
python -m pip install mongoengine
|
||||||
|
|
||||||
0.8.7
|
0.8.7
|
||||||
*****
|
*****
|
||||||
@@ -153,7 +153,7 @@ inherited classes like so: ::
|
|||||||
|
|
||||||
# 4. Remove indexes
|
# 4. Remove indexes
|
||||||
info = collection.index_information()
|
info = collection.index_information()
|
||||||
indexes_to_drop = [key for key, value in info.iteritems()
|
indexes_to_drop = [key for key, value in info.items()
|
||||||
if '_types' in dict(value['key'])]
|
if '_types' in dict(value['key'])]
|
||||||
for index in indexes_to_drop:
|
for index in indexes_to_drop:
|
||||||
collection.drop_index(index)
|
collection.drop_index(index)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
VERSION = (0, 18, 2)
|
VERSION = (0, 20, 0)
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
from bson import DBRef
|
from bson import DBRef
|
||||||
import six
|
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine.common import _import_class
|
from mongoengine.common import _import_class
|
||||||
from mongoengine.errors import DoesNotExist, MultipleObjectsReturned
|
from mongoengine.errors import DoesNotExist, MultipleObjectsReturned
|
||||||
@@ -53,7 +51,7 @@ class BaseDict(dict):
|
|||||||
if isinstance(instance, BaseDocument):
|
if isinstance(instance, BaseDocument):
|
||||||
self._instance = weakref.proxy(instance)
|
self._instance = weakref.proxy(instance)
|
||||||
self._name = name
|
self._name = name
|
||||||
super(BaseDict, self).__init__(dict_items)
|
super().__init__(dict_items)
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
# get does not use __getitem__ by default so we must override it as well
|
# get does not use __getitem__ by default so we must override it as well
|
||||||
@@ -63,18 +61,18 @@ class BaseDict(dict):
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
value = super(BaseDict, self).__getitem__(key)
|
value = super().__getitem__(key)
|
||||||
|
|
||||||
EmbeddedDocument = _import_class("EmbeddedDocument")
|
EmbeddedDocument = _import_class("EmbeddedDocument")
|
||||||
if isinstance(value, EmbeddedDocument) and value._instance is None:
|
if isinstance(value, EmbeddedDocument) and value._instance is None:
|
||||||
value._instance = self._instance
|
value._instance = self._instance
|
||||||
elif isinstance(value, dict) and not isinstance(value, BaseDict):
|
elif isinstance(value, dict) and not isinstance(value, BaseDict):
|
||||||
value = BaseDict(value, None, "%s.%s" % (self._name, key))
|
value = BaseDict(value, None, "{}.{}".format(self._name, key))
|
||||||
super(BaseDict, self).__setitem__(key, value)
|
super().__setitem__(key, value)
|
||||||
value._instance = self._instance
|
value._instance = self._instance
|
||||||
elif isinstance(value, list) and not isinstance(value, BaseList):
|
elif isinstance(value, list) and not isinstance(value, BaseList):
|
||||||
value = BaseList(value, None, "%s.%s" % (self._name, key))
|
value = BaseList(value, None, "{}.{}".format(self._name, key))
|
||||||
super(BaseDict, self).__setitem__(key, value)
|
super().__setitem__(key, value)
|
||||||
value._instance = self._instance
|
value._instance = self._instance
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@@ -99,7 +97,7 @@ class BaseDict(dict):
|
|||||||
def _mark_as_changed(self, key=None):
|
def _mark_as_changed(self, key=None):
|
||||||
if hasattr(self._instance, "_mark_as_changed"):
|
if hasattr(self._instance, "_mark_as_changed"):
|
||||||
if key:
|
if key:
|
||||||
self._instance._mark_as_changed("%s.%s" % (self._name, key))
|
self._instance._mark_as_changed("{}.{}".format(self._name, key))
|
||||||
else:
|
else:
|
||||||
self._instance._mark_as_changed(self._name)
|
self._instance._mark_as_changed(self._name)
|
||||||
|
|
||||||
@@ -117,10 +115,13 @@ class BaseList(list):
|
|||||||
if isinstance(instance, BaseDocument):
|
if isinstance(instance, BaseDocument):
|
||||||
self._instance = weakref.proxy(instance)
|
self._instance = weakref.proxy(instance)
|
||||||
self._name = name
|
self._name = name
|
||||||
super(BaseList, self).__init__(list_items)
|
super().__init__(list_items)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
value = super(BaseList, self).__getitem__(key)
|
# change index to positive value because MongoDB does not support negative one
|
||||||
|
if isinstance(key, int) and key < 0:
|
||||||
|
key = len(self) + key
|
||||||
|
value = super().__getitem__(key)
|
||||||
|
|
||||||
if isinstance(key, slice):
|
if isinstance(key, slice):
|
||||||
# When receiving a slice operator, we don't convert the structure and bind
|
# When receiving a slice operator, we don't convert the structure and bind
|
||||||
@@ -132,19 +133,18 @@ class BaseList(list):
|
|||||||
value._instance = self._instance
|
value._instance = self._instance
|
||||||
elif isinstance(value, dict) and not isinstance(value, BaseDict):
|
elif isinstance(value, dict) and not isinstance(value, BaseDict):
|
||||||
# Replace dict by BaseDict
|
# Replace dict by BaseDict
|
||||||
value = BaseDict(value, None, "%s.%s" % (self._name, key))
|
value = BaseDict(value, None, "{}.{}".format(self._name, key))
|
||||||
super(BaseList, self).__setitem__(key, value)
|
super().__setitem__(key, value)
|
||||||
value._instance = self._instance
|
value._instance = self._instance
|
||||||
elif isinstance(value, list) and not isinstance(value, BaseList):
|
elif isinstance(value, list) and not isinstance(value, BaseList):
|
||||||
# Replace list by BaseList
|
# Replace list by BaseList
|
||||||
value = BaseList(value, None, "%s.%s" % (self._name, key))
|
value = BaseList(value, None, "{}.{}".format(self._name, key))
|
||||||
super(BaseList, self).__setitem__(key, value)
|
super().__setitem__(key, value)
|
||||||
value._instance = self._instance
|
value._instance = self._instance
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for v in super(BaseList, self).__iter__():
|
yield from super().__iter__()
|
||||||
yield v
|
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
self.instance = None
|
self.instance = None
|
||||||
@@ -162,7 +162,7 @@ class BaseList(list):
|
|||||||
# instead, we simply marks the whole list as changed
|
# instead, we simply marks the whole list as changed
|
||||||
changed_key = None
|
changed_key = None
|
||||||
|
|
||||||
result = super(BaseList, self).__setitem__(key, value)
|
result = super().__setitem__(key, value)
|
||||||
self._mark_as_changed(changed_key)
|
self._mark_as_changed(changed_key)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -177,30 +177,19 @@ class BaseList(list):
|
|||||||
__iadd__ = mark_as_changed_wrapper(list.__iadd__)
|
__iadd__ = mark_as_changed_wrapper(list.__iadd__)
|
||||||
__imul__ = mark_as_changed_wrapper(list.__imul__)
|
__imul__ = mark_as_changed_wrapper(list.__imul__)
|
||||||
|
|
||||||
if six.PY2:
|
|
||||||
# Under py3 __setslice__, __delslice__ and __getslice__
|
|
||||||
# are replaced by __setitem__, __delitem__ and __getitem__ with a slice as parameter
|
|
||||||
# so we mimic this under python 2
|
|
||||||
def __setslice__(self, i, j, sequence):
|
|
||||||
return self.__setitem__(slice(i, j), sequence)
|
|
||||||
|
|
||||||
def __delslice__(self, i, j):
|
|
||||||
return self.__delitem__(slice(i, j))
|
|
||||||
|
|
||||||
def __getslice__(self, i, j):
|
|
||||||
return self.__getitem__(slice(i, j))
|
|
||||||
|
|
||||||
def _mark_as_changed(self, key=None):
|
def _mark_as_changed(self, key=None):
|
||||||
if hasattr(self._instance, "_mark_as_changed"):
|
if hasattr(self._instance, "_mark_as_changed"):
|
||||||
if key:
|
if key:
|
||||||
self._instance._mark_as_changed("%s.%s" % (self._name, key % len(self)))
|
self._instance._mark_as_changed(
|
||||||
|
"{}.{}".format(self._name, key % len(self))
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self._instance._mark_as_changed(self._name)
|
self._instance._mark_as_changed(self._name)
|
||||||
|
|
||||||
|
|
||||||
class EmbeddedDocumentList(BaseList):
|
class EmbeddedDocumentList(BaseList):
|
||||||
def __init__(self, list_items, instance, name):
|
def __init__(self, list_items, instance, name):
|
||||||
super(EmbeddedDocumentList, self).__init__(list_items, instance, name)
|
super().__init__(list_items, instance, name)
|
||||||
self._instance = instance
|
self._instance = instance
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -210,7 +199,7 @@ class EmbeddedDocumentList(BaseList):
|
|||||||
"""
|
"""
|
||||||
for key, expected_value in kwargs.items():
|
for key, expected_value in kwargs.items():
|
||||||
doc_val = getattr(embedded_doc, key)
|
doc_val = getattr(embedded_doc, key)
|
||||||
if doc_val != expected_value and six.text_type(doc_val) != expected_value:
|
if doc_val != expected_value and str(doc_val) != expected_value:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -300,11 +289,11 @@ class EmbeddedDocumentList(BaseList):
|
|||||||
|
|
||||||
def create(self, **values):
|
def create(self, **values):
|
||||||
"""
|
"""
|
||||||
Creates a new embedded document and saves it to the database.
|
Creates a new instance of the EmbeddedDocument and appends it to this EmbeddedDocumentList.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The embedded document changes are not automatically saved
|
the instance of the EmbeddedDocument is not automatically saved to the database.
|
||||||
to the database after calling this method.
|
You still need to call .save() on the parent Document.
|
||||||
|
|
||||||
:param values: A dictionary of values for the embedded document.
|
:param values: A dictionary of values for the embedded document.
|
||||||
:return: The new embedded document instance.
|
:return: The new embedded document instance.
|
||||||
@@ -365,13 +354,13 @@ class EmbeddedDocumentList(BaseList):
|
|||||||
return len(values)
|
return len(values)
|
||||||
|
|
||||||
|
|
||||||
class StrictDict(object):
|
class StrictDict:
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
_special_fields = {"get", "pop", "iteritems", "items", "keys", "create"}
|
_special_fields = {"get", "pop", "iteritems", "items", "keys", "create"}
|
||||||
_classes = {}
|
_classes = {}
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
for k, v in iteritems(kwargs):
|
for k, v in kwargs.items():
|
||||||
setattr(self, k, v)
|
setattr(self, k, v)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
@@ -419,13 +408,13 @@ class StrictDict(object):
|
|||||||
return (key for key in self.__slots__ if hasattr(self, key))
|
return (key for key in self.__slots__ if hasattr(self, key))
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(list(iteritems(self)))
|
return len(list(self.items()))
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.items() == other.items()
|
return list(self.items()) == list(other.items())
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return self.items() != other.items()
|
return not (self == other)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, allowed_keys):
|
def create(cls, allowed_keys):
|
||||||
@@ -440,7 +429,7 @@ class StrictDict(object):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "{%s}" % ", ".join(
|
return "{%s}" % ", ".join(
|
||||||
'"{0!s}": {1!r}'.format(k, v) for k, v in self.items()
|
'"{!s}": {!r}'.format(k, v) for k, v in self.items()
|
||||||
)
|
)
|
||||||
|
|
||||||
cls._classes[allowed_keys] = SpecificStrictDict
|
cls._classes[allowed_keys] = SpecificStrictDict
|
||||||
@@ -465,9 +454,7 @@ class LazyReference(DBRef):
|
|||||||
self.document_type = document_type
|
self.document_type = document_type
|
||||||
self._cached_doc = cached_doc
|
self._cached_doc = cached_doc
|
||||||
self.passthrough = passthrough
|
self.passthrough = passthrough
|
||||||
super(LazyReference, self).__init__(
|
super().__init__(self.document_type._get_collection_name(), pk)
|
||||||
self.document_type._get_collection_name(), pk
|
|
||||||
)
|
|
||||||
|
|
||||||
def __getitem__(self, name):
|
def __getitem__(self, name):
|
||||||
if not self.passthrough:
|
if not self.passthrough:
|
||||||
@@ -485,4 +472,4 @@ class LazyReference(DBRef):
|
|||||||
raise AttributeError()
|
raise AttributeError()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<LazyReference(%s, %r)>" % (self.document_type, self.pk)
|
return "<LazyReference({}, {!r})>".format(self.document_type, self.pk)
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import copy
|
import copy
|
||||||
|
|
||||||
import numbers
|
import numbers
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from bson import DBRef, ObjectId, SON, json_util
|
from bson import DBRef, ObjectId, SON, json_util
|
||||||
import pymongo
|
import pymongo
|
||||||
import six
|
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine import signals
|
from mongoengine import signals
|
||||||
from mongoengine.base.common import get_document
|
from mongoengine.base.common import get_document
|
||||||
@@ -25,14 +24,13 @@ from mongoengine.errors import (
|
|||||||
OperationError,
|
OperationError,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
)
|
)
|
||||||
from mongoengine.python_support import Hashable
|
|
||||||
|
|
||||||
__all__ = ("BaseDocument", "NON_FIELD_ERRORS")
|
__all__ = ("BaseDocument", "NON_FIELD_ERRORS")
|
||||||
|
|
||||||
NON_FIELD_ERRORS = "__all__"
|
NON_FIELD_ERRORS = "__all__"
|
||||||
|
|
||||||
|
|
||||||
class BaseDocument(object):
|
class BaseDocument:
|
||||||
# TODO simplify how `_changed_fields` is used.
|
# TODO simplify how `_changed_fields` is used.
|
||||||
# Currently, handling of `_changed_fields` seems unnecessarily convoluted:
|
# Currently, handling of `_changed_fields` seems unnecessarily convoluted:
|
||||||
# 1. `BaseDocument` defines `_changed_fields` in its `__slots__`, yet it's
|
# 1. `BaseDocument` defines `_changed_fields` in its `__slots__`, yet it's
|
||||||
@@ -62,13 +60,13 @@ class BaseDocument(object):
|
|||||||
"""
|
"""
|
||||||
Initialise a document or an embedded document.
|
Initialise a document or an embedded document.
|
||||||
|
|
||||||
:param dict values: A dictionary of keys and values for the document.
|
:param values: A dictionary of keys and values for the document.
|
||||||
It may contain additional reserved keywords, e.g. "__auto_convert".
|
It may contain additional reserved keywords, e.g. "__auto_convert".
|
||||||
:param bool __auto_convert: If True, supplied values will be converted
|
:param __auto_convert: If True, supplied values will be converted
|
||||||
to Python-type values via each field's `to_python` method.
|
to Python-type values via each field's `to_python` method.
|
||||||
:param set __only_fields: A set of fields that have been loaded for
|
:param __only_fields: A set of fields that have been loaded for
|
||||||
this document. Empty if all fields have been loaded.
|
this document. Empty if all fields have been loaded.
|
||||||
:param bool _created: Indicates whether this is a brand new document
|
:param _created: Indicates whether this is a brand new document
|
||||||
or whether it's already been persisted before. Defaults to true.
|
or whether it's already been persisted before. Defaults to true.
|
||||||
"""
|
"""
|
||||||
self._initialised = False
|
self._initialised = False
|
||||||
@@ -92,10 +90,10 @@ class BaseDocument(object):
|
|||||||
# if so raise an Exception.
|
# if so raise an Exception.
|
||||||
if not self._dynamic and (self._meta.get("strict", True) or _created):
|
if not self._dynamic and (self._meta.get("strict", True) or _created):
|
||||||
_undefined_fields = set(values.keys()) - set(
|
_undefined_fields = set(values.keys()) - set(
|
||||||
self._fields.keys() + ["id", "pk", "_cls", "_text_score"]
|
list(self._fields.keys()) + ["id", "pk", "_cls", "_text_score"]
|
||||||
)
|
)
|
||||||
if _undefined_fields:
|
if _undefined_fields:
|
||||||
msg = ('The fields "{0}" do not exist on the document "{1}"').format(
|
msg = ('The fields "{}" do not exist on the document "{}"').format(
|
||||||
_undefined_fields, self._class_name
|
_undefined_fields, self._class_name
|
||||||
)
|
)
|
||||||
raise FieldDoesNotExist(msg)
|
raise FieldDoesNotExist(msg)
|
||||||
@@ -110,7 +108,7 @@ class BaseDocument(object):
|
|||||||
# Assign default values to the instance.
|
# Assign default values to the instance.
|
||||||
# We set default values only for fields loaded from DB. See
|
# We set default values only for fields loaded from DB. See
|
||||||
# https://github.com/mongoengine/mongoengine/issues/399 for more info.
|
# https://github.com/mongoengine/mongoengine/issues/399 for more info.
|
||||||
for key, field in iteritems(self._fields):
|
for key, field in self._fields.items():
|
||||||
if self._db_field_map.get(key, key) in __only_fields:
|
if self._db_field_map.get(key, key) in __only_fields:
|
||||||
continue
|
continue
|
||||||
value = getattr(self, key, None)
|
value = getattr(self, key, None)
|
||||||
@@ -122,14 +120,14 @@ class BaseDocument(object):
|
|||||||
# Set passed values after initialisation
|
# Set passed values after initialisation
|
||||||
if self._dynamic:
|
if self._dynamic:
|
||||||
dynamic_data = {}
|
dynamic_data = {}
|
||||||
for key, value in iteritems(values):
|
for key, value in values.items():
|
||||||
if key in self._fields or key == "_id":
|
if key in self._fields or key == "_id":
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
else:
|
else:
|
||||||
dynamic_data[key] = value
|
dynamic_data[key] = value
|
||||||
else:
|
else:
|
||||||
FileField = _import_class("FileField")
|
FileField = _import_class("FileField")
|
||||||
for key, value in iteritems(values):
|
for key, value in values.items():
|
||||||
key = self._reverse_db_field_map.get(key, key)
|
key = self._reverse_db_field_map.get(key, key)
|
||||||
if key in self._fields or key in ("id", "pk", "_cls"):
|
if key in self._fields or key in ("id", "pk", "_cls"):
|
||||||
if __auto_convert and value is not None:
|
if __auto_convert and value is not None:
|
||||||
@@ -145,7 +143,7 @@ class BaseDocument(object):
|
|||||||
|
|
||||||
if self._dynamic:
|
if self._dynamic:
|
||||||
self._dynamic_lock = False
|
self._dynamic_lock = False
|
||||||
for key, value in iteritems(dynamic_data):
|
for key, value in dynamic_data.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
# Flag initialised
|
# Flag initialised
|
||||||
@@ -163,7 +161,7 @@ class BaseDocument(object):
|
|||||||
default = default()
|
default = default()
|
||||||
setattr(self, field_name, default)
|
setattr(self, field_name, default)
|
||||||
else:
|
else:
|
||||||
super(BaseDocument, self).__delattr__(*args, **kwargs)
|
super().__delattr__(*args, **kwargs)
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
# Handle dynamic data only if an initialised dynamic document
|
# Handle dynamic data only if an initialised dynamic document
|
||||||
@@ -210,9 +208,9 @@ class BaseDocument(object):
|
|||||||
and self__created
|
and self__created
|
||||||
and name == self._meta.get("id_field")
|
and name == self._meta.get("id_field")
|
||||||
):
|
):
|
||||||
super(BaseDocument, self).__setattr__("_created", False)
|
super().__setattr__("_created", False)
|
||||||
|
|
||||||
super(BaseDocument, self).__setattr__(name, value)
|
super().__setattr__(name, value)
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
data = {}
|
data = {}
|
||||||
@@ -288,16 +286,13 @@ class BaseDocument(object):
|
|||||||
except (UnicodeEncodeError, UnicodeDecodeError):
|
except (UnicodeEncodeError, UnicodeDecodeError):
|
||||||
u = "[Bad Unicode data]"
|
u = "[Bad Unicode data]"
|
||||||
repr_type = str if u is None else type(u)
|
repr_type = str if u is None else type(u)
|
||||||
return repr_type("<%s: %s>" % (self.__class__.__name__, u))
|
return repr_type("<{}: {}>".format(self.__class__.__name__, u))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# TODO this could be simpler?
|
# TODO this could be simpler?
|
||||||
if hasattr(self, "__unicode__"):
|
if hasattr(self, "__unicode__"):
|
||||||
if six.PY3:
|
return self.__unicode__()
|
||||||
return self.__unicode__()
|
return "%s object" % self.__class__.__name__
|
||||||
else:
|
|
||||||
return six.text_type(self).encode("utf-8")
|
|
||||||
return six.text_type("%s object" % self.__class__.__name__)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if (
|
if (
|
||||||
@@ -446,7 +441,7 @@ class BaseDocument(object):
|
|||||||
pk = self.pk
|
pk = self.pk
|
||||||
elif self._instance and hasattr(self._instance, "pk"):
|
elif self._instance and hasattr(self._instance, "pk"):
|
||||||
pk = self._instance.pk
|
pk = self._instance.pk
|
||||||
message = "ValidationError (%s:%s) " % (self._class_name, pk)
|
message = "ValidationError ({}:{}) ".format(self._class_name, pk)
|
||||||
raise ValidationError(message, errors=errors)
|
raise ValidationError(message, errors=errors)
|
||||||
|
|
||||||
def to_json(self, *args, **kwargs):
|
def to_json(self, *args, **kwargs):
|
||||||
@@ -519,7 +514,7 @@ class BaseDocument(object):
|
|||||||
if "." in key:
|
if "." in key:
|
||||||
key, rest = key.split(".", 1)
|
key, rest = key.split(".", 1)
|
||||||
key = self._db_field_map.get(key, key)
|
key = self._db_field_map.get(key, key)
|
||||||
key = "%s.%s" % (key, rest)
|
key = "{}.{}".format(key, rest)
|
||||||
else:
|
else:
|
||||||
key = self._db_field_map.get(key, key)
|
key = self._db_field_map.get(key, key)
|
||||||
|
|
||||||
@@ -578,10 +573,10 @@ class BaseDocument(object):
|
|||||||
if not hasattr(data, "items"):
|
if not hasattr(data, "items"):
|
||||||
iterator = enumerate(data)
|
iterator = enumerate(data)
|
||||||
else:
|
else:
|
||||||
iterator = iteritems(data)
|
iterator = data.items()
|
||||||
|
|
||||||
for index_or_key, value in iterator:
|
for index_or_key, value in iterator:
|
||||||
item_key = "%s%s." % (base_key, index_or_key)
|
item_key = "{}{}.".format(base_key, index_or_key)
|
||||||
# don't check anything lower if this key is already marked
|
# don't check anything lower if this key is already marked
|
||||||
# as changed.
|
# as changed.
|
||||||
if item_key[:-1] in changed_fields:
|
if item_key[:-1] in changed_fields:
|
||||||
@@ -589,7 +584,7 @@ class BaseDocument(object):
|
|||||||
|
|
||||||
if hasattr(value, "_get_changed_fields"):
|
if hasattr(value, "_get_changed_fields"):
|
||||||
changed = value._get_changed_fields()
|
changed = value._get_changed_fields()
|
||||||
changed_fields += ["%s%s" % (item_key, k) for k in changed if k]
|
changed_fields += ["{}{}".format(item_key, k) for k in changed if k]
|
||||||
elif isinstance(value, (list, tuple, dict)):
|
elif isinstance(value, (list, tuple, dict)):
|
||||||
self._nestable_types_changed_fields(changed_fields, item_key, value)
|
self._nestable_types_changed_fields(changed_fields, item_key, value)
|
||||||
|
|
||||||
@@ -620,7 +615,7 @@ class BaseDocument(object):
|
|||||||
if isinstance(data, EmbeddedDocument):
|
if isinstance(data, EmbeddedDocument):
|
||||||
# Find all embedded fields that have been changed
|
# Find all embedded fields that have been changed
|
||||||
changed = data._get_changed_fields()
|
changed = data._get_changed_fields()
|
||||||
changed_fields += ["%s%s" % (key, k) for k in changed if k]
|
changed_fields += ["{}{}".format(key, k) for k in changed if k]
|
||||||
elif isinstance(data, (list, tuple, dict)):
|
elif isinstance(data, (list, tuple, dict)):
|
||||||
if hasattr(field, "field") and isinstance(
|
if hasattr(field, "field") and isinstance(
|
||||||
field.field, (ReferenceField, GenericReferenceField)
|
field.field, (ReferenceField, GenericReferenceField)
|
||||||
@@ -670,7 +665,7 @@ class BaseDocument(object):
|
|||||||
del set_data["_id"]
|
del set_data["_id"]
|
||||||
|
|
||||||
# Determine if any changed items were actually unset.
|
# Determine if any changed items were actually unset.
|
||||||
for path, value in set_data.items():
|
for path, value in list(set_data.items()):
|
||||||
if value or isinstance(
|
if value or isinstance(
|
||||||
value, (numbers.Number, bool)
|
value, (numbers.Number, bool)
|
||||||
): # Account for 0 and True that are truthy
|
): # Account for 0 and True that are truthy
|
||||||
@@ -732,7 +727,10 @@ class BaseDocument(object):
|
|||||||
only_fields = []
|
only_fields = []
|
||||||
|
|
||||||
if son and not isinstance(son, dict):
|
if son and not isinstance(son, dict):
|
||||||
raise ValueError("The source SON object needs to be of type 'dict'")
|
raise ValueError(
|
||||||
|
"The source SON object needs to be of type 'dict' but a '%s' was found"
|
||||||
|
% type(son)
|
||||||
|
)
|
||||||
|
|
||||||
# Get the class name from the document, falling back to the given
|
# Get the class name from the document, falling back to the given
|
||||||
# class if unavailable
|
# class if unavailable
|
||||||
@@ -741,7 +739,7 @@ class BaseDocument(object):
|
|||||||
# Convert SON to a data dict, making sure each key is a string and
|
# Convert SON to a data dict, making sure each key is a string and
|
||||||
# corresponds to the right db field.
|
# corresponds to the right db field.
|
||||||
data = {}
|
data = {}
|
||||||
for key, value in iteritems(son):
|
for key, value in son.items():
|
||||||
key = str(key)
|
key = str(key)
|
||||||
key = cls._db_field_map.get(key, key)
|
key = cls._db_field_map.get(key, key)
|
||||||
data[key] = value
|
data[key] = value
|
||||||
@@ -756,7 +754,7 @@ class BaseDocument(object):
|
|||||||
if not _auto_dereference:
|
if not _auto_dereference:
|
||||||
fields = copy.deepcopy(fields)
|
fields = copy.deepcopy(fields)
|
||||||
|
|
||||||
for field_name, field in iteritems(fields):
|
for field_name, field in fields.items():
|
||||||
field._auto_dereference = _auto_dereference
|
field._auto_dereference = _auto_dereference
|
||||||
if field.db_field in data:
|
if field.db_field in data:
|
||||||
value = data[field.db_field]
|
value = data[field.db_field]
|
||||||
@@ -770,16 +768,17 @@ class BaseDocument(object):
|
|||||||
errors_dict[field_name] = e
|
errors_dict[field_name] = e
|
||||||
|
|
||||||
if errors_dict:
|
if errors_dict:
|
||||||
errors = "\n".join(["%s - %s" % (k, v) for k, v in errors_dict.items()])
|
errors = "\n".join(
|
||||||
msg = "Invalid data to create a `%s` instance.\n%s" % (
|
["Field '{}' - {}".format(k, v) for k, v in errors_dict.items()]
|
||||||
cls._class_name,
|
)
|
||||||
errors,
|
msg = "Invalid data to create a `{}` instance.\n{}".format(
|
||||||
|
cls._class_name, errors,
|
||||||
)
|
)
|
||||||
raise InvalidDocumentError(msg)
|
raise InvalidDocumentError(msg)
|
||||||
|
|
||||||
# In STRICT documents, remove any keys that aren't in cls._fields
|
# In STRICT documents, remove any keys that aren't in cls._fields
|
||||||
if cls.STRICT:
|
if cls.STRICT:
|
||||||
data = {k: v for k, v in iteritems(data) if k in cls._fields}
|
data = {k: v for k, v in data.items() if k in cls._fields}
|
||||||
|
|
||||||
obj = cls(
|
obj = cls(
|
||||||
__auto_convert=False, _created=created, __only_fields=only_fields, **data
|
__auto_convert=False, _created=created, __only_fields=only_fields, **data
|
||||||
@@ -826,7 +825,7 @@ class BaseDocument(object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _build_index_spec(cls, spec):
|
def _build_index_spec(cls, spec):
|
||||||
"""Build a PyMongo index spec from a MongoEngine index spec."""
|
"""Build a PyMongo index spec from a MongoEngine index spec."""
|
||||||
if isinstance(spec, six.string_types):
|
if isinstance(spec, str):
|
||||||
spec = {"fields": [spec]}
|
spec = {"fields": [spec]}
|
||||||
elif isinstance(spec, (list, tuple)):
|
elif isinstance(spec, (list, tuple)):
|
||||||
spec = {"fields": list(spec)}
|
spec = {"fields": list(spec)}
|
||||||
@@ -923,7 +922,7 @@ class BaseDocument(object):
|
|||||||
|
|
||||||
# Add any unique_with fields to the back of the index spec
|
# Add any unique_with fields to the back of the index spec
|
||||||
if field.unique_with:
|
if field.unique_with:
|
||||||
if isinstance(field.unique_with, six.string_types):
|
if isinstance(field.unique_with, str):
|
||||||
field.unique_with = [field.unique_with]
|
field.unique_with = [field.unique_with]
|
||||||
|
|
||||||
# Convert unique_with field names to real field names
|
# Convert unique_with field names to real field names
|
||||||
@@ -944,7 +943,8 @@ class BaseDocument(object):
|
|||||||
|
|
||||||
# Add the new index to the list
|
# Add the new index to the list
|
||||||
fields = [
|
fields = [
|
||||||
("%s%s" % (namespace, f), pymongo.ASCENDING) for f in unique_fields
|
("{}{}".format(namespace, f), pymongo.ASCENDING)
|
||||||
|
for f in unique_fields
|
||||||
]
|
]
|
||||||
index = {"fields": fields, "unique": True, "sparse": sparse}
|
index = {"fields": fields, "unique": True, "sparse": sparse}
|
||||||
unique_indexes.append(index)
|
unique_indexes.append(index)
|
||||||
@@ -1001,7 +1001,7 @@ class BaseDocument(object):
|
|||||||
elif field._geo_index:
|
elif field._geo_index:
|
||||||
field_name = field.db_field
|
field_name = field.db_field
|
||||||
if parent_field:
|
if parent_field:
|
||||||
field_name = "%s.%s" % (parent_field, field_name)
|
field_name = "{}.{}".format(parent_field, field_name)
|
||||||
geo_indices.append({"fields": [(field_name, field._geo_index)]})
|
geo_indices.append({"fields": [(field_name, field._geo_index)]})
|
||||||
|
|
||||||
return geo_indices
|
return geo_indices
|
||||||
@@ -1170,9 +1170,6 @@ class BaseDocument(object):
|
|||||||
else [value]
|
else [value]
|
||||||
)
|
)
|
||||||
return sep.join(
|
return sep.join(
|
||||||
[
|
[str(dict(field.choices).get(val, val)) for val in values or []]
|
||||||
six.text_type(dict(field.choices).get(val, val))
|
|
||||||
for val in values or []
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
return value
|
return value
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import weakref
|
|||||||
|
|
||||||
from bson import DBRef, ObjectId, SON
|
from bson import DBRef, ObjectId, SON
|
||||||
import pymongo
|
import pymongo
|
||||||
import six
|
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine.base.common import UPDATE_OPERATORS
|
from mongoengine.base.common import UPDATE_OPERATORS
|
||||||
from mongoengine.base.datastructures import BaseDict, BaseList, EmbeddedDocumentList
|
from mongoengine.base.datastructures import BaseDict, BaseList, EmbeddedDocumentList
|
||||||
@@ -15,7 +13,7 @@ from mongoengine.errors import DeprecatedError, ValidationError
|
|||||||
__all__ = ("BaseField", "ComplexBaseField", "ObjectIdField", "GeoJsonBaseField")
|
__all__ = ("BaseField", "ComplexBaseField", "ObjectIdField", "GeoJsonBaseField")
|
||||||
|
|
||||||
|
|
||||||
class BaseField(object):
|
class BaseField:
|
||||||
"""A base class for fields in a MongoDB document. Instances of this class
|
"""A base class for fields in a MongoDB document. Instances of this class
|
||||||
may be added to subclasses of `Document` to define a document's schema.
|
may be added to subclasses of `Document` to define a document's schema.
|
||||||
|
|
||||||
@@ -36,7 +34,6 @@ class BaseField(object):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
db_field=None,
|
db_field=None,
|
||||||
name=None,
|
|
||||||
required=False,
|
required=False,
|
||||||
default=None,
|
default=None,
|
||||||
unique=False,
|
unique=False,
|
||||||
@@ -51,7 +48,6 @@ class BaseField(object):
|
|||||||
"""
|
"""
|
||||||
:param db_field: The database field to store this field in
|
:param db_field: The database field to store this field in
|
||||||
(defaults to the name of the field)
|
(defaults to the name of the field)
|
||||||
:param name: Deprecated - use db_field
|
|
||||||
:param required: If the field is required. Whether it has to have a
|
:param required: If the field is required. Whether it has to have a
|
||||||
value or not. Defaults to False.
|
value or not. Defaults to False.
|
||||||
:param default: (optional) The default value for this field if no value
|
:param default: (optional) The default value for this field if no value
|
||||||
@@ -75,11 +71,8 @@ class BaseField(object):
|
|||||||
existing attributes. Common metadata includes `verbose_name` and
|
existing attributes. Common metadata includes `verbose_name` and
|
||||||
`help_text`.
|
`help_text`.
|
||||||
"""
|
"""
|
||||||
self.db_field = (db_field or name) if not primary_key else "_id"
|
self.db_field = db_field if not primary_key else "_id"
|
||||||
|
|
||||||
if name:
|
|
||||||
msg = 'Field\'s "name" attribute deprecated in favour of "db_field"'
|
|
||||||
warnings.warn(msg, DeprecationWarning)
|
|
||||||
self.required = required or primary_key
|
self.required = required or primary_key
|
||||||
self.default = default
|
self.default = default
|
||||||
self.unique = bool(unique or unique_with)
|
self.unique = bool(unique or unique_with)
|
||||||
@@ -92,13 +85,11 @@ class BaseField(object):
|
|||||||
self._owner_document = None
|
self._owner_document = None
|
||||||
|
|
||||||
# Make sure db_field is a string (if it's explicitly defined).
|
# Make sure db_field is a string (if it's explicitly defined).
|
||||||
if self.db_field is not None and not isinstance(
|
if self.db_field is not None and not isinstance(self.db_field, str):
|
||||||
self.db_field, six.string_types
|
|
||||||
):
|
|
||||||
raise TypeError("db_field should be a string.")
|
raise TypeError("db_field should be a string.")
|
||||||
|
|
||||||
# Make sure db_field doesn't contain any forbidden characters.
|
# Make sure db_field doesn't contain any forbidden characters.
|
||||||
if isinstance(self.db_field, six.string_types) and (
|
if isinstance(self.db_field, str) and (
|
||||||
"." in self.db_field
|
"." in self.db_field
|
||||||
or "\0" in self.db_field
|
or "\0" in self.db_field
|
||||||
or self.db_field.startswith("$")
|
or self.db_field.startswith("$")
|
||||||
@@ -221,14 +212,12 @@ class BaseField(object):
|
|||||||
# Choices which are other types of Documents
|
# Choices which are other types of Documents
|
||||||
if isinstance(value, (Document, EmbeddedDocument)):
|
if isinstance(value, (Document, EmbeddedDocument)):
|
||||||
if not any(isinstance(value, c) for c in choice_list):
|
if not any(isinstance(value, c) for c in choice_list):
|
||||||
self.error(
|
self.error("Value must be an instance of %s" % (choice_list))
|
||||||
"Value must be an instance of %s" % (six.text_type(choice_list))
|
|
||||||
)
|
|
||||||
# Choices which are types other than Documents
|
# Choices which are types other than Documents
|
||||||
else:
|
else:
|
||||||
values = value if isinstance(value, (list, tuple)) else [value]
|
values = value if isinstance(value, (list, tuple)) else [value]
|
||||||
if len(set(values) - set(choice_list)):
|
if len(set(values) - set(choice_list)):
|
||||||
self.error("Value must be one of %s" % six.text_type(choice_list))
|
self.error("Value must be one of %s" % str(choice_list))
|
||||||
|
|
||||||
def _validate(self, value, **kwargs):
|
def _validate(self, value, **kwargs):
|
||||||
# Check the Choices Constraint
|
# Check the Choices Constraint
|
||||||
@@ -316,7 +305,7 @@ class ComplexBaseField(BaseField):
|
|||||||
if hasattr(instance._data[self.name], "_dereferenced"):
|
if hasattr(instance._data[self.name], "_dereferenced"):
|
||||||
instance._data[self.name]._dereferenced = True
|
instance._data[self.name]._dereferenced = True
|
||||||
|
|
||||||
value = super(ComplexBaseField, self).__get__(instance, owner)
|
value = super().__get__(instance, owner)
|
||||||
|
|
||||||
# Convert lists / values so we can watch for any changes on them
|
# Convert lists / values so we can watch for any changes on them
|
||||||
if isinstance(value, (list, tuple)):
|
if isinstance(value, (list, tuple)):
|
||||||
@@ -345,7 +334,7 @@ class ComplexBaseField(BaseField):
|
|||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
"""Convert a MongoDB-compatible type to a Python type."""
|
"""Convert a MongoDB-compatible type to a Python type."""
|
||||||
if isinstance(value, six.string_types):
|
if isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
if hasattr(value, "to_python"):
|
if hasattr(value, "to_python"):
|
||||||
@@ -399,7 +388,7 @@ class ComplexBaseField(BaseField):
|
|||||||
EmbeddedDocument = _import_class("EmbeddedDocument")
|
EmbeddedDocument = _import_class("EmbeddedDocument")
|
||||||
GenericReferenceField = _import_class("GenericReferenceField")
|
GenericReferenceField = _import_class("GenericReferenceField")
|
||||||
|
|
||||||
if isinstance(value, six.string_types):
|
if isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
if hasattr(value, "to_mongo"):
|
if hasattr(value, "to_mongo"):
|
||||||
@@ -423,11 +412,11 @@ class ComplexBaseField(BaseField):
|
|||||||
if self.field:
|
if self.field:
|
||||||
value_dict = {
|
value_dict = {
|
||||||
key: self.field._to_mongo_safe_call(item, use_db_field, fields)
|
key: self.field._to_mongo_safe_call(item, use_db_field, fields)
|
||||||
for key, item in iteritems(value)
|
for key, item in value.items()
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
value_dict = {}
|
value_dict = {}
|
||||||
for k, v in iteritems(value):
|
for k, v in value.items():
|
||||||
if isinstance(v, Document):
|
if isinstance(v, Document):
|
||||||
# We need the id from the saved object to create the DBRef
|
# We need the id from the saved object to create the DBRef
|
||||||
if v.pk is None:
|
if v.pk is None:
|
||||||
@@ -466,8 +455,8 @@ class ComplexBaseField(BaseField):
|
|||||||
"""If field is provided ensure the value is valid."""
|
"""If field is provided ensure the value is valid."""
|
||||||
errors = {}
|
errors = {}
|
||||||
if self.field:
|
if self.field:
|
||||||
if hasattr(value, "iteritems") or hasattr(value, "items"):
|
if hasattr(value, "items"):
|
||||||
sequence = iteritems(value)
|
sequence = value.items()
|
||||||
else:
|
else:
|
||||||
sequence = enumerate(value)
|
sequence = enumerate(value)
|
||||||
for k, v in sequence:
|
for k, v in sequence:
|
||||||
@@ -480,7 +469,9 @@ class ComplexBaseField(BaseField):
|
|||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
field_class = self.field.__class__.__name__
|
field_class = self.field.__class__.__name__
|
||||||
self.error("Invalid %s item (%s)" % (field_class, value), errors=errors)
|
self.error(
|
||||||
|
"Invalid {} item ({})".format(field_class, value), errors=errors
|
||||||
|
)
|
||||||
# Don't allow empty values if required
|
# Don't allow empty values if required
|
||||||
if self.required and not value:
|
if self.required and not value:
|
||||||
self.error("Field is required and cannot be empty")
|
self.error("Field is required and cannot be empty")
|
||||||
@@ -513,10 +504,9 @@ class ObjectIdField(BaseField):
|
|||||||
def to_mongo(self, value):
|
def to_mongo(self, value):
|
||||||
if not isinstance(value, ObjectId):
|
if not isinstance(value, ObjectId):
|
||||||
try:
|
try:
|
||||||
return ObjectId(six.text_type(value))
|
return ObjectId(str(value))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# e.message attribute has been deprecated since Python 2.6
|
self.error(str(e))
|
||||||
self.error(six.text_type(e))
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
@@ -524,9 +514,9 @@ class ObjectIdField(BaseField):
|
|||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
try:
|
try:
|
||||||
ObjectId(six.text_type(value))
|
ObjectId(str(value))
|
||||||
except Exception:
|
except Exception:
|
||||||
self.error("Invalid Object ID")
|
self.error("Invalid ObjectID")
|
||||||
|
|
||||||
|
|
||||||
class GeoJsonBaseField(BaseField):
|
class GeoJsonBaseField(BaseField):
|
||||||
@@ -546,14 +536,14 @@ class GeoJsonBaseField(BaseField):
|
|||||||
self._name = "%sField" % self._type
|
self._name = "%sField" % self._type
|
||||||
if not auto_index:
|
if not auto_index:
|
||||||
self._geo_index = False
|
self._geo_index = False
|
||||||
super(GeoJsonBaseField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
"""Validate the GeoJson object based on its type."""
|
"""Validate the GeoJson object based on its type."""
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
if set(value.keys()) == {"type", "coordinates"}:
|
if set(value.keys()) == {"type", "coordinates"}:
|
||||||
if value["type"] != self._type:
|
if value["type"] != self._type:
|
||||||
self.error('%s type must be "%s"' % (self._name, self._type))
|
self.error('{} type must be "{}"'.format(self._name, self._type))
|
||||||
return self.validate(value["coordinates"])
|
return self.validate(value["coordinates"])
|
||||||
else:
|
else:
|
||||||
self.error(
|
self.error(
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
import six
|
|
||||||
from six import iteritems, itervalues
|
|
||||||
|
|
||||||
from mongoengine.base.common import _document_registry
|
from mongoengine.base.common import _document_registry
|
||||||
from mongoengine.base.fields import BaseField, ComplexBaseField, ObjectIdField
|
from mongoengine.base.fields import BaseField, ComplexBaseField, ObjectIdField
|
||||||
from mongoengine.common import _import_class
|
from mongoengine.common import _import_class
|
||||||
@@ -25,7 +22,7 @@ class DocumentMetaclass(type):
|
|||||||
# TODO lower complexity of this method
|
# TODO lower complexity of this method
|
||||||
def __new__(mcs, name, bases, attrs):
|
def __new__(mcs, name, bases, attrs):
|
||||||
flattened_bases = mcs._get_bases(bases)
|
flattened_bases = mcs._get_bases(bases)
|
||||||
super_new = super(DocumentMetaclass, mcs).__new__
|
super_new = super().__new__
|
||||||
|
|
||||||
# If a base class just call super
|
# If a base class just call super
|
||||||
metaclass = attrs.get("my_metaclass")
|
metaclass = attrs.get("my_metaclass")
|
||||||
@@ -69,7 +66,7 @@ class DocumentMetaclass(type):
|
|||||||
# Standard object mixin - merge in any Fields
|
# Standard object mixin - merge in any Fields
|
||||||
if not hasattr(base, "_meta"):
|
if not hasattr(base, "_meta"):
|
||||||
base_fields = {}
|
base_fields = {}
|
||||||
for attr_name, attr_value in iteritems(base.__dict__):
|
for attr_name, attr_value in base.__dict__.items():
|
||||||
if not isinstance(attr_value, BaseField):
|
if not isinstance(attr_value, BaseField):
|
||||||
continue
|
continue
|
||||||
attr_value.name = attr_name
|
attr_value.name = attr_name
|
||||||
@@ -81,7 +78,7 @@ class DocumentMetaclass(type):
|
|||||||
|
|
||||||
# Discover any document fields
|
# Discover any document fields
|
||||||
field_names = {}
|
field_names = {}
|
||||||
for attr_name, attr_value in iteritems(attrs):
|
for attr_name, attr_value in attrs.items():
|
||||||
if not isinstance(attr_value, BaseField):
|
if not isinstance(attr_value, BaseField):
|
||||||
continue
|
continue
|
||||||
attr_value.name = attr_name
|
attr_value.name = attr_name
|
||||||
@@ -111,9 +108,7 @@ class DocumentMetaclass(type):
|
|||||||
|
|
||||||
attrs["_fields_ordered"] = tuple(
|
attrs["_fields_ordered"] = tuple(
|
||||||
i[1]
|
i[1]
|
||||||
for i in sorted(
|
for i in sorted((v.creation_counter, v.name) for v in doc_fields.values())
|
||||||
(v.creation_counter, v.name) for v in itervalues(doc_fields)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -173,24 +168,8 @@ class DocumentMetaclass(type):
|
|||||||
# Add class to the _document_registry
|
# Add class to the _document_registry
|
||||||
_document_registry[new_class._class_name] = new_class
|
_document_registry[new_class._class_name] = new_class
|
||||||
|
|
||||||
# In Python 2, User-defined methods objects have special read-only
|
|
||||||
# attributes 'im_func' and 'im_self' which contain the function obj
|
|
||||||
# and class instance object respectively. With Python 3 these special
|
|
||||||
# attributes have been replaced by __func__ and __self__. The Blinker
|
|
||||||
# module continues to use im_func and im_self, so the code below
|
|
||||||
# copies __func__ into im_func and __self__ into im_self for
|
|
||||||
# classmethod objects in Document derived classes.
|
|
||||||
if six.PY3:
|
|
||||||
for val in new_class.__dict__.values():
|
|
||||||
if isinstance(val, classmethod):
|
|
||||||
f = val.__get__(new_class)
|
|
||||||
if hasattr(f, "__func__") and not hasattr(f, "im_func"):
|
|
||||||
f.__dict__.update({"im_func": getattr(f, "__func__")})
|
|
||||||
if hasattr(f, "__self__") and not hasattr(f, "im_self"):
|
|
||||||
f.__dict__.update({"im_self": getattr(f, "__self__")})
|
|
||||||
|
|
||||||
# Handle delete rules
|
# Handle delete rules
|
||||||
for field in itervalues(new_class._fields):
|
for field in new_class._fields.values():
|
||||||
f = field
|
f = field
|
||||||
if f.owner_document is None:
|
if f.owner_document is None:
|
||||||
f.owner_document = new_class
|
f.owner_document = new_class
|
||||||
@@ -252,8 +231,7 @@ class DocumentMetaclass(type):
|
|||||||
if base is object:
|
if base is object:
|
||||||
continue
|
continue
|
||||||
yield base
|
yield base
|
||||||
for child_base in mcs.__get_bases(base.__bases__):
|
yield from mcs.__get_bases(base.__bases__)
|
||||||
yield child_base
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _import_classes(mcs):
|
def _import_classes(mcs):
|
||||||
@@ -271,7 +249,7 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
|
|
||||||
def __new__(mcs, name, bases, attrs):
|
def __new__(mcs, name, bases, attrs):
|
||||||
flattened_bases = mcs._get_bases(bases)
|
flattened_bases = mcs._get_bases(bases)
|
||||||
super_new = super(TopLevelDocumentMetaclass, mcs).__new__
|
super_new = super().__new__
|
||||||
|
|
||||||
# Set default _meta data if base class, otherwise get user defined meta
|
# Set default _meta data if base class, otherwise get user defined meta
|
||||||
if attrs.get("my_metaclass") == TopLevelDocumentMetaclass:
|
if attrs.get("my_metaclass") == TopLevelDocumentMetaclass:
|
||||||
@@ -284,7 +262,6 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
"indexes": [], # indexes to be ensured at runtime
|
"indexes": [], # indexes to be ensured at runtime
|
||||||
"id_field": None,
|
"id_field": None,
|
||||||
"index_background": False,
|
"index_background": False,
|
||||||
"index_drop_dups": False,
|
|
||||||
"index_opts": None,
|
"index_opts": None,
|
||||||
"delete_rules": None,
|
"delete_rules": None,
|
||||||
# allow_inheritance can be True, False, and None. True means
|
# allow_inheritance can be True, False, and None. True means
|
||||||
@@ -399,7 +376,7 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
new_class.objects = QuerySetManager()
|
new_class.objects = QuerySetManager()
|
||||||
|
|
||||||
# Validate the fields and set primary key if needed
|
# Validate the fields and set primary key if needed
|
||||||
for field_name, field in iteritems(new_class._fields):
|
for field_name, field in new_class._fields.items():
|
||||||
if field.primary_key:
|
if field.primary_key:
|
||||||
# Ensure only one primary key is set
|
# Ensure only one primary key is set
|
||||||
current_pk = new_class._meta.get("id_field")
|
current_pk = new_class._meta.get("id_field")
|
||||||
@@ -462,8 +439,8 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
|
|
||||||
id_basename, id_db_basename, i = ("auto_id", "_auto_id", 0)
|
id_basename, id_db_basename, i = ("auto_id", "_auto_id", 0)
|
||||||
for i in itertools.count():
|
for i in itertools.count():
|
||||||
id_name = "{0}_{1}".format(id_basename, i)
|
id_name = "{}_{}".format(id_basename, i)
|
||||||
id_db_name = "{0}_{1}".format(id_db_basename, i)
|
id_db_name = "{}_{}".format(id_db_basename, i)
|
||||||
if id_name not in existing_fields and id_db_name not in existing_db_fields:
|
if id_name not in existing_fields and id_db_name not in existing_db_fields:
|
||||||
return id_name, id_db_name
|
return id_name, id_db_name
|
||||||
|
|
||||||
@@ -476,7 +453,7 @@ class MetaDict(dict):
|
|||||||
_merge_options = ("indexes",)
|
_merge_options = ("indexes",)
|
||||||
|
|
||||||
def merge(self, new_options):
|
def merge(self, new_options):
|
||||||
for k, v in iteritems(new_options):
|
for k, v in new_options.items():
|
||||||
if k in self._merge_options:
|
if k in self._merge_options:
|
||||||
self[k] = self.get(k, []) + v
|
self[k] = self.get(k, []) + v
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
class LazyRegexCompiler(object):
|
class LazyRegexCompiler:
|
||||||
"""Descriptor to allow lazy compilation of regex"""
|
"""Descriptor to allow lazy compilation of regex"""
|
||||||
|
|
||||||
def __init__(self, pattern, flags=0):
|
def __init__(self, pattern, flags=0):
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from pymongo import MongoClient, ReadPreference, uri_parser
|
from pymongo import MongoClient, ReadPreference, uri_parser
|
||||||
from pymongo.database import _check_name
|
from pymongo.database import _check_name
|
||||||
import six
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"DEFAULT_CONNECTION_NAME",
|
"DEFAULT_CONNECTION_NAME",
|
||||||
@@ -39,8 +38,8 @@ def _check_db_name(name):
|
|||||||
"""Check if a database name is valid.
|
"""Check if a database name is valid.
|
||||||
This functionality is copied from pymongo Database class constructor.
|
This functionality is copied from pymongo Database class constructor.
|
||||||
"""
|
"""
|
||||||
if not isinstance(name, six.string_types):
|
if not isinstance(name, str):
|
||||||
raise TypeError("name must be an instance of %s" % six.string_types)
|
raise TypeError("name must be an instance of %s" % str)
|
||||||
elif name != "$external":
|
elif name != "$external":
|
||||||
_check_name(name)
|
_check_name(name)
|
||||||
|
|
||||||
@@ -93,7 +92,7 @@ def _get_connection_settings(
|
|||||||
conn_host = conn_settings["host"]
|
conn_host = conn_settings["host"]
|
||||||
|
|
||||||
# Host can be a list or a string, so if string, force to a list.
|
# Host can be a list or a string, so if string, force to a list.
|
||||||
if isinstance(conn_host, six.string_types):
|
if isinstance(conn_host, str):
|
||||||
conn_host = [conn_host]
|
conn_host = [conn_host]
|
||||||
|
|
||||||
resolved_hosts = []
|
resolved_hosts = []
|
||||||
@@ -148,7 +147,7 @@ def _get_connection_settings(
|
|||||||
# TODO simplify the code below once we drop support for
|
# TODO simplify the code below once we drop support for
|
||||||
# PyMongo v3.4.
|
# PyMongo v3.4.
|
||||||
read_pf_mode = uri_options["readpreference"]
|
read_pf_mode = uri_options["readpreference"]
|
||||||
if isinstance(read_pf_mode, six.string_types):
|
if isinstance(read_pf_mode, str):
|
||||||
read_pf_mode = read_pf_mode.lower()
|
read_pf_mode = read_pf_mode.lower()
|
||||||
for preference in read_preferences:
|
for preference in read_preferences:
|
||||||
if (
|
if (
|
||||||
@@ -318,7 +317,7 @@ def _create_connection(alias, connection_class, **connection_settings):
|
|||||||
try:
|
try:
|
||||||
return connection_class(**connection_settings)
|
return connection_class(**connection_settings)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ConnectionFailure("Cannot connect to database %s :\n%s" % (alias, e))
|
raise ConnectionFailure("Cannot connect to database {} :\n{}".format(alias, e))
|
||||||
|
|
||||||
|
|
||||||
def _find_existing_connection(connection_settings):
|
def _find_existing_connection(connection_settings):
|
||||||
@@ -396,8 +395,8 @@ def connect(db=None, alias=DEFAULT_CONNECTION_NAME, **kwargs):
|
|||||||
|
|
||||||
if new_conn_settings != prev_conn_setting:
|
if new_conn_settings != prev_conn_setting:
|
||||||
err_msg = (
|
err_msg = (
|
||||||
u"A different connection with alias `{}` was already "
|
"A different connection with alias `{}` was already "
|
||||||
u"registered. Use disconnect() first"
|
"registered. Use disconnect() first"
|
||||||
).format(alias)
|
).format(alias)
|
||||||
raise ConnectionFailure(err_msg)
|
raise ConnectionFailure(err_msg)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
from pymongo.read_concern import ReadConcern
|
||||||
from pymongo.write_concern import WriteConcern
|
from pymongo.write_concern import WriteConcern
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine.common import _import_class
|
from mongoengine.common import _import_class
|
||||||
from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db
|
from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db
|
||||||
@@ -14,10 +14,11 @@ __all__ = (
|
|||||||
"no_sub_classes",
|
"no_sub_classes",
|
||||||
"query_counter",
|
"query_counter",
|
||||||
"set_write_concern",
|
"set_write_concern",
|
||||||
|
"set_read_write_concern",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class switch_db(object):
|
class switch_db:
|
||||||
"""switch_db alias context manager.
|
"""switch_db alias context manager.
|
||||||
|
|
||||||
Example ::
|
Example ::
|
||||||
@@ -58,7 +59,7 @@ class switch_db(object):
|
|||||||
self.cls._collection = self.collection
|
self.cls._collection = self.collection
|
||||||
|
|
||||||
|
|
||||||
class switch_collection(object):
|
class switch_collection:
|
||||||
"""switch_collection alias context manager.
|
"""switch_collection alias context manager.
|
||||||
|
|
||||||
Example ::
|
Example ::
|
||||||
@@ -100,7 +101,7 @@ class switch_collection(object):
|
|||||||
self.cls._get_collection_name = self.ori_get_collection_name
|
self.cls._get_collection_name = self.ori_get_collection_name
|
||||||
|
|
||||||
|
|
||||||
class no_dereference(object):
|
class no_dereference:
|
||||||
"""no_dereference context manager.
|
"""no_dereference context manager.
|
||||||
|
|
||||||
Turns off all dereferencing in Documents for the duration of the context
|
Turns off all dereferencing in Documents for the duration of the context
|
||||||
@@ -123,7 +124,7 @@ class no_dereference(object):
|
|||||||
|
|
||||||
self.deref_fields = [
|
self.deref_fields = [
|
||||||
k
|
k
|
||||||
for k, v in iteritems(self.cls._fields)
|
for k, v in self.cls._fields.items()
|
||||||
if isinstance(v, (ReferenceField, GenericReferenceField, ComplexBaseField))
|
if isinstance(v, (ReferenceField, GenericReferenceField, ComplexBaseField))
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -140,7 +141,7 @@ class no_dereference(object):
|
|||||||
return self.cls
|
return self.cls
|
||||||
|
|
||||||
|
|
||||||
class no_sub_classes(object):
|
class no_sub_classes:
|
||||||
"""no_sub_classes context manager.
|
"""no_sub_classes context manager.
|
||||||
|
|
||||||
Only returns instances of this class and no sub (inherited) classes::
|
Only returns instances of this class and no sub (inherited) classes::
|
||||||
@@ -168,10 +169,10 @@ class no_sub_classes(object):
|
|||||||
self.cls._subclasses = self.cls_initial_subclasses
|
self.cls._subclasses = self.cls_initial_subclasses
|
||||||
|
|
||||||
|
|
||||||
class query_counter(object):
|
class query_counter:
|
||||||
"""Query_counter context manager to get the number of queries.
|
"""Query_counter context manager to get the number of queries.
|
||||||
This works by updating the `profiling_level` of the database so that all queries get logged,
|
This works by updating the `profiling_level` of the database so that all queries get logged,
|
||||||
resetting the db.system.profile collection at the beginnig of the context and counting the new entries.
|
resetting the db.system.profile collection at the beginning of the context and counting the new entries.
|
||||||
|
|
||||||
This was designed for debugging purpose. In fact it is a global counter so queries issued by other threads/processes
|
This was designed for debugging purpose. In fact it is a global counter so queries issued by other threads/processes
|
||||||
can interfere with it
|
can interfere with it
|
||||||
@@ -182,10 +183,10 @@ class query_counter(object):
|
|||||||
- Some queries are ignored by default by the counter (killcursors, db.system.indexes)
|
- Some queries are ignored by default by the counter (killcursors, db.system.indexes)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, alias=DEFAULT_CONNECTION_NAME):
|
||||||
"""Construct the query_counter
|
"""Construct the query_counter
|
||||||
"""
|
"""
|
||||||
self.db = get_db()
|
self.db = get_db(alias=alias)
|
||||||
self.initial_profiling_level = None
|
self.initial_profiling_level = None
|
||||||
self._ctx_query_counter = 0 # number of queries issued by the context
|
self._ctx_query_counter = 0 # number of queries issued by the context
|
||||||
|
|
||||||
@@ -235,7 +236,7 @@ class query_counter(object):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""repr query_counter as the number of queries."""
|
"""repr query_counter as the number of queries."""
|
||||||
return u"%s" % self._get_count()
|
return "%s" % self._get_count()
|
||||||
|
|
||||||
def _get_count(self):
|
def _get_count(self):
|
||||||
"""Get the number of queries by counting the current number of entries in db.system.profile
|
"""Get the number of queries by counting the current number of entries in db.system.profile
|
||||||
@@ -247,8 +248,8 @@ class query_counter(object):
|
|||||||
- self._ctx_query_counter
|
- self._ctx_query_counter
|
||||||
)
|
)
|
||||||
self._ctx_query_counter += (
|
self._ctx_query_counter += (
|
||||||
1
|
1 # Account for the query we just issued to gather the information
|
||||||
) # Account for the query we just issued to gather the information
|
)
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
@@ -257,3 +258,21 @@ def set_write_concern(collection, write_concerns):
|
|||||||
combined_concerns = dict(collection.write_concern.document.items())
|
combined_concerns = dict(collection.write_concern.document.items())
|
||||||
combined_concerns.update(write_concerns)
|
combined_concerns.update(write_concerns)
|
||||||
yield collection.with_options(write_concern=WriteConcern(**combined_concerns))
|
yield collection.with_options(write_concern=WriteConcern(**combined_concerns))
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def set_read_write_concern(collection, write_concerns, read_concerns):
|
||||||
|
combined_write_concerns = dict(collection.write_concern.document.items())
|
||||||
|
|
||||||
|
if write_concerns is not None:
|
||||||
|
combined_write_concerns.update(write_concerns)
|
||||||
|
|
||||||
|
combined_read_concerns = dict(collection.read_concern.document.items())
|
||||||
|
|
||||||
|
if read_concerns is not None:
|
||||||
|
combined_read_concerns.update(read_concerns)
|
||||||
|
|
||||||
|
yield collection.with_options(
|
||||||
|
write_concern=WriteConcern(**combined_write_concerns),
|
||||||
|
read_concern=ReadConcern(**combined_read_concerns),
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
from bson import DBRef, SON
|
from bson import DBRef, SON
|
||||||
import six
|
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine.base import (
|
from mongoengine.base import (
|
||||||
BaseDict,
|
BaseDict,
|
||||||
@@ -16,7 +14,7 @@ from mongoengine.fields import DictField, ListField, MapField, ReferenceField
|
|||||||
from mongoengine.queryset import QuerySet
|
from mongoengine.queryset import QuerySet
|
||||||
|
|
||||||
|
|
||||||
class DeReference(object):
|
class DeReference:
|
||||||
def __call__(self, items, max_depth=1, instance=None, name=None):
|
def __call__(self, items, max_depth=1, instance=None, name=None):
|
||||||
"""
|
"""
|
||||||
Cheaply dereferences the items to a set depth.
|
Cheaply dereferences the items to a set depth.
|
||||||
@@ -30,7 +28,7 @@ class DeReference(object):
|
|||||||
:class:`~mongoengine.base.ComplexBaseField`
|
:class:`~mongoengine.base.ComplexBaseField`
|
||||||
:param get: A boolean determining if being called by __get__
|
:param get: A boolean determining if being called by __get__
|
||||||
"""
|
"""
|
||||||
if items is None or isinstance(items, six.string_types):
|
if items is None or isinstance(items, str):
|
||||||
return items
|
return items
|
||||||
|
|
||||||
# cheapest way to convert a queryset to a list
|
# cheapest way to convert a queryset to a list
|
||||||
@@ -79,7 +77,7 @@ class DeReference(object):
|
|||||||
|
|
||||||
def _get_items_from_dict(items):
|
def _get_items_from_dict(items):
|
||||||
new_items = {}
|
new_items = {}
|
||||||
for k, v in iteritems(items):
|
for k, v in items.items():
|
||||||
value = v
|
value = v
|
||||||
if isinstance(v, list):
|
if isinstance(v, list):
|
||||||
value = _get_items_from_list(v)
|
value = _get_items_from_list(v)
|
||||||
@@ -120,7 +118,7 @@ class DeReference(object):
|
|||||||
depth += 1
|
depth += 1
|
||||||
for item in iterator:
|
for item in iterator:
|
||||||
if isinstance(item, (Document, EmbeddedDocument)):
|
if isinstance(item, (Document, EmbeddedDocument)):
|
||||||
for field_name, field in iteritems(item._fields):
|
for field_name, field in item._fields.items():
|
||||||
v = item._data.get(field_name, None)
|
v = item._data.get(field_name, None)
|
||||||
if isinstance(v, LazyReference):
|
if isinstance(v, LazyReference):
|
||||||
# LazyReference inherits DBRef but should not be dereferenced here !
|
# LazyReference inherits DBRef but should not be dereferenced here !
|
||||||
@@ -136,7 +134,7 @@ class DeReference(object):
|
|||||||
getattr(field, "field", None), "document_type", None
|
getattr(field, "field", None), "document_type", None
|
||||||
)
|
)
|
||||||
references = self._find_references(v, depth)
|
references = self._find_references(v, depth)
|
||||||
for key, refs in iteritems(references):
|
for key, refs in references.items():
|
||||||
if isinstance(
|
if isinstance(
|
||||||
field_cls, (Document, TopLevelDocumentMetaclass)
|
field_cls, (Document, TopLevelDocumentMetaclass)
|
||||||
):
|
):
|
||||||
@@ -153,7 +151,7 @@ class DeReference(object):
|
|||||||
)
|
)
|
||||||
elif isinstance(item, (dict, list, tuple)) and depth - 1 <= self.max_depth:
|
elif isinstance(item, (dict, list, tuple)) and depth - 1 <= self.max_depth:
|
||||||
references = self._find_references(item, depth - 1)
|
references = self._find_references(item, depth - 1)
|
||||||
for key, refs in iteritems(references):
|
for key, refs in references.items():
|
||||||
reference_map.setdefault(key, set()).update(refs)
|
reference_map.setdefault(key, set()).update(refs)
|
||||||
|
|
||||||
return reference_map
|
return reference_map
|
||||||
@@ -162,7 +160,7 @@ class DeReference(object):
|
|||||||
"""Fetch all references and convert to their document objects
|
"""Fetch all references and convert to their document objects
|
||||||
"""
|
"""
|
||||||
object_map = {}
|
object_map = {}
|
||||||
for collection, dbrefs in iteritems(self.reference_map):
|
for collection, dbrefs in self.reference_map.items():
|
||||||
|
|
||||||
# we use getattr instead of hasattr because hasattr swallows any exception under python2
|
# we use getattr instead of hasattr because hasattr swallows any exception under python2
|
||||||
# so it could hide nasty things without raising exceptions (cfr bug #1688))
|
# so it could hide nasty things without raising exceptions (cfr bug #1688))
|
||||||
@@ -174,7 +172,7 @@ class DeReference(object):
|
|||||||
dbref for dbref in dbrefs if (col_name, dbref) not in object_map
|
dbref for dbref in dbrefs if (col_name, dbref) not in object_map
|
||||||
]
|
]
|
||||||
references = collection.objects.in_bulk(refs)
|
references = collection.objects.in_bulk(refs)
|
||||||
for key, doc in iteritems(references):
|
for key, doc in references.items():
|
||||||
object_map[(col_name, key)] = doc
|
object_map[(col_name, key)] = doc
|
||||||
else: # Generic reference: use the refs data to convert to document
|
else: # Generic reference: use the refs data to convert to document
|
||||||
if isinstance(doc_type, (ListField, DictField, MapField)):
|
if isinstance(doc_type, (ListField, DictField, MapField)):
|
||||||
@@ -250,7 +248,7 @@ class DeReference(object):
|
|||||||
data = []
|
data = []
|
||||||
else:
|
else:
|
||||||
is_list = False
|
is_list = False
|
||||||
iterator = iteritems(items)
|
iterator = items.items()
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
depth += 1
|
depth += 1
|
||||||
@@ -274,14 +272,12 @@ class DeReference(object):
|
|||||||
(v["_ref"].collection, v["_ref"].id), v
|
(v["_ref"].collection, v["_ref"].id), v
|
||||||
)
|
)
|
||||||
elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth:
|
elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth:
|
||||||
item_name = six.text_type("{0}.{1}.{2}").format(
|
item_name = "{}.{}.{}".format(name, k, field_name)
|
||||||
name, k, field_name
|
|
||||||
)
|
|
||||||
data[k]._data[field_name] = self._attach_objects(
|
data[k]._data[field_name] = self._attach_objects(
|
||||||
v, depth, instance=instance, name=item_name
|
v, depth, instance=instance, name=item_name
|
||||||
)
|
)
|
||||||
elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth:
|
elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth:
|
||||||
item_name = "%s.%s" % (name, k) if name else name
|
item_name = "{}.{}".format(name, k) if name else name
|
||||||
data[k] = self._attach_objects(
|
data[k] = self._attach_objects(
|
||||||
v, depth - 1, instance=instance, name=item_name
|
v, depth - 1, instance=instance, name=item_name
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import warnings
|
|||||||
from bson.dbref import DBRef
|
from bson.dbref import DBRef
|
||||||
import pymongo
|
import pymongo
|
||||||
from pymongo.read_preferences import ReadPreference
|
from pymongo.read_preferences import ReadPreference
|
||||||
import six
|
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine import signals
|
from mongoengine import signals
|
||||||
from mongoengine.base import (
|
from mongoengine.base import (
|
||||||
@@ -44,7 +42,7 @@ def includes_cls(fields):
|
|||||||
"""Helper function used for ensuring and comparing indexes."""
|
"""Helper function used for ensuring and comparing indexes."""
|
||||||
first_field = None
|
first_field = None
|
||||||
if len(fields):
|
if len(fields):
|
||||||
if isinstance(fields[0], six.string_types):
|
if isinstance(fields[0], str):
|
||||||
first_field = fields[0]
|
first_field = fields[0]
|
||||||
elif isinstance(fields[0], (list, tuple)) and len(fields[0]):
|
elif isinstance(fields[0], (list, tuple)) and len(fields[0]):
|
||||||
first_field = fields[0][0]
|
first_field = fields[0][0]
|
||||||
@@ -55,8 +53,8 @@ class InvalidCollectionError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
class EmbeddedDocument(BaseDocument, metaclass=DocumentMetaclass):
|
||||||
"""A :class:`~mongoengine.Document` that isn't stored in its own
|
r"""A :class:`~mongoengine.Document` that isn't stored in its own
|
||||||
collection. :class:`~mongoengine.EmbeddedDocument`\ s should be used as
|
collection. :class:`~mongoengine.EmbeddedDocument`\ s should be used as
|
||||||
fields on :class:`~mongoengine.Document`\ s through the
|
fields on :class:`~mongoengine.Document`\ s through the
|
||||||
:class:`~mongoengine.EmbeddedDocumentField` field type.
|
:class:`~mongoengine.EmbeddedDocumentField` field type.
|
||||||
@@ -71,7 +69,6 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
|||||||
|
|
||||||
__slots__ = ("_instance",)
|
__slots__ = ("_instance",)
|
||||||
|
|
||||||
# The __metaclass__ attribute is removed by 2to3 when running with Python3
|
|
||||||
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
||||||
my_metaclass = DocumentMetaclass
|
my_metaclass = DocumentMetaclass
|
||||||
|
|
||||||
@@ -82,7 +79,7 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
|||||||
__hash__ = None
|
__hash__ = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(EmbeddedDocument, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self._instance = None
|
self._instance = None
|
||||||
self._changed_fields = []
|
self._changed_fields = []
|
||||||
|
|
||||||
@@ -95,7 +92,7 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
|||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
def to_mongo(self, *args, **kwargs):
|
def to_mongo(self, *args, **kwargs):
|
||||||
data = super(EmbeddedDocument, self).to_mongo(*args, **kwargs)
|
data = super().to_mongo(*args, **kwargs)
|
||||||
|
|
||||||
# remove _id from the SON if it's in it and it's None
|
# remove _id from the SON if it's in it and it's None
|
||||||
if "_id" in data and data["_id"] is None:
|
if "_id" in data and data["_id"] is None:
|
||||||
@@ -104,7 +101,7 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
class Document(BaseDocument, metaclass=TopLevelDocumentMetaclass):
|
||||||
"""The base class used for defining the structure and properties of
|
"""The base class used for defining the structure and properties of
|
||||||
collections of documents stored in MongoDB. Inherit from this class, and
|
collections of documents stored in MongoDB. Inherit from this class, and
|
||||||
add fields as class attributes to define a document's structure.
|
add fields as class attributes to define a document's structure.
|
||||||
@@ -156,7 +153,6 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
in the :attr:`meta` dictionary.
|
in the :attr:`meta` dictionary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The __metaclass__ attribute is removed by 2to3 when running with Python3
|
|
||||||
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
||||||
my_metaclass = TopLevelDocumentMetaclass
|
my_metaclass = TopLevelDocumentMetaclass
|
||||||
|
|
||||||
@@ -260,7 +256,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
return db.create_collection(collection_name, **opts)
|
return db.create_collection(collection_name, **opts)
|
||||||
|
|
||||||
def to_mongo(self, *args, **kwargs):
|
def to_mongo(self, *args, **kwargs):
|
||||||
data = super(Document, self).to_mongo(*args, **kwargs)
|
data = super().to_mongo(*args, **kwargs)
|
||||||
|
|
||||||
# If '_id' is None, try and set it from self._data. If that
|
# If '_id' is None, try and set it from self._data. If that
|
||||||
# doesn't exist either, remove '_id' from the SON completely.
|
# doesn't exist either, remove '_id' from the SON completely.
|
||||||
@@ -332,7 +328,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
):
|
):
|
||||||
"""Save the :class:`~mongoengine.Document` to the database. If the
|
"""Save the :class:`~mongoengine.Document` to the database. If the
|
||||||
document already exists, it will be updated, otherwise it will be
|
document already exists, it will be updated, otherwise it will be
|
||||||
created.
|
created. Returns the saved object instance.
|
||||||
|
|
||||||
:param force_insert: only try to create a new document, don't allow
|
:param force_insert: only try to create a new document, don't allow
|
||||||
updates of existing documents.
|
updates of existing documents.
|
||||||
@@ -431,16 +427,16 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
self.cascade_save(**kwargs)
|
self.cascade_save(**kwargs)
|
||||||
|
|
||||||
except pymongo.errors.DuplicateKeyError as err:
|
except pymongo.errors.DuplicateKeyError as err:
|
||||||
message = u"Tried to save duplicate unique keys (%s)"
|
message = "Tried to save duplicate unique keys (%s)"
|
||||||
raise NotUniqueError(message % six.text_type(err))
|
raise NotUniqueError(message % err)
|
||||||
except pymongo.errors.OperationFailure as err:
|
except pymongo.errors.OperationFailure as err:
|
||||||
message = "Could not save document (%s)"
|
message = "Could not save document (%s)"
|
||||||
if re.match("^E1100[01] duplicate key", six.text_type(err)):
|
if re.match("^E1100[01] duplicate key", str(err)):
|
||||||
# E11000 - duplicate key error index
|
# E11000 - duplicate key error index
|
||||||
# E11001 - duplicate key on update
|
# E11001 - duplicate key on update
|
||||||
message = u"Tried to save duplicate unique keys (%s)"
|
message = "Tried to save duplicate unique keys (%s)"
|
||||||
raise NotUniqueError(message % six.text_type(err))
|
raise NotUniqueError(message % err)
|
||||||
raise OperationError(message % six.text_type(err))
|
raise OperationError(message % err)
|
||||||
|
|
||||||
# Make sure we store the PK on this document now that it's saved
|
# Make sure we store the PK on this document now that it's saved
|
||||||
id_field = self._meta["id_field"]
|
id_field = self._meta["id_field"]
|
||||||
@@ -559,7 +555,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
if not getattr(ref, "_changed_fields", True):
|
if not getattr(ref, "_changed_fields", True):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ref_id = "%s,%s" % (ref.__class__.__name__, str(ref._data))
|
ref_id = "{},{}".format(ref.__class__.__name__, str(ref._data))
|
||||||
if ref and ref_id not in _refs:
|
if ref and ref_id not in _refs:
|
||||||
_refs.append(ref_id)
|
_refs.append(ref_id)
|
||||||
kwargs["_refs"] = _refs
|
kwargs["_refs"] = _refs
|
||||||
@@ -634,7 +630,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
|
|
||||||
# Delete FileFields separately
|
# Delete FileFields separately
|
||||||
FileField = _import_class("FileField")
|
FileField = _import_class("FileField")
|
||||||
for name, field in iteritems(self._fields):
|
for name, field in self._fields.items():
|
||||||
if isinstance(field, FileField):
|
if isinstance(field, FileField):
|
||||||
getattr(self, name).delete()
|
getattr(self, name).delete()
|
||||||
|
|
||||||
@@ -643,7 +639,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
write_concern=write_concern, _from_doc_delete=True
|
write_concern=write_concern, _from_doc_delete=True
|
||||||
)
|
)
|
||||||
except pymongo.errors.OperationFailure as err:
|
except pymongo.errors.OperationFailure as err:
|
||||||
message = u"Could not delete document (%s)" % err.message
|
message = "Could not delete document (%s)" % err.message
|
||||||
raise OperationError(message)
|
raise OperationError(message)
|
||||||
signals.post_delete.send(self.__class__, document=self, **signal_kwargs)
|
signals.post_delete.send(self.__class__, document=self, **signal_kwargs)
|
||||||
|
|
||||||
@@ -851,17 +847,13 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
index_spec = cls._build_index_spec(keys)
|
index_spec = cls._build_index_spec(keys)
|
||||||
index_spec = index_spec.copy()
|
index_spec = index_spec.copy()
|
||||||
fields = index_spec.pop("fields")
|
fields = index_spec.pop("fields")
|
||||||
drop_dups = kwargs.get("drop_dups", False)
|
|
||||||
if drop_dups:
|
|
||||||
msg = "drop_dups is deprecated and is removed when using PyMongo 3+."
|
|
||||||
warnings.warn(msg, DeprecationWarning)
|
|
||||||
index_spec["background"] = background
|
index_spec["background"] = background
|
||||||
index_spec.update(kwargs)
|
index_spec.update(kwargs)
|
||||||
|
|
||||||
return cls._get_collection().create_index(fields, **index_spec)
|
return cls._get_collection().create_index(fields, **index_spec)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ensure_index(cls, key_or_list, drop_dups=False, background=False, **kwargs):
|
def ensure_index(cls, key_or_list, background=False, **kwargs):
|
||||||
"""Ensure that the given indexes are in place. Deprecated in favour
|
"""Ensure that the given indexes are in place. Deprecated in favour
|
||||||
of create_index.
|
of create_index.
|
||||||
|
|
||||||
@@ -869,12 +861,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
construct a multi-field index); keys may be prefixed with a **+**
|
construct a multi-field index); keys may be prefixed with a **+**
|
||||||
or a **-** to determine the index ordering
|
or a **-** to determine the index ordering
|
||||||
:param background: Allows index creation in the background
|
:param background: Allows index creation in the background
|
||||||
:param drop_dups: Was removed/ignored with MongoDB >2.7.5. The value
|
|
||||||
will be removed if PyMongo3+ is used
|
|
||||||
"""
|
"""
|
||||||
if drop_dups:
|
|
||||||
msg = "drop_dups is deprecated and is removed when using PyMongo 3+."
|
|
||||||
warnings.warn(msg, DeprecationWarning)
|
|
||||||
return cls.create_index(key_or_list, background=background, **kwargs)
|
return cls.create_index(key_or_list, background=background, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -887,12 +874,8 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
`auto_create_index` to False in the documents meta data
|
`auto_create_index` to False in the documents meta data
|
||||||
"""
|
"""
|
||||||
background = cls._meta.get("index_background", False)
|
background = cls._meta.get("index_background", False)
|
||||||
drop_dups = cls._meta.get("index_drop_dups", False)
|
|
||||||
index_opts = cls._meta.get("index_opts") or {}
|
index_opts = cls._meta.get("index_opts") or {}
|
||||||
index_cls = cls._meta.get("index_cls", True)
|
index_cls = cls._meta.get("index_cls", True)
|
||||||
if drop_dups:
|
|
||||||
msg = "drop_dups is deprecated and is removed when using PyMongo 3+."
|
|
||||||
warnings.warn(msg, DeprecationWarning)
|
|
||||||
|
|
||||||
collection = cls._get_collection()
|
collection = cls._get_collection()
|
||||||
# 746: when connection is via mongos, the read preference is not necessarily an indication that
|
# 746: when connection is via mongos, the read preference is not necessarily an indication that
|
||||||
@@ -992,10 +975,10 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
indexes.append(index)
|
indexes.append(index)
|
||||||
|
|
||||||
# finish up by appending { '_id': 1 } and { '_cls': 1 }, if needed
|
# finish up by appending { '_id': 1 } and { '_cls': 1 }, if needed
|
||||||
if [(u"_id", 1)] not in indexes:
|
if [("_id", 1)] not in indexes:
|
||||||
indexes.append([(u"_id", 1)])
|
indexes.append([("_id", 1)])
|
||||||
if cls._meta.get("index_cls", True) and cls._meta.get("allow_inheritance"):
|
if cls._meta.get("index_cls", True) and cls._meta.get("allow_inheritance"):
|
||||||
indexes.append([(u"_cls", 1)])
|
indexes.append([("_cls", 1)])
|
||||||
|
|
||||||
return indexes
|
return indexes
|
||||||
|
|
||||||
@@ -1019,19 +1002,19 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
extra = [index for index in existing if index not in required]
|
extra = [index for index in existing if index not in required]
|
||||||
|
|
||||||
# if { _cls: 1 } is missing, make sure it's *really* necessary
|
# if { _cls: 1 } is missing, make sure it's *really* necessary
|
||||||
if [(u"_cls", 1)] in missing:
|
if [("_cls", 1)] in missing:
|
||||||
cls_obsolete = False
|
cls_obsolete = False
|
||||||
for index in existing:
|
for index in existing:
|
||||||
if includes_cls(index) and index not in extra:
|
if includes_cls(index) and index not in extra:
|
||||||
cls_obsolete = True
|
cls_obsolete = True
|
||||||
break
|
break
|
||||||
if cls_obsolete:
|
if cls_obsolete:
|
||||||
missing.remove([(u"_cls", 1)])
|
missing.remove([("_cls", 1)])
|
||||||
|
|
||||||
return {"missing": missing, "extra": extra}
|
return {"missing": missing, "extra": extra}
|
||||||
|
|
||||||
|
|
||||||
class DynamicDocument(six.with_metaclass(TopLevelDocumentMetaclass, Document)):
|
class DynamicDocument(Document, metaclass=TopLevelDocumentMetaclass):
|
||||||
"""A Dynamic Document class allowing flexible, expandable and uncontrolled
|
"""A Dynamic Document class allowing flexible, expandable and uncontrolled
|
||||||
schemas. As a :class:`~mongoengine.Document` subclass, acts in the same
|
schemas. As a :class:`~mongoengine.Document` subclass, acts in the same
|
||||||
way as an ordinary document but has expanded style properties. Any data
|
way as an ordinary document but has expanded style properties. Any data
|
||||||
@@ -1045,7 +1028,6 @@ class DynamicDocument(six.with_metaclass(TopLevelDocumentMetaclass, Document)):
|
|||||||
There is one caveat on Dynamic Documents: undeclared fields cannot start with `_`
|
There is one caveat on Dynamic Documents: undeclared fields cannot start with `_`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The __metaclass__ attribute is removed by 2to3 when running with Python3
|
|
||||||
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
||||||
my_metaclass = TopLevelDocumentMetaclass
|
my_metaclass = TopLevelDocumentMetaclass
|
||||||
|
|
||||||
@@ -1060,16 +1042,15 @@ class DynamicDocument(six.with_metaclass(TopLevelDocumentMetaclass, Document)):
|
|||||||
setattr(self, field_name, None)
|
setattr(self, field_name, None)
|
||||||
self._dynamic_fields[field_name].null = False
|
self._dynamic_fields[field_name].null = False
|
||||||
else:
|
else:
|
||||||
super(DynamicDocument, self).__delattr__(*args, **kwargs)
|
super().__delattr__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class DynamicEmbeddedDocument(six.with_metaclass(DocumentMetaclass, EmbeddedDocument)):
|
class DynamicEmbeddedDocument(EmbeddedDocument, metaclass=DocumentMetaclass):
|
||||||
"""A Dynamic Embedded Document class allowing flexible, expandable and
|
"""A Dynamic Embedded Document class allowing flexible, expandable and
|
||||||
uncontrolled schemas. See :class:`~mongoengine.DynamicDocument` for more
|
uncontrolled schemas. See :class:`~mongoengine.DynamicDocument` for more
|
||||||
information about dynamic documents.
|
information about dynamic documents.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The __metaclass__ attribute is removed by 2to3 when running with Python3
|
|
||||||
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
# my_metaclass is defined so that metaclass can be queried in Python 2 & 3
|
||||||
my_metaclass = DocumentMetaclass
|
my_metaclass = DocumentMetaclass
|
||||||
|
|
||||||
@@ -1089,7 +1070,7 @@ class DynamicEmbeddedDocument(six.with_metaclass(DocumentMetaclass, EmbeddedDocu
|
|||||||
setattr(self, field_name, None)
|
setattr(self, field_name, None)
|
||||||
|
|
||||||
|
|
||||||
class MapReduceDocument(object):
|
class MapReduceDocument:
|
||||||
"""A document returned from a map/reduce query.
|
"""A document returned from a map/reduce query.
|
||||||
|
|
||||||
:param collection: An instance of :class:`~pymongo.Collection`
|
:param collection: An instance of :class:`~pymongo.Collection`
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
import six
|
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
"NotRegistered",
|
"NotRegistered",
|
||||||
@@ -87,24 +85,24 @@ class ValidationError(AssertionError):
|
|||||||
_message = None
|
_message = None
|
||||||
|
|
||||||
def __init__(self, message="", **kwargs):
|
def __init__(self, message="", **kwargs):
|
||||||
super(ValidationError, self).__init__(message)
|
super().__init__(message)
|
||||||
self.errors = kwargs.get("errors", {})
|
self.errors = kwargs.get("errors", {})
|
||||||
self.field_name = kwargs.get("field_name")
|
self.field_name = kwargs.get("field_name")
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return six.text_type(self.message)
|
return str(self.message)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%s,)" % (self.__class__.__name__, self.message)
|
return "{}({},)".format(self.__class__.__name__, self.message)
|
||||||
|
|
||||||
def __getattribute__(self, name):
|
def __getattribute__(self, name):
|
||||||
message = super(ValidationError, self).__getattribute__(name)
|
message = super().__getattribute__(name)
|
||||||
if name == "message":
|
if name == "message":
|
||||||
if self.field_name:
|
if self.field_name:
|
||||||
message = "%s" % message
|
message = "%s" % message
|
||||||
if self.errors:
|
if self.errors:
|
||||||
message = "%s(%s)" % (message, self._format_errors())
|
message = "{}({})".format(message, self._format_errors())
|
||||||
return message
|
return message
|
||||||
|
|
||||||
def _get_message(self):
|
def _get_message(self):
|
||||||
@@ -126,12 +124,12 @@ class ValidationError(AssertionError):
|
|||||||
def build_dict(source):
|
def build_dict(source):
|
||||||
errors_dict = {}
|
errors_dict = {}
|
||||||
if isinstance(source, dict):
|
if isinstance(source, dict):
|
||||||
for field_name, error in iteritems(source):
|
for field_name, error in source.items():
|
||||||
errors_dict[field_name] = build_dict(error)
|
errors_dict[field_name] = build_dict(error)
|
||||||
elif isinstance(source, ValidationError) and source.errors:
|
elif isinstance(source, ValidationError) and source.errors:
|
||||||
return build_dict(source.errors)
|
return build_dict(source.errors)
|
||||||
else:
|
else:
|
||||||
return six.text_type(source)
|
return str(source)
|
||||||
|
|
||||||
return errors_dict
|
return errors_dict
|
||||||
|
|
||||||
@@ -147,15 +145,15 @@ class ValidationError(AssertionError):
|
|||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = " ".join([generate_key(k) for k in value])
|
value = " ".join([generate_key(k) for k in value])
|
||||||
elif isinstance(value, dict):
|
elif isinstance(value, dict):
|
||||||
value = " ".join([generate_key(v, k) for k, v in iteritems(value)])
|
value = " ".join([generate_key(v, k) for k, v in value.items()])
|
||||||
|
|
||||||
results = "%s.%s" % (prefix, value) if prefix else value
|
results = "{}.{}".format(prefix, value) if prefix else value
|
||||||
return results
|
return results
|
||||||
|
|
||||||
error_dict = defaultdict(list)
|
error_dict = defaultdict(list)
|
||||||
for k, v in iteritems(self.to_dict()):
|
for k, v in self.to_dict().items():
|
||||||
error_dict[generate_key(v)].append(k)
|
error_dict[generate_key(v)].append(k)
|
||||||
return " ".join(["%s: %s" % (k, v) for k, v in iteritems(error_dict)])
|
return " ".join(["{}: {}".format(k, v) for k, v in error_dict.items()])
|
||||||
|
|
||||||
|
|
||||||
class DeprecatedError(Exception):
|
class DeprecatedError(Exception):
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import re
|
|||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
from io import BytesIO
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
from bson import Binary, DBRef, ObjectId, SON
|
from bson import Binary, DBRef, ObjectId, SON
|
||||||
|
from bson.int64 import Int64
|
||||||
import gridfs
|
import gridfs
|
||||||
import pymongo
|
import pymongo
|
||||||
from pymongo import ReturnDocument
|
from pymongo import ReturnDocument
|
||||||
import six
|
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import dateutil
|
import dateutil
|
||||||
@@ -21,11 +21,6 @@ except ImportError:
|
|||||||
else:
|
else:
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
|
|
||||||
try:
|
|
||||||
from bson.int64 import Int64
|
|
||||||
except ImportError:
|
|
||||||
Int64 = long
|
|
||||||
|
|
||||||
|
|
||||||
from mongoengine.base import (
|
from mongoengine.base import (
|
||||||
BaseDocument,
|
BaseDocument,
|
||||||
@@ -41,7 +36,7 @@ from mongoengine.common import _import_class
|
|||||||
from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db
|
from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db
|
||||||
from mongoengine.document import Document, EmbeddedDocument
|
from mongoengine.document import Document, EmbeddedDocument
|
||||||
from mongoengine.errors import DoesNotExist, InvalidQueryError, ValidationError
|
from mongoengine.errors import DoesNotExist, InvalidQueryError, ValidationError
|
||||||
from mongoengine.python_support import StringIO
|
from mongoengine.mongodb_support import MONGODB_36, get_mongodb_version
|
||||||
from mongoengine.queryset import DO_NOTHING
|
from mongoengine.queryset import DO_NOTHING
|
||||||
from mongoengine.queryset.base import BaseQuerySet
|
from mongoengine.queryset.base import BaseQuerySet
|
||||||
from mongoengine.queryset.transform import STRING_OPERATORS
|
from mongoengine.queryset.transform import STRING_OPERATORS
|
||||||
@@ -52,11 +47,6 @@ except ImportError:
|
|||||||
Image = None
|
Image = None
|
||||||
ImageOps = None
|
ImageOps = None
|
||||||
|
|
||||||
if six.PY3:
|
|
||||||
# Useless as long as 2to3 gets executed
|
|
||||||
# as it turns `long` into `int` blindly
|
|
||||||
long = int
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
"StringField",
|
"StringField",
|
||||||
@@ -113,10 +103,10 @@ class StringField(BaseField):
|
|||||||
self.regex = re.compile(regex) if regex else None
|
self.regex = re.compile(regex) if regex else None
|
||||||
self.max_length = max_length
|
self.max_length = max_length
|
||||||
self.min_length = min_length
|
self.min_length = min_length
|
||||||
super(StringField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if isinstance(value, six.text_type):
|
if isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
try:
|
try:
|
||||||
value = value.decode("utf-8")
|
value = value.decode("utf-8")
|
||||||
@@ -125,7 +115,7 @@ class StringField(BaseField):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if not isinstance(value, six.string_types):
|
if not isinstance(value, str):
|
||||||
self.error("StringField only accepts string values")
|
self.error("StringField only accepts string values")
|
||||||
|
|
||||||
if self.max_length is not None and len(value) > self.max_length:
|
if self.max_length is not None and len(value) > self.max_length:
|
||||||
@@ -141,7 +131,7 @@ class StringField(BaseField):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
if not isinstance(op, six.string_types):
|
if not isinstance(op, str):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
if op in STRING_OPERATORS:
|
if op in STRING_OPERATORS:
|
||||||
@@ -161,7 +151,7 @@ class StringField(BaseField):
|
|||||||
# escape unsafe characters which could lead to a re.error
|
# escape unsafe characters which could lead to a re.error
|
||||||
value = re.escape(value)
|
value = re.escape(value)
|
||||||
value = re.compile(regex % value, flags)
|
value = re.compile(regex % value, flags)
|
||||||
return super(StringField, self).prepare_query_value(op, value)
|
return super().prepare_query_value(op, value)
|
||||||
|
|
||||||
|
|
||||||
class URLField(StringField):
|
class URLField(StringField):
|
||||||
@@ -185,17 +175,17 @@ class URLField(StringField):
|
|||||||
def __init__(self, url_regex=None, schemes=None, **kwargs):
|
def __init__(self, url_regex=None, schemes=None, **kwargs):
|
||||||
self.url_regex = url_regex or self._URL_REGEX
|
self.url_regex = url_regex or self._URL_REGEX
|
||||||
self.schemes = schemes or self._URL_SCHEMES
|
self.schemes = schemes or self._URL_SCHEMES
|
||||||
super(URLField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
# Check first if the scheme is valid
|
# Check first if the scheme is valid
|
||||||
scheme = value.split("://")[0].lower()
|
scheme = value.split("://")[0].lower()
|
||||||
if scheme not in self.schemes:
|
if scheme not in self.schemes:
|
||||||
self.error(u"Invalid scheme {} in URL: {}".format(scheme, value))
|
self.error("Invalid scheme {} in URL: {}".format(scheme, value))
|
||||||
|
|
||||||
# Then check full URL
|
# Then check full URL
|
||||||
if not self.url_regex.match(value):
|
if not self.url_regex.match(value):
|
||||||
self.error(u"Invalid URL: {}".format(value))
|
self.error("Invalid URL: {}".format(value))
|
||||||
|
|
||||||
|
|
||||||
class EmailField(StringField):
|
class EmailField(StringField):
|
||||||
@@ -213,7 +203,7 @@ class EmailField(StringField):
|
|||||||
)
|
)
|
||||||
|
|
||||||
UTF8_USER_REGEX = LazyRegexCompiler(
|
UTF8_USER_REGEX = LazyRegexCompiler(
|
||||||
six.u(
|
(
|
||||||
# RFC 6531 Section 3.3 extends `atext` (used by dot-atom) to
|
# RFC 6531 Section 3.3 extends `atext` (used by dot-atom) to
|
||||||
# include `UTF8-non-ascii`.
|
# include `UTF8-non-ascii`.
|
||||||
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z\u0080-\U0010FFFF]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z\u0080-\U0010FFFF]+)*\Z"
|
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z\u0080-\U0010FFFF]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z\u0080-\U0010FFFF]+)*\Z"
|
||||||
@@ -228,7 +218,7 @@ class EmailField(StringField):
|
|||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
)
|
)
|
||||||
|
|
||||||
error_msg = u"Invalid email address: %s"
|
error_msg = "Invalid email address: %s"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -252,7 +242,7 @@ class EmailField(StringField):
|
|||||||
self.domain_whitelist = domain_whitelist or []
|
self.domain_whitelist = domain_whitelist or []
|
||||||
self.allow_utf8_user = allow_utf8_user
|
self.allow_utf8_user = allow_utf8_user
|
||||||
self.allow_ip_domain = allow_ip_domain
|
self.allow_ip_domain = allow_ip_domain
|
||||||
super(EmailField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def validate_user_part(self, user_part):
|
def validate_user_part(self, user_part):
|
||||||
"""Validate the user part of the email address. Return True if
|
"""Validate the user part of the email address. Return True if
|
||||||
@@ -279,13 +269,13 @@ class EmailField(StringField):
|
|||||||
try:
|
try:
|
||||||
socket.inet_pton(addr_family, domain_part[1:-1])
|
socket.inet_pton(addr_family, domain_part[1:-1])
|
||||||
return True
|
return True
|
||||||
except (socket.error, UnicodeEncodeError):
|
except (OSError, UnicodeEncodeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
super(EmailField, self).validate(value)
|
super().validate(value)
|
||||||
|
|
||||||
if "@" not in value:
|
if "@" not in value:
|
||||||
self.error(self.error_msg % value)
|
self.error(self.error_msg % value)
|
||||||
@@ -302,12 +292,16 @@ class EmailField(StringField):
|
|||||||
domain_part = domain_part.encode("idna").decode("ascii")
|
domain_part = domain_part.encode("idna").decode("ascii")
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
self.error(
|
self.error(
|
||||||
"%s %s" % (self.error_msg % value, "(domain failed IDN encoding)")
|
"{} {}".format(
|
||||||
|
self.error_msg % value, "(domain failed IDN encoding)"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if not self.validate_domain_part(domain_part):
|
if not self.validate_domain_part(domain_part):
|
||||||
self.error(
|
self.error(
|
||||||
"%s %s" % (self.error_msg % value, "(domain validation failed)")
|
"{} {}".format(
|
||||||
|
self.error_msg % value, "(domain validation failed)"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -316,7 +310,7 @@ class IntField(BaseField):
|
|||||||
|
|
||||||
def __init__(self, min_value=None, max_value=None, **kwargs):
|
def __init__(self, min_value=None, max_value=None, **kwargs):
|
||||||
self.min_value, self.max_value = min_value, max_value
|
self.min_value, self.max_value = min_value, max_value
|
||||||
super(IntField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
try:
|
try:
|
||||||
@@ -341,19 +335,19 @@ class IntField(BaseField):
|
|||||||
if value is None:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
return super(IntField, self).prepare_query_value(op, int(value))
|
return super().prepare_query_value(op, int(value))
|
||||||
|
|
||||||
|
|
||||||
class LongField(BaseField):
|
class LongField(BaseField):
|
||||||
"""64-bit integer field."""
|
"""64-bit integer field. (Equivalent to IntField since the support to Python2 was dropped)"""
|
||||||
|
|
||||||
def __init__(self, min_value=None, max_value=None, **kwargs):
|
def __init__(self, min_value=None, max_value=None, **kwargs):
|
||||||
self.min_value, self.max_value = min_value, max_value
|
self.min_value, self.max_value = min_value, max_value
|
||||||
super(LongField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
try:
|
try:
|
||||||
value = long(value)
|
value = int(value)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
return value
|
return value
|
||||||
@@ -363,7 +357,7 @@ class LongField(BaseField):
|
|||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
try:
|
try:
|
||||||
value = long(value)
|
value = int(value)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
self.error("%s could not be converted to long" % value)
|
self.error("%s could not be converted to long" % value)
|
||||||
|
|
||||||
@@ -377,7 +371,7 @@ class LongField(BaseField):
|
|||||||
if value is None:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
return super(LongField, self).prepare_query_value(op, long(value))
|
return super().prepare_query_value(op, int(value))
|
||||||
|
|
||||||
|
|
||||||
class FloatField(BaseField):
|
class FloatField(BaseField):
|
||||||
@@ -385,7 +379,7 @@ class FloatField(BaseField):
|
|||||||
|
|
||||||
def __init__(self, min_value=None, max_value=None, **kwargs):
|
def __init__(self, min_value=None, max_value=None, **kwargs):
|
||||||
self.min_value, self.max_value = min_value, max_value
|
self.min_value, self.max_value = min_value, max_value
|
||||||
super(FloatField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
try:
|
try:
|
||||||
@@ -395,7 +389,7 @@ class FloatField(BaseField):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if isinstance(value, six.integer_types):
|
if isinstance(value, int):
|
||||||
try:
|
try:
|
||||||
value = float(value)
|
value = float(value)
|
||||||
except OverflowError:
|
except OverflowError:
|
||||||
@@ -414,7 +408,7 @@ class FloatField(BaseField):
|
|||||||
if value is None:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
return super(FloatField, self).prepare_query_value(op, float(value))
|
return super().prepare_query_value(op, float(value))
|
||||||
|
|
||||||
|
|
||||||
class DecimalField(BaseField):
|
class DecimalField(BaseField):
|
||||||
@@ -461,7 +455,7 @@ class DecimalField(BaseField):
|
|||||||
self.precision = precision
|
self.precision = precision
|
||||||
self.rounding = rounding
|
self.rounding = rounding
|
||||||
|
|
||||||
super(DecimalField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -480,13 +474,13 @@ class DecimalField(BaseField):
|
|||||||
if value is None:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
if self.force_string:
|
if self.force_string:
|
||||||
return six.text_type(self.to_python(value))
|
return str(self.to_python(value))
|
||||||
return float(self.to_python(value))
|
return float(self.to_python(value))
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if not isinstance(value, decimal.Decimal):
|
if not isinstance(value, decimal.Decimal):
|
||||||
if not isinstance(value, six.string_types):
|
if not isinstance(value, str):
|
||||||
value = six.text_type(value)
|
value = str(value)
|
||||||
try:
|
try:
|
||||||
value = decimal.Decimal(value)
|
value = decimal.Decimal(value)
|
||||||
except (TypeError, ValueError, decimal.InvalidOperation) as exc:
|
except (TypeError, ValueError, decimal.InvalidOperation) as exc:
|
||||||
@@ -499,7 +493,7 @@ class DecimalField(BaseField):
|
|||||||
self.error("Decimal value is too large")
|
self.error("Decimal value is too large")
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
return super(DecimalField, self).prepare_query_value(op, self.to_mongo(value))
|
return super().prepare_query_value(op, self.to_mongo(value))
|
||||||
|
|
||||||
|
|
||||||
class BooleanField(BaseField):
|
class BooleanField(BaseField):
|
||||||
@@ -539,7 +533,7 @@ class DateTimeField(BaseField):
|
|||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
new_value = self.to_mongo(value)
|
new_value = self.to_mongo(value)
|
||||||
if not isinstance(new_value, (datetime.datetime, datetime.date)):
|
if not isinstance(new_value, (datetime.datetime, datetime.date)):
|
||||||
self.error(u'cannot parse date "%s"' % value)
|
self.error('cannot parse date "%s"' % value)
|
||||||
|
|
||||||
def to_mongo(self, value):
|
def to_mongo(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -551,7 +545,7 @@ class DateTimeField(BaseField):
|
|||||||
if callable(value):
|
if callable(value):
|
||||||
return value()
|
return value()
|
||||||
|
|
||||||
if not isinstance(value, six.string_types):
|
if not isinstance(value, str):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self._parse_datetime(value)
|
return self._parse_datetime(value)
|
||||||
@@ -596,19 +590,19 @@ class DateTimeField(BaseField):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
return super(DateTimeField, self).prepare_query_value(op, self.to_mongo(value))
|
return super().prepare_query_value(op, self.to_mongo(value))
|
||||||
|
|
||||||
|
|
||||||
class DateField(DateTimeField):
|
class DateField(DateTimeField):
|
||||||
def to_mongo(self, value):
|
def to_mongo(self, value):
|
||||||
value = super(DateField, self).to_mongo(value)
|
value = super().to_mongo(value)
|
||||||
# drop hours, minutes, seconds
|
# drop hours, minutes, seconds
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
value = datetime.datetime(value.year, value.month, value.day)
|
value = datetime.datetime(value.year, value.month, value.day)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
value = super(DateField, self).to_python(value)
|
value = super().to_python(value)
|
||||||
# convert datetime to date
|
# convert datetime to date
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
value = datetime.date(value.year, value.month, value.day)
|
value = datetime.date(value.year, value.month, value.day)
|
||||||
@@ -642,7 +636,7 @@ class ComplexDateTimeField(StringField):
|
|||||||
"""
|
"""
|
||||||
self.separator = separator
|
self.separator = separator
|
||||||
self.format = separator.join(["%Y", "%m", "%d", "%H", "%M", "%S", "%f"])
|
self.format = separator.join(["%Y", "%m", "%d", "%H", "%M", "%S", "%f"])
|
||||||
super(ComplexDateTimeField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def _convert_from_datetime(self, val):
|
def _convert_from_datetime(self, val):
|
||||||
"""
|
"""
|
||||||
@@ -673,17 +667,20 @@ class ComplexDateTimeField(StringField):
|
|||||||
if instance is None:
|
if instance is None:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
data = super(ComplexDateTimeField, self).__get__(instance, owner)
|
data = super().__get__(instance, owner)
|
||||||
|
|
||||||
if isinstance(data, datetime.datetime) or data is None:
|
if isinstance(data, datetime.datetime) or data is None:
|
||||||
return data
|
return data
|
||||||
return self._convert_from_string(data)
|
return self._convert_from_string(data)
|
||||||
|
|
||||||
def __set__(self, instance, value):
|
def __set__(self, instance, value):
|
||||||
super(ComplexDateTimeField, self).__set__(instance, value)
|
super().__set__(instance, value)
|
||||||
value = instance._data[self.name]
|
value = instance._data[self.name]
|
||||||
if value is not None:
|
if value is not None:
|
||||||
instance._data[self.name] = self._convert_from_datetime(value)
|
if isinstance(value, datetime.datetime):
|
||||||
|
instance._data[self.name] = self._convert_from_datetime(value)
|
||||||
|
else:
|
||||||
|
instance._data[self.name] = value
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
value = self.to_python(value)
|
value = self.to_python(value)
|
||||||
@@ -702,9 +699,7 @@ class ComplexDateTimeField(StringField):
|
|||||||
return self._convert_from_datetime(value)
|
return self._convert_from_datetime(value)
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
return super(ComplexDateTimeField, self).prepare_query_value(
|
return super().prepare_query_value(op, self._convert_from_datetime(value))
|
||||||
op, self._convert_from_datetime(value)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class EmbeddedDocumentField(BaseField):
|
class EmbeddedDocumentField(BaseField):
|
||||||
@@ -715,7 +710,7 @@ class EmbeddedDocumentField(BaseField):
|
|||||||
def __init__(self, document_type, **kwargs):
|
def __init__(self, document_type, **kwargs):
|
||||||
# XXX ValidationError raised outside of the "validate" method.
|
# XXX ValidationError raised outside of the "validate" method.
|
||||||
if not (
|
if not (
|
||||||
isinstance(document_type, six.string_types)
|
isinstance(document_type, str)
|
||||||
or issubclass(document_type, EmbeddedDocument)
|
or issubclass(document_type, EmbeddedDocument)
|
||||||
):
|
):
|
||||||
self.error(
|
self.error(
|
||||||
@@ -724,11 +719,11 @@ class EmbeddedDocumentField(BaseField):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.document_type_obj = document_type
|
self.document_type_obj = document_type
|
||||||
super(EmbeddedDocumentField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def document_type(self):
|
def document_type(self):
|
||||||
if isinstance(self.document_type_obj, six.string_types):
|
if isinstance(self.document_type_obj, str):
|
||||||
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
||||||
resolved_document_type = self.owner_document
|
resolved_document_type = self.owner_document
|
||||||
else:
|
else:
|
||||||
@@ -785,7 +780,7 @@ class EmbeddedDocumentField(BaseField):
|
|||||||
"Querying the embedded document '%s' failed, due to an invalid query value"
|
"Querying the embedded document '%s' failed, due to an invalid query value"
|
||||||
% (self.document_type._class_name,)
|
% (self.document_type._class_name,)
|
||||||
)
|
)
|
||||||
super(EmbeddedDocumentField, self).prepare_query_value(op, value)
|
super().prepare_query_value(op, value)
|
||||||
return self.to_mongo(value)
|
return self.to_mongo(value)
|
||||||
|
|
||||||
|
|
||||||
@@ -801,9 +796,7 @@ class GenericEmbeddedDocumentField(BaseField):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
return super(GenericEmbeddedDocumentField, self).prepare_query_value(
|
return super().prepare_query_value(op, self.to_mongo(value))
|
||||||
op, self.to_mongo(value)
|
|
||||||
)
|
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
@@ -854,7 +847,7 @@ class DynamicField(BaseField):
|
|||||||
"""Convert a Python type to a MongoDB compatible type.
|
"""Convert a Python type to a MongoDB compatible type.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(value, six.string_types):
|
if isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
if hasattr(value, "to_mongo"):
|
if hasattr(value, "to_mongo"):
|
||||||
@@ -876,12 +869,12 @@ class DynamicField(BaseField):
|
|||||||
value = {k: v for k, v in enumerate(value)}
|
value = {k: v for k, v in enumerate(value)}
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
for k, v in iteritems(value):
|
for k, v in value.items():
|
||||||
data[k] = self.to_mongo(v, use_db_field, fields)
|
data[k] = self.to_mongo(v, use_db_field, fields)
|
||||||
|
|
||||||
value = data
|
value = data
|
||||||
if is_list: # Convert back to a list
|
if is_list: # Convert back to a list
|
||||||
value = [v for k, v in sorted(iteritems(data), key=itemgetter(0))]
|
value = [v for k, v in sorted(data.items(), key=itemgetter(0))]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
@@ -891,15 +884,15 @@ class DynamicField(BaseField):
|
|||||||
value = doc_cls._get_db().dereference(value["_ref"])
|
value = doc_cls._get_db().dereference(value["_ref"])
|
||||||
return doc_cls._from_son(value)
|
return doc_cls._from_son(value)
|
||||||
|
|
||||||
return super(DynamicField, self).to_python(value)
|
return super().to_python(value)
|
||||||
|
|
||||||
def lookup_member(self, member_name):
|
def lookup_member(self, member_name):
|
||||||
return member_name
|
return member_name
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
if isinstance(value, six.string_types):
|
if isinstance(value, str):
|
||||||
return StringField().prepare_query_value(op, value)
|
return StringField().prepare_query_value(op, value)
|
||||||
return super(DynamicField, self).prepare_query_value(op, self.to_mongo(value))
|
return super().prepare_query_value(op, self.to_mongo(value))
|
||||||
|
|
||||||
def validate(self, value, clean=True):
|
def validate(self, value, clean=True):
|
||||||
if hasattr(value, "validate"):
|
if hasattr(value, "validate"):
|
||||||
@@ -920,7 +913,7 @@ class ListField(ComplexBaseField):
|
|||||||
self.field = field
|
self.field = field
|
||||||
self.max_length = max_length
|
self.max_length = max_length
|
||||||
kwargs.setdefault("default", lambda: [])
|
kwargs.setdefault("default", lambda: [])
|
||||||
super(ListField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
if instance is None:
|
if instance is None:
|
||||||
@@ -934,7 +927,7 @@ class ListField(ComplexBaseField):
|
|||||||
and value
|
and value
|
||||||
):
|
):
|
||||||
instance._data[self.name] = [self.field.build_lazyref(x) for x in value]
|
instance._data[self.name] = [self.field.build_lazyref(x) for x in value]
|
||||||
return super(ListField, self).__get__(instance, owner)
|
return super().__get__(instance, owner)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
"""Make sure that a list of valid fields is being used."""
|
"""Make sure that a list of valid fields is being used."""
|
||||||
@@ -948,7 +941,7 @@ class ListField(ComplexBaseField):
|
|||||||
if self.max_length is not None and len(value) > self.max_length:
|
if self.max_length is not None and len(value) > self.max_length:
|
||||||
self.error("List is too long")
|
self.error("List is too long")
|
||||||
|
|
||||||
super(ListField, self).validate(value)
|
super().validate(value)
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
# Validate that the `set` operator doesn't contain more items than `max_length`.
|
# Validate that the `set` operator doesn't contain more items than `max_length`.
|
||||||
@@ -962,14 +955,14 @@ class ListField(ComplexBaseField):
|
|||||||
if (
|
if (
|
||||||
op in ("set", "unset", None)
|
op in ("set", "unset", None)
|
||||||
and hasattr(value, "__iter__")
|
and hasattr(value, "__iter__")
|
||||||
and not isinstance(value, six.string_types)
|
and not isinstance(value, str)
|
||||||
and not isinstance(value, BaseDocument)
|
and not isinstance(value, BaseDocument)
|
||||||
):
|
):
|
||||||
return [self.field.prepare_query_value(op, v) for v in value]
|
return [self.field.prepare_query_value(op, v) for v in value]
|
||||||
|
|
||||||
return self.field.prepare_query_value(op, value)
|
return self.field.prepare_query_value(op, value)
|
||||||
|
|
||||||
return super(ListField, self).prepare_query_value(op, value)
|
return super().prepare_query_value(op, value)
|
||||||
|
|
||||||
|
|
||||||
class EmbeddedDocumentListField(ListField):
|
class EmbeddedDocumentListField(ListField):
|
||||||
@@ -990,9 +983,7 @@ class EmbeddedDocumentListField(ListField):
|
|||||||
:param kwargs: Keyword arguments passed directly into the parent
|
:param kwargs: Keyword arguments passed directly into the parent
|
||||||
:class:`~mongoengine.ListField`.
|
:class:`~mongoengine.ListField`.
|
||||||
"""
|
"""
|
||||||
super(EmbeddedDocumentListField, self).__init__(
|
super().__init__(field=EmbeddedDocumentField(document_type), **kwargs)
|
||||||
field=EmbeddedDocumentField(document_type), **kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SortedListField(ListField):
|
class SortedListField(ListField):
|
||||||
@@ -1018,10 +1009,10 @@ class SortedListField(ListField):
|
|||||||
self._ordering = kwargs.pop("ordering")
|
self._ordering = kwargs.pop("ordering")
|
||||||
if "reverse" in kwargs.keys():
|
if "reverse" in kwargs.keys():
|
||||||
self._order_reverse = kwargs.pop("reverse")
|
self._order_reverse = kwargs.pop("reverse")
|
||||||
super(SortedListField, self).__init__(field, **kwargs)
|
super().__init__(field, **kwargs)
|
||||||
|
|
||||||
def to_mongo(self, value, use_db_field=True, fields=None):
|
def to_mongo(self, value, use_db_field=True, fields=None):
|
||||||
value = super(SortedListField, self).to_mongo(value, use_db_field, fields)
|
value = super().to_mongo(value, use_db_field, fields)
|
||||||
if self._ordering is not None:
|
if self._ordering is not None:
|
||||||
return sorted(
|
return sorted(
|
||||||
value, key=itemgetter(self._ordering), reverse=self._order_reverse
|
value, key=itemgetter(self._ordering), reverse=self._order_reverse
|
||||||
@@ -1034,9 +1025,7 @@ def key_not_string(d):
|
|||||||
dictionary is not a string.
|
dictionary is not a string.
|
||||||
"""
|
"""
|
||||||
for k, v in d.items():
|
for k, v in d.items():
|
||||||
if not isinstance(k, six.string_types) or (
|
if not isinstance(k, str) or (isinstance(v, dict) and key_not_string(v)):
|
||||||
isinstance(v, dict) and key_not_string(v)
|
|
||||||
):
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -1051,6 +1040,15 @@ def key_has_dot_or_dollar(d):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def key_starts_with_dollar(d):
|
||||||
|
"""Helper function to recursively determine if any key in a
|
||||||
|
dictionary starts with a dollar
|
||||||
|
"""
|
||||||
|
for k, v in d.items():
|
||||||
|
if (k.startswith("$")) or (isinstance(v, dict) and key_starts_with_dollar(v)):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class DictField(ComplexBaseField):
|
class DictField(ComplexBaseField):
|
||||||
"""A dictionary field that wraps a standard Python dictionary. This is
|
"""A dictionary field that wraps a standard Python dictionary. This is
|
||||||
similar to an embedded document, but the structure is not defined.
|
similar to an embedded document, but the structure is not defined.
|
||||||
@@ -1067,7 +1065,7 @@ class DictField(ComplexBaseField):
|
|||||||
self._auto_dereference = False
|
self._auto_dereference = False
|
||||||
|
|
||||||
kwargs.setdefault("default", lambda: {})
|
kwargs.setdefault("default", lambda: {})
|
||||||
super(DictField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
"""Make sure that a list of valid fields is being used."""
|
"""Make sure that a list of valid fields is being used."""
|
||||||
@@ -1077,12 +1075,17 @@ class DictField(ComplexBaseField):
|
|||||||
if key_not_string(value):
|
if key_not_string(value):
|
||||||
msg = "Invalid dictionary key - documents must have only string keys"
|
msg = "Invalid dictionary key - documents must have only string keys"
|
||||||
self.error(msg)
|
self.error(msg)
|
||||||
if key_has_dot_or_dollar(value):
|
|
||||||
|
# Following condition applies to MongoDB >= 3.6
|
||||||
|
# older Mongo has stricter constraints but
|
||||||
|
# it will be rejected upon insertion anyway
|
||||||
|
# Having a validation that depends on the MongoDB version
|
||||||
|
# is not straightforward as the field isn't aware of the connected Mongo
|
||||||
|
if key_starts_with_dollar(value):
|
||||||
self.error(
|
self.error(
|
||||||
'Invalid dictionary key name - keys may not contain "."'
|
'Invalid dictionary key name - keys may not startswith "$" characters'
|
||||||
' or startswith "$" characters'
|
|
||||||
)
|
)
|
||||||
super(DictField, self).validate(value)
|
super().validate(value)
|
||||||
|
|
||||||
def lookup_member(self, member_name):
|
def lookup_member(self, member_name):
|
||||||
return DictField(db_field=member_name)
|
return DictField(db_field=member_name)
|
||||||
@@ -1099,7 +1102,7 @@ class DictField(ComplexBaseField):
|
|||||||
"iexact",
|
"iexact",
|
||||||
]
|
]
|
||||||
|
|
||||||
if op in match_operators and isinstance(value, six.string_types):
|
if op in match_operators and isinstance(value, str):
|
||||||
return StringField().prepare_query_value(op, value)
|
return StringField().prepare_query_value(op, value)
|
||||||
|
|
||||||
if hasattr(
|
if hasattr(
|
||||||
@@ -1111,7 +1114,7 @@ class DictField(ComplexBaseField):
|
|||||||
}
|
}
|
||||||
return self.field.prepare_query_value(op, value)
|
return self.field.prepare_query_value(op, value)
|
||||||
|
|
||||||
return super(DictField, self).prepare_query_value(op, value)
|
return super().prepare_query_value(op, value)
|
||||||
|
|
||||||
|
|
||||||
class MapField(DictField):
|
class MapField(DictField):
|
||||||
@@ -1126,7 +1129,7 @@ class MapField(DictField):
|
|||||||
# XXX ValidationError raised outside of the "validate" method.
|
# XXX ValidationError raised outside of the "validate" method.
|
||||||
if not isinstance(field, BaseField):
|
if not isinstance(field, BaseField):
|
||||||
self.error("Argument to MapField constructor must be a valid field")
|
self.error("Argument to MapField constructor must be a valid field")
|
||||||
super(MapField, self).__init__(field=field, *args, **kwargs)
|
super().__init__(field=field, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ReferenceField(BaseField):
|
class ReferenceField(BaseField):
|
||||||
@@ -1186,7 +1189,7 @@ class ReferenceField(BaseField):
|
|||||||
:class:`~pymongo.dbref.DBRef`, regardless of the value of `dbref`.
|
:class:`~pymongo.dbref.DBRef`, regardless of the value of `dbref`.
|
||||||
"""
|
"""
|
||||||
# XXX ValidationError raised outside of the "validate" method.
|
# XXX ValidationError raised outside of the "validate" method.
|
||||||
if not isinstance(document_type, six.string_types) and not issubclass(
|
if not isinstance(document_type, str) and not issubclass(
|
||||||
document_type, Document
|
document_type, Document
|
||||||
):
|
):
|
||||||
self.error(
|
self.error(
|
||||||
@@ -1197,11 +1200,11 @@ class ReferenceField(BaseField):
|
|||||||
self.dbref = dbref
|
self.dbref = dbref
|
||||||
self.document_type_obj = document_type
|
self.document_type_obj = document_type
|
||||||
self.reverse_delete_rule = reverse_delete_rule
|
self.reverse_delete_rule = reverse_delete_rule
|
||||||
super(ReferenceField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def document_type(self):
|
def document_type(self):
|
||||||
if isinstance(self.document_type_obj, six.string_types):
|
if isinstance(self.document_type_obj, str):
|
||||||
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
||||||
self.document_type_obj = self.owner_document
|
self.document_type_obj = self.owner_document
|
||||||
else:
|
else:
|
||||||
@@ -1230,7 +1233,7 @@ class ReferenceField(BaseField):
|
|||||||
else:
|
else:
|
||||||
instance._data[self.name] = cls._from_son(dereferenced)
|
instance._data[self.name] = cls._from_son(dereferenced)
|
||||||
|
|
||||||
return super(ReferenceField, self).__get__(instance, owner)
|
return super().__get__(instance, owner)
|
||||||
|
|
||||||
def to_mongo(self, document):
|
def to_mongo(self, document):
|
||||||
if isinstance(document, DBRef):
|
if isinstance(document, DBRef):
|
||||||
@@ -1281,7 +1284,7 @@ class ReferenceField(BaseField):
|
|||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
super(ReferenceField, self).prepare_query_value(op, value)
|
super().prepare_query_value(op, value)
|
||||||
return self.to_mongo(value)
|
return self.to_mongo(value)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
@@ -1317,7 +1320,7 @@ class CachedReferenceField(BaseField):
|
|||||||
fields = []
|
fields = []
|
||||||
|
|
||||||
# XXX ValidationError raised outside of the "validate" method.
|
# XXX ValidationError raised outside of the "validate" method.
|
||||||
if not isinstance(document_type, six.string_types) and not issubclass(
|
if not isinstance(document_type, str) and not issubclass(
|
||||||
document_type, Document
|
document_type, Document
|
||||||
):
|
):
|
||||||
self.error(
|
self.error(
|
||||||
@@ -1328,7 +1331,7 @@ class CachedReferenceField(BaseField):
|
|||||||
self.auto_sync = auto_sync
|
self.auto_sync = auto_sync
|
||||||
self.document_type_obj = document_type
|
self.document_type_obj = document_type
|
||||||
self.fields = fields
|
self.fields = fields
|
||||||
super(CachedReferenceField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def start_listener(self):
|
def start_listener(self):
|
||||||
from mongoengine import signals
|
from mongoengine import signals
|
||||||
@@ -1340,7 +1343,7 @@ class CachedReferenceField(BaseField):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
update_kwargs = {
|
update_kwargs = {
|
||||||
"set__%s__%s" % (self.name, key): val
|
"set__{}__{}".format(self.name, key): val
|
||||||
for key, val in document._delta()[0].items()
|
for key, val in document._delta()[0].items()
|
||||||
if key in self.fields
|
if key in self.fields
|
||||||
}
|
}
|
||||||
@@ -1362,7 +1365,7 @@ class CachedReferenceField(BaseField):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def document_type(self):
|
def document_type(self):
|
||||||
if isinstance(self.document_type_obj, six.string_types):
|
if isinstance(self.document_type_obj, str):
|
||||||
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
||||||
self.document_type_obj = self.owner_document
|
self.document_type_obj = self.owner_document
|
||||||
else:
|
else:
|
||||||
@@ -1386,7 +1389,7 @@ class CachedReferenceField(BaseField):
|
|||||||
else:
|
else:
|
||||||
instance._data[self.name] = self.document_type._from_son(dereferenced)
|
instance._data[self.name] = self.document_type._from_son(dereferenced)
|
||||||
|
|
||||||
return super(CachedReferenceField, self).__get__(instance, owner)
|
return super().__get__(instance, owner)
|
||||||
|
|
||||||
def to_mongo(self, document, use_db_field=True, fields=None):
|
def to_mongo(self, document, use_db_field=True, fields=None):
|
||||||
id_field_name = self.document_type._meta["id_field"]
|
id_field_name = self.document_type._meta["id_field"]
|
||||||
@@ -1485,12 +1488,12 @@ class GenericReferenceField(BaseField):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
choices = kwargs.pop("choices", None)
|
choices = kwargs.pop("choices", None)
|
||||||
super(GenericReferenceField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.choices = []
|
self.choices = []
|
||||||
# Keep the choices as a list of allowed Document class names
|
# Keep the choices as a list of allowed Document class names
|
||||||
if choices:
|
if choices:
|
||||||
for choice in choices:
|
for choice in choices:
|
||||||
if isinstance(choice, six.string_types):
|
if isinstance(choice, str):
|
||||||
self.choices.append(choice)
|
self.choices.append(choice)
|
||||||
elif isinstance(choice, type) and issubclass(choice, Document):
|
elif isinstance(choice, type) and issubclass(choice, Document):
|
||||||
self.choices.append(choice._class_name)
|
self.choices.append(choice._class_name)
|
||||||
@@ -1499,7 +1502,7 @@ class GenericReferenceField(BaseField):
|
|||||||
# method.
|
# method.
|
||||||
self.error(
|
self.error(
|
||||||
"Invalid choices provided: must be a list of"
|
"Invalid choices provided: must be a list of"
|
||||||
"Document subclasses and/or six.string_typess"
|
"Document subclasses and/or str"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _validate_choices(self, value):
|
def _validate_choices(self, value):
|
||||||
@@ -1509,7 +1512,7 @@ class GenericReferenceField(BaseField):
|
|||||||
value = value.get("_cls")
|
value = value.get("_cls")
|
||||||
elif isinstance(value, Document):
|
elif isinstance(value, Document):
|
||||||
value = value._class_name
|
value = value._class_name
|
||||||
super(GenericReferenceField, self)._validate_choices(value)
|
super()._validate_choices(value)
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
if instance is None:
|
if instance is None:
|
||||||
@@ -1525,7 +1528,7 @@ class GenericReferenceField(BaseField):
|
|||||||
else:
|
else:
|
||||||
instance._data[self.name] = dereferenced
|
instance._data[self.name] = dereferenced
|
||||||
|
|
||||||
return super(GenericReferenceField, self).__get__(instance, owner)
|
return super().__get__(instance, owner)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if not isinstance(value, (Document, DBRef, dict, SON)):
|
if not isinstance(value, (Document, DBRef, dict, SON)):
|
||||||
@@ -1589,22 +1592,22 @@ class BinaryField(BaseField):
|
|||||||
|
|
||||||
def __init__(self, max_bytes=None, **kwargs):
|
def __init__(self, max_bytes=None, **kwargs):
|
||||||
self.max_bytes = max_bytes
|
self.max_bytes = max_bytes
|
||||||
super(BinaryField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def __set__(self, instance, value):
|
def __set__(self, instance, value):
|
||||||
"""Handle bytearrays in python 3.1"""
|
"""Handle bytearrays in python 3.1"""
|
||||||
if six.PY3 and isinstance(value, bytearray):
|
if isinstance(value, bytearray):
|
||||||
value = six.binary_type(value)
|
value = bytes(value)
|
||||||
return super(BinaryField, self).__set__(instance, value)
|
return super().__set__(instance, value)
|
||||||
|
|
||||||
def to_mongo(self, value):
|
def to_mongo(self, value):
|
||||||
return Binary(value)
|
return Binary(value)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if not isinstance(value, (six.binary_type, Binary)):
|
if not isinstance(value, (bytes, Binary)):
|
||||||
self.error(
|
self.error(
|
||||||
"BinaryField only accepts instances of "
|
"BinaryField only accepts instances of "
|
||||||
"(%s, %s, Binary)" % (six.binary_type.__name__, Binary.__name__)
|
"(%s, %s, Binary)" % (bytes.__name__, Binary.__name__)
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.max_bytes is not None and len(value) > self.max_bytes:
|
if self.max_bytes is not None and len(value) > self.max_bytes:
|
||||||
@@ -1613,14 +1616,14 @@ class BinaryField(BaseField):
|
|||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
return super(BinaryField, self).prepare_query_value(op, self.to_mongo(value))
|
return super().prepare_query_value(op, self.to_mongo(value))
|
||||||
|
|
||||||
|
|
||||||
class GridFSError(Exception):
|
class GridFSError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class GridFSProxy(object):
|
class GridFSProxy:
|
||||||
"""Proxy object to handle writing and reading of files to and from GridFS
|
"""Proxy object to handle writing and reading of files to and from GridFS
|
||||||
|
|
||||||
.. versionadded:: 0.4
|
.. versionadded:: 0.4
|
||||||
@@ -1670,8 +1673,6 @@ class GridFSProxy(object):
|
|||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
return bool(self.grid_id)
|
return bool(self.grid_id)
|
||||||
|
|
||||||
__nonzero__ = __bool__ # For Py2 support
|
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
self_dict = self.__dict__
|
self_dict = self.__dict__
|
||||||
self_dict["_fs"] = None
|
self_dict["_fs"] = None
|
||||||
@@ -1686,12 +1687,12 @@ class GridFSProxy(object):
|
|||||||
return self.__copy__()
|
return self.__copy__()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s: %s>" % (self.__class__.__name__, self.grid_id)
|
return "<{}: {}>".format(self.__class__.__name__, self.grid_id)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
gridout = self.get()
|
gridout = self.get()
|
||||||
filename = getattr(gridout, "filename") if gridout else "<no file>"
|
filename = getattr(gridout, "filename") if gridout else "<no file>"
|
||||||
return "<%s: %s (%s)>" % (self.__class__.__name__, filename, self.grid_id)
|
return "<{}: {} ({})>".format(self.__class__.__name__, filename, self.grid_id)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, GridFSProxy):
|
if isinstance(other, GridFSProxy):
|
||||||
@@ -1802,7 +1803,7 @@ class FileField(BaseField):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self, db_alias=DEFAULT_CONNECTION_NAME, collection_name="fs", **kwargs
|
self, db_alias=DEFAULT_CONNECTION_NAME, collection_name="fs", **kwargs
|
||||||
):
|
):
|
||||||
super(FileField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.collection_name = collection_name
|
self.collection_name = collection_name
|
||||||
self.db_alias = db_alias
|
self.db_alias = db_alias
|
||||||
|
|
||||||
@@ -1825,7 +1826,7 @@ class FileField(BaseField):
|
|||||||
key = self.name
|
key = self.name
|
||||||
if (
|
if (
|
||||||
hasattr(value, "read") and not isinstance(value, GridFSProxy)
|
hasattr(value, "read") and not isinstance(value, GridFSProxy)
|
||||||
) or isinstance(value, (six.binary_type, six.string_types)):
|
) or isinstance(value, (bytes, str)):
|
||||||
# using "FileField() = file/string" notation
|
# using "FileField() = file/string" notation
|
||||||
grid_file = instance._data.get(self.name)
|
grid_file = instance._data.get(self.name)
|
||||||
# If a file already exists, delete it
|
# If a file already exists, delete it
|
||||||
@@ -1943,11 +1944,11 @@ class ImageGridFsProxy(GridFSProxy):
|
|||||||
|
|
||||||
w, h = img.size
|
w, h = img.size
|
||||||
|
|
||||||
io = StringIO()
|
io = BytesIO()
|
||||||
img.save(io, img_format, progressive=progressive)
|
img.save(io, img_format, progressive=progressive)
|
||||||
io.seek(0)
|
io.seek(0)
|
||||||
|
|
||||||
return super(ImageGridFsProxy, self).put(
|
return super().put(
|
||||||
io, width=w, height=h, format=img_format, thumbnail_id=thumb_id, **kwargs
|
io, width=w, height=h, format=img_format, thumbnail_id=thumb_id, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1957,12 +1958,12 @@ class ImageGridFsProxy(GridFSProxy):
|
|||||||
if out and out.thumbnail_id:
|
if out and out.thumbnail_id:
|
||||||
self.fs.delete(out.thumbnail_id)
|
self.fs.delete(out.thumbnail_id)
|
||||||
|
|
||||||
return super(ImageGridFsProxy, self).delete()
|
return super().delete()
|
||||||
|
|
||||||
def _put_thumbnail(self, thumbnail, format, progressive, **kwargs):
|
def _put_thumbnail(self, thumbnail, format, progressive, **kwargs):
|
||||||
w, h = thumbnail.size
|
w, h = thumbnail.size
|
||||||
|
|
||||||
io = StringIO()
|
io = BytesIO()
|
||||||
thumbnail.save(io, format, progressive=progressive)
|
thumbnail.save(io, format, progressive=progressive)
|
||||||
io.seek(0)
|
io.seek(0)
|
||||||
|
|
||||||
@@ -2032,16 +2033,11 @@ class ImageField(FileField):
|
|||||||
for att_name, att in extra_args.items():
|
for att_name, att in extra_args.items():
|
||||||
value = None
|
value = None
|
||||||
if isinstance(att, (tuple, list)):
|
if isinstance(att, (tuple, list)):
|
||||||
if six.PY3:
|
value = dict(itertools.zip_longest(params_size, att, fillvalue=None))
|
||||||
value = dict(
|
|
||||||
itertools.zip_longest(params_size, att, fillvalue=None)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
value = dict(map(None, params_size, att))
|
|
||||||
|
|
||||||
setattr(self, att_name, value)
|
setattr(self, att_name, value)
|
||||||
|
|
||||||
super(ImageField, self).__init__(collection_name=collection_name, **kwargs)
|
super().__init__(collection_name=collection_name, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SequenceField(BaseField):
|
class SequenceField(BaseField):
|
||||||
@@ -2093,14 +2089,14 @@ class SequenceField(BaseField):
|
|||||||
self.value_decorator = (
|
self.value_decorator = (
|
||||||
value_decorator if callable(value_decorator) else self.VALUE_DECORATOR
|
value_decorator if callable(value_decorator) else self.VALUE_DECORATOR
|
||||||
)
|
)
|
||||||
super(SequenceField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
"""
|
"""
|
||||||
Generate and Increment the counter
|
Generate and Increment the counter
|
||||||
"""
|
"""
|
||||||
sequence_name = self.get_sequence_name()
|
sequence_name = self.get_sequence_name()
|
||||||
sequence_id = "%s.%s" % (sequence_name, self.name)
|
sequence_id = "{}.{}".format(sequence_name, self.name)
|
||||||
collection = get_db(alias=self.db_alias)[self.collection_name]
|
collection = get_db(alias=self.db_alias)[self.collection_name]
|
||||||
|
|
||||||
counter = collection.find_one_and_update(
|
counter = collection.find_one_and_update(
|
||||||
@@ -2114,7 +2110,7 @@ class SequenceField(BaseField):
|
|||||||
def set_next_value(self, value):
|
def set_next_value(self, value):
|
||||||
"""Helper method to set the next sequence value"""
|
"""Helper method to set the next sequence value"""
|
||||||
sequence_name = self.get_sequence_name()
|
sequence_name = self.get_sequence_name()
|
||||||
sequence_id = "%s.%s" % (sequence_name, self.name)
|
sequence_id = "{}.{}".format(sequence_name, self.name)
|
||||||
collection = get_db(alias=self.db_alias)[self.collection_name]
|
collection = get_db(alias=self.db_alias)[self.collection_name]
|
||||||
counter = collection.find_one_and_update(
|
counter = collection.find_one_and_update(
|
||||||
filter={"_id": sequence_id},
|
filter={"_id": sequence_id},
|
||||||
@@ -2131,7 +2127,7 @@ class SequenceField(BaseField):
|
|||||||
as it is only fixed on set.
|
as it is only fixed on set.
|
||||||
"""
|
"""
|
||||||
sequence_name = self.get_sequence_name()
|
sequence_name = self.get_sequence_name()
|
||||||
sequence_id = "%s.%s" % (sequence_name, self.name)
|
sequence_id = "{}.{}".format(sequence_name, self.name)
|
||||||
collection = get_db(alias=self.db_alias)[self.collection_name]
|
collection = get_db(alias=self.db_alias)[self.collection_name]
|
||||||
data = collection.find_one({"_id": sequence_id})
|
data = collection.find_one({"_id": sequence_id})
|
||||||
|
|
||||||
@@ -2154,7 +2150,7 @@ class SequenceField(BaseField):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
value = super(SequenceField, self).__get__(instance, owner)
|
value = super().__get__(instance, owner)
|
||||||
if value is None and instance._initialised:
|
if value is None and instance._initialised:
|
||||||
value = self.generate()
|
value = self.generate()
|
||||||
instance._data[self.name] = value
|
instance._data[self.name] = value
|
||||||
@@ -2167,7 +2163,7 @@ class SequenceField(BaseField):
|
|||||||
if value is None and instance._initialised:
|
if value is None and instance._initialised:
|
||||||
value = self.generate()
|
value = self.generate()
|
||||||
|
|
||||||
return super(SequenceField, self).__set__(instance, value)
|
return super().__set__(instance, value)
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
"""
|
"""
|
||||||
@@ -2201,14 +2197,14 @@ class UUIDField(BaseField):
|
|||||||
.. versionchanged:: 0.6.19
|
.. versionchanged:: 0.6.19
|
||||||
"""
|
"""
|
||||||
self._binary = binary
|
self._binary = binary
|
||||||
super(UUIDField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if not self._binary:
|
if not self._binary:
|
||||||
original_value = value
|
original_value = value
|
||||||
try:
|
try:
|
||||||
if not isinstance(value, six.string_types):
|
if not isinstance(value, str):
|
||||||
value = six.text_type(value)
|
value = str(value)
|
||||||
return uuid.UUID(value)
|
return uuid.UUID(value)
|
||||||
except (ValueError, TypeError, AttributeError):
|
except (ValueError, TypeError, AttributeError):
|
||||||
return original_value
|
return original_value
|
||||||
@@ -2216,8 +2212,8 @@ class UUIDField(BaseField):
|
|||||||
|
|
||||||
def to_mongo(self, value):
|
def to_mongo(self, value):
|
||||||
if not self._binary:
|
if not self._binary:
|
||||||
return six.text_type(value)
|
return str(value)
|
||||||
elif isinstance(value, six.string_types):
|
elif isinstance(value, str):
|
||||||
return uuid.UUID(value)
|
return uuid.UUID(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@@ -2228,7 +2224,7 @@ class UUIDField(BaseField):
|
|||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if not isinstance(value, uuid.UUID):
|
if not isinstance(value, uuid.UUID):
|
||||||
if not isinstance(value, six.string_types):
|
if not isinstance(value, str):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
try:
|
try:
|
||||||
uuid.UUID(value)
|
uuid.UUID(value)
|
||||||
@@ -2427,7 +2423,7 @@ class LazyReferenceField(BaseField):
|
|||||||
document. Note this only work getting field (not setting or deleting).
|
document. Note this only work getting field (not setting or deleting).
|
||||||
"""
|
"""
|
||||||
# XXX ValidationError raised outside of the "validate" method.
|
# XXX ValidationError raised outside of the "validate" method.
|
||||||
if not isinstance(document_type, six.string_types) and not issubclass(
|
if not isinstance(document_type, str) and not issubclass(
|
||||||
document_type, Document
|
document_type, Document
|
||||||
):
|
):
|
||||||
self.error(
|
self.error(
|
||||||
@@ -2439,11 +2435,11 @@ class LazyReferenceField(BaseField):
|
|||||||
self.passthrough = passthrough
|
self.passthrough = passthrough
|
||||||
self.document_type_obj = document_type
|
self.document_type_obj = document_type
|
||||||
self.reverse_delete_rule = reverse_delete_rule
|
self.reverse_delete_rule = reverse_delete_rule
|
||||||
super(LazyReferenceField, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def document_type(self):
|
def document_type(self):
|
||||||
if isinstance(self.document_type_obj, six.string_types):
|
if isinstance(self.document_type_obj, str):
|
||||||
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT:
|
||||||
self.document_type_obj = self.owner_document
|
self.document_type_obj = self.owner_document
|
||||||
else:
|
else:
|
||||||
@@ -2482,7 +2478,7 @@ class LazyReferenceField(BaseField):
|
|||||||
if value:
|
if value:
|
||||||
instance._data[self.name] = value
|
instance._data[self.name] = value
|
||||||
|
|
||||||
return super(LazyReferenceField, self).__get__(instance, owner)
|
return super().__get__(instance, owner)
|
||||||
|
|
||||||
def to_mongo(self, value):
|
def to_mongo(self, value):
|
||||||
if isinstance(value, LazyReference):
|
if isinstance(value, LazyReference):
|
||||||
@@ -2502,6 +2498,13 @@ class LazyReferenceField(BaseField):
|
|||||||
else:
|
else:
|
||||||
return pk
|
return pk
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
"""Convert a MongoDB-compatible type to a Python type."""
|
||||||
|
if not isinstance(value, (DBRef, Document, EmbeddedDocument)):
|
||||||
|
collection = self.document_type._get_collection_name()
|
||||||
|
value = DBRef(collection, self.document_type.id.to_python(value))
|
||||||
|
return value
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if isinstance(value, LazyReference):
|
if isinstance(value, LazyReference):
|
||||||
if value.collection != self.document_type._get_collection_name():
|
if value.collection != self.document_type._get_collection_name():
|
||||||
@@ -2539,7 +2542,7 @@ class LazyReferenceField(BaseField):
|
|||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
super(LazyReferenceField, self).prepare_query_value(op, value)
|
super().prepare_query_value(op, value)
|
||||||
return self.to_mongo(value)
|
return self.to_mongo(value)
|
||||||
|
|
||||||
def lookup_member(self, member_name):
|
def lookup_member(self, member_name):
|
||||||
@@ -2566,12 +2569,12 @@ class GenericLazyReferenceField(GenericReferenceField):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.passthrough = kwargs.pop("passthrough", False)
|
self.passthrough = kwargs.pop("passthrough", False)
|
||||||
super(GenericLazyReferenceField, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def _validate_choices(self, value):
|
def _validate_choices(self, value):
|
||||||
if isinstance(value, LazyReference):
|
if isinstance(value, LazyReference):
|
||||||
value = value.document_type._class_name
|
value = value.document_type._class_name
|
||||||
super(GenericLazyReferenceField, self)._validate_choices(value)
|
super()._validate_choices(value)
|
||||||
|
|
||||||
def build_lazyref(self, value):
|
def build_lazyref(self, value):
|
||||||
if isinstance(value, LazyReference):
|
if isinstance(value, LazyReference):
|
||||||
@@ -2600,7 +2603,7 @@ class GenericLazyReferenceField(GenericReferenceField):
|
|||||||
if value:
|
if value:
|
||||||
instance._data[self.name] = value
|
instance._data[self.name] = value
|
||||||
|
|
||||||
return super(GenericLazyReferenceField, self).__get__(instance, owner)
|
return super().__get__(instance, owner)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if isinstance(value, LazyReference) and value.pk is None:
|
if isinstance(value, LazyReference) and value.pk is None:
|
||||||
@@ -2608,7 +2611,7 @@ class GenericLazyReferenceField(GenericReferenceField):
|
|||||||
"You can only reference documents once they have been"
|
"You can only reference documents once they have been"
|
||||||
" saved to the database"
|
" saved to the database"
|
||||||
)
|
)
|
||||||
return super(GenericLazyReferenceField, self).validate(value)
|
return super().validate(value)
|
||||||
|
|
||||||
def to_mongo(self, document):
|
def to_mongo(self, document):
|
||||||
if document is None:
|
if document is None:
|
||||||
@@ -2627,4 +2630,4 @@ class GenericLazyReferenceField(GenericReferenceField):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return super(GenericLazyReferenceField, self).to_mongo(document)
|
return super().to_mongo(document)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ MONGODB_36 = (3, 6)
|
|||||||
|
|
||||||
|
|
||||||
def get_mongodb_version():
|
def get_mongodb_version():
|
||||||
"""Return the version of the connected mongoDB (first 2 digits)
|
"""Return the version of the default connected mongoDB (first 2 digits)
|
||||||
|
|
||||||
:return: tuple(int, int)
|
:return: tuple(int, int)
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
"""
|
|
||||||
Helper functions, constants, and types to aid with Python v2.7 - v3.x support
|
|
||||||
"""
|
|
||||||
import six
|
|
||||||
|
|
||||||
# six.BytesIO resolves to StringIO.StringIO in Py2 and io.BytesIO in Py3.
|
|
||||||
StringIO = six.BytesIO
|
|
||||||
|
|
||||||
# Additionally for Py2, try to use the faster cStringIO, if available
|
|
||||||
if not six.PY3:
|
|
||||||
try:
|
|
||||||
import cStringIO
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
StringIO = cStringIO.StringIO
|
|
||||||
|
|
||||||
|
|
||||||
if six.PY3:
|
|
||||||
from collections.abc import Hashable
|
|
||||||
else:
|
|
||||||
# raises DeprecationWarnings in Python >=3.7
|
|
||||||
from collections import Hashable
|
|
||||||
@@ -1,24 +1,27 @@
|
|||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
|
|
||||||
from bson import SON, json_util
|
from bson import SON, json_util
|
||||||
from bson.code import Code
|
from bson.code import Code
|
||||||
import pymongo
|
import pymongo
|
||||||
import pymongo.errors
|
import pymongo.errors
|
||||||
from pymongo.collection import ReturnDocument
|
from pymongo.collection import ReturnDocument
|
||||||
from pymongo.common import validate_read_preference
|
from pymongo.common import validate_read_preference
|
||||||
import six
|
from pymongo.read_concern import ReadConcern
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine import signals
|
from mongoengine import signals
|
||||||
from mongoengine.base import get_document
|
from mongoengine.base import get_document
|
||||||
from mongoengine.common import _import_class
|
from mongoengine.common import _import_class
|
||||||
from mongoengine.connection import get_db
|
from mongoengine.connection import get_db
|
||||||
from mongoengine.context_managers import set_write_concern, switch_db
|
from mongoengine.context_managers import (
|
||||||
|
set_read_write_concern,
|
||||||
|
set_write_concern,
|
||||||
|
switch_db,
|
||||||
|
)
|
||||||
from mongoengine.errors import (
|
from mongoengine.errors import (
|
||||||
BulkWriteError,
|
BulkWriteError,
|
||||||
InvalidQueryError,
|
InvalidQueryError,
|
||||||
@@ -41,7 +44,7 @@ DENY = 3
|
|||||||
PULL = 4
|
PULL = 4
|
||||||
|
|
||||||
|
|
||||||
class BaseQuerySet(object):
|
class BaseQuerySet:
|
||||||
"""A set of results returned from a query. Wraps a MongoDB cursor,
|
"""A set of results returned from a query. Wraps a MongoDB cursor,
|
||||||
providing :class:`~mongoengine.Document` objects as the results.
|
providing :class:`~mongoengine.Document` objects as the results.
|
||||||
"""
|
"""
|
||||||
@@ -60,8 +63,8 @@ class BaseQuerySet(object):
|
|||||||
self._ordering = None
|
self._ordering = None
|
||||||
self._snapshot = False
|
self._snapshot = False
|
||||||
self._timeout = True
|
self._timeout = True
|
||||||
self._slave_okay = False
|
|
||||||
self._read_preference = None
|
self._read_preference = None
|
||||||
|
self._read_concern = None
|
||||||
self._iter = False
|
self._iter = False
|
||||||
self._scalar = []
|
self._scalar = []
|
||||||
self._none = False
|
self._none = False
|
||||||
@@ -81,6 +84,7 @@ class BaseQuerySet(object):
|
|||||||
self._limit = None
|
self._limit = None
|
||||||
self._skip = None
|
self._skip = None
|
||||||
self._hint = -1 # Using -1 as None is a valid value for hint
|
self._hint = -1 # Using -1 as None is a valid value for hint
|
||||||
|
self._collation = None
|
||||||
self._batch_size = None
|
self._batch_size = None
|
||||||
self.only_fields = []
|
self.only_fields = []
|
||||||
self._max_time_ms = None
|
self._max_time_ms = None
|
||||||
@@ -203,8 +207,6 @@ class BaseQuerySet(object):
|
|||||||
"""Avoid to open all records in an if stmt in Py3."""
|
"""Avoid to open all records in an if stmt in Py3."""
|
||||||
return self._has_data()
|
return self._has_data()
|
||||||
|
|
||||||
__nonzero__ = __bool__ # For Py2 support
|
|
||||||
|
|
||||||
# Core functions
|
# Core functions
|
||||||
|
|
||||||
def all(self):
|
def all(self):
|
||||||
@@ -255,20 +257,21 @@ class BaseQuerySet(object):
|
|||||||
queryset = queryset.filter(*q_objs, **query)
|
queryset = queryset.filter(*q_objs, **query)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = six.next(queryset)
|
result = next(queryset)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
msg = "%s matching query does not exist." % queryset._document._class_name
|
msg = "%s matching query does not exist." % queryset._document._class_name
|
||||||
raise queryset._document.DoesNotExist(msg)
|
raise queryset._document.DoesNotExist(msg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
six.next(queryset)
|
# Check if there is another match
|
||||||
|
next(queryset)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# If we were able to retrieve the 2nd doc, rewind the cursor and
|
# If we were able to retrieve the 2nd doc, raise the MultipleObjectsReturned exception.
|
||||||
# raise the MultipleObjectsReturned exception.
|
raise queryset._document.MultipleObjectsReturned(
|
||||||
queryset.rewind()
|
"2 or more items returned, instead of 1"
|
||||||
message = u"%d items returned, instead of 1" % queryset.count()
|
)
|
||||||
raise queryset._document.MultipleObjectsReturned(message)
|
|
||||||
|
|
||||||
def create(self, **kwargs):
|
def create(self, **kwargs):
|
||||||
"""Create new object. Returns the saved object instance.
|
"""Create new object. Returns the saved object instance.
|
||||||
@@ -301,7 +304,7 @@ class BaseQuerySet(object):
|
|||||||
``insert(..., {w: 2, fsync: True})`` will wait until at least
|
``insert(..., {w: 2, fsync: True})`` will wait until at least
|
||||||
two servers have recorded the write and will force an fsync on
|
two servers have recorded the write and will force an fsync on
|
||||||
each server being written to.
|
each server being written to.
|
||||||
:parm signal_kwargs: (optional) kwargs dictionary to be passed to
|
:param signal_kwargs: (optional) kwargs dictionary to be passed to
|
||||||
the signal calls.
|
the signal calls.
|
||||||
|
|
||||||
By default returns document instances, set ``load_bulk`` to False to
|
By default returns document instances, set ``load_bulk`` to False to
|
||||||
@@ -352,20 +355,20 @@ class BaseQuerySet(object):
|
|||||||
)
|
)
|
||||||
except pymongo.errors.DuplicateKeyError as err:
|
except pymongo.errors.DuplicateKeyError as err:
|
||||||
message = "Could not save document (%s)"
|
message = "Could not save document (%s)"
|
||||||
raise NotUniqueError(message % six.text_type(err))
|
raise NotUniqueError(message % err)
|
||||||
except pymongo.errors.BulkWriteError as err:
|
except pymongo.errors.BulkWriteError as err:
|
||||||
# inserting documents that already have an _id field will
|
# inserting documents that already have an _id field will
|
||||||
# give huge performance debt or raise
|
# give huge performance debt or raise
|
||||||
message = u"Bulk write error: (%s)"
|
message = "Bulk write error: (%s)"
|
||||||
raise BulkWriteError(message % six.text_type(err.details))
|
raise BulkWriteError(message % err.details)
|
||||||
except pymongo.errors.OperationFailure as err:
|
except pymongo.errors.OperationFailure as err:
|
||||||
message = "Could not save document (%s)"
|
message = "Could not save document (%s)"
|
||||||
if re.match("^E1100[01] duplicate key", six.text_type(err)):
|
if re.match("^E1100[01] duplicate key", str(err)):
|
||||||
# E11000 - duplicate key error index
|
# E11000 - duplicate key error index
|
||||||
# E11001 - duplicate key on update
|
# E11001 - duplicate key on update
|
||||||
message = u"Tried to save duplicate unique keys (%s)"
|
message = "Tried to save duplicate unique keys (%s)"
|
||||||
raise NotUniqueError(message % six.text_type(err))
|
raise NotUniqueError(message % err)
|
||||||
raise OperationError(message % six.text_type(err))
|
raise OperationError(message % err)
|
||||||
|
|
||||||
# Apply inserted_ids to documents
|
# Apply inserted_ids to documents
|
||||||
for doc, doc_id in zip(docs, ids):
|
for doc, doc_id in zip(docs, ids):
|
||||||
@@ -489,7 +492,13 @@ class BaseQuerySet(object):
|
|||||||
return result.deleted_count
|
return result.deleted_count
|
||||||
|
|
||||||
def update(
|
def update(
|
||||||
self, upsert=False, multi=True, write_concern=None, full_result=False, **update
|
self,
|
||||||
|
upsert=False,
|
||||||
|
multi=True,
|
||||||
|
write_concern=None,
|
||||||
|
read_concern=None,
|
||||||
|
full_result=False,
|
||||||
|
**update
|
||||||
):
|
):
|
||||||
"""Perform an atomic update on the fields matched by the query.
|
"""Perform an atomic update on the fields matched by the query.
|
||||||
|
|
||||||
@@ -501,6 +510,7 @@ class BaseQuerySet(object):
|
|||||||
``save(..., write_concern={w: 2, fsync: True}, ...)`` will
|
``save(..., write_concern={w: 2, fsync: True}, ...)`` will
|
||||||
wait until at least two servers have recorded the write and
|
wait until at least two servers have recorded the write and
|
||||||
will force an fsync on the primary server.
|
will force an fsync on the primary server.
|
||||||
|
:param read_concern: Override the read concern for the operation
|
||||||
:param full_result: Return the associated ``pymongo.UpdateResult`` rather than just the number
|
:param full_result: Return the associated ``pymongo.UpdateResult`` rather than just the number
|
||||||
updated items
|
updated items
|
||||||
:param update: Django-style update keyword arguments
|
:param update: Django-style update keyword arguments
|
||||||
@@ -527,7 +537,9 @@ class BaseQuerySet(object):
|
|||||||
else:
|
else:
|
||||||
update["$set"] = {"_cls": queryset._document._class_name}
|
update["$set"] = {"_cls": queryset._document._class_name}
|
||||||
try:
|
try:
|
||||||
with set_write_concern(queryset._collection, write_concern) as collection:
|
with set_read_write_concern(
|
||||||
|
queryset._collection, write_concern, read_concern
|
||||||
|
) as collection:
|
||||||
update_func = collection.update_one
|
update_func = collection.update_one
|
||||||
if multi:
|
if multi:
|
||||||
update_func = collection.update_many
|
update_func = collection.update_many
|
||||||
@@ -537,14 +549,14 @@ class BaseQuerySet(object):
|
|||||||
elif result.raw_result:
|
elif result.raw_result:
|
||||||
return result.raw_result["n"]
|
return result.raw_result["n"]
|
||||||
except pymongo.errors.DuplicateKeyError as err:
|
except pymongo.errors.DuplicateKeyError as err:
|
||||||
raise NotUniqueError(u"Update failed (%s)" % six.text_type(err))
|
raise NotUniqueError("Update failed (%s)" % err)
|
||||||
except pymongo.errors.OperationFailure as err:
|
except pymongo.errors.OperationFailure as err:
|
||||||
if six.text_type(err) == u"multi not coded yet":
|
if str(err) == "multi not coded yet":
|
||||||
message = u"update() method requires MongoDB 1.1.3+"
|
message = "update() method requires MongoDB 1.1.3+"
|
||||||
raise OperationError(message)
|
raise OperationError(message)
|
||||||
raise OperationError(u"Update failed (%s)" % six.text_type(err))
|
raise OperationError("Update failed (%s)" % err)
|
||||||
|
|
||||||
def upsert_one(self, write_concern=None, **update):
|
def upsert_one(self, write_concern=None, read_concern=None, **update):
|
||||||
"""Overwrite or add the first document matched by the query.
|
"""Overwrite or add the first document matched by the query.
|
||||||
|
|
||||||
:param write_concern: Extra keyword arguments are passed down which
|
:param write_concern: Extra keyword arguments are passed down which
|
||||||
@@ -553,6 +565,7 @@ class BaseQuerySet(object):
|
|||||||
``save(..., write_concern={w: 2, fsync: True}, ...)`` will
|
``save(..., write_concern={w: 2, fsync: True}, ...)`` will
|
||||||
wait until at least two servers have recorded the write and
|
wait until at least two servers have recorded the write and
|
||||||
will force an fsync on the primary server.
|
will force an fsync on the primary server.
|
||||||
|
:param read_concern: Override the read concern for the operation
|
||||||
:param update: Django-style update keyword arguments
|
:param update: Django-style update keyword arguments
|
||||||
|
|
||||||
:returns the new or overwritten document
|
:returns the new or overwritten document
|
||||||
@@ -564,6 +577,7 @@ class BaseQuerySet(object):
|
|||||||
multi=False,
|
multi=False,
|
||||||
upsert=True,
|
upsert=True,
|
||||||
write_concern=write_concern,
|
write_concern=write_concern,
|
||||||
|
read_concern=read_concern,
|
||||||
full_result=True,
|
full_result=True,
|
||||||
**update
|
**update
|
||||||
)
|
)
|
||||||
@@ -660,9 +674,9 @@ class BaseQuerySet(object):
|
|||||||
**self._cursor_args
|
**self._cursor_args
|
||||||
)
|
)
|
||||||
except pymongo.errors.DuplicateKeyError as err:
|
except pymongo.errors.DuplicateKeyError as err:
|
||||||
raise NotUniqueError(u"Update failed (%s)" % err)
|
raise NotUniqueError("Update failed (%s)" % err)
|
||||||
except pymongo.errors.OperationFailure as err:
|
except pymongo.errors.OperationFailure as err:
|
||||||
raise OperationError(u"Update failed (%s)" % err)
|
raise OperationError("Update failed (%s)" % err)
|
||||||
|
|
||||||
if full_response:
|
if full_response:
|
||||||
if result["value"] is not None:
|
if result["value"] is not None:
|
||||||
@@ -691,10 +705,10 @@ class BaseQuerySet(object):
|
|||||||
return queryset.filter(pk=object_id).first()
|
return queryset.filter(pk=object_id).first()
|
||||||
|
|
||||||
def in_bulk(self, object_ids):
|
def in_bulk(self, object_ids):
|
||||||
"""Retrieve a set of documents by their ids.
|
""""Retrieve a set of documents by their ids.
|
||||||
|
|
||||||
:param object_ids: a list or tuple of ``ObjectId``\ s
|
:param object_ids: a list or tuple of ObjectId's
|
||||||
:rtype: dict of ObjectIds as keys and collection-specific
|
:rtype: dict of ObjectId's as keys and collection-specific
|
||||||
Document subclasses as values.
|
Document subclasses as values.
|
||||||
|
|
||||||
.. versionadded:: 0.3
|
.. versionadded:: 0.3
|
||||||
@@ -774,7 +788,6 @@ class BaseQuerySet(object):
|
|||||||
"_ordering",
|
"_ordering",
|
||||||
"_snapshot",
|
"_snapshot",
|
||||||
"_timeout",
|
"_timeout",
|
||||||
"_slave_okay",
|
|
||||||
"_read_preference",
|
"_read_preference",
|
||||||
"_iter",
|
"_iter",
|
||||||
"_scalar",
|
"_scalar",
|
||||||
@@ -782,6 +795,7 @@ class BaseQuerySet(object):
|
|||||||
"_limit",
|
"_limit",
|
||||||
"_skip",
|
"_skip",
|
||||||
"_hint",
|
"_hint",
|
||||||
|
"_collation",
|
||||||
"_auto_dereference",
|
"_auto_dereference",
|
||||||
"_search_text",
|
"_search_text",
|
||||||
"only_fields",
|
"only_fields",
|
||||||
@@ -864,6 +878,32 @@ class BaseQuerySet(object):
|
|||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
def collation(self, collation=None):
|
||||||
|
"""
|
||||||
|
Collation allows users to specify language-specific rules for string
|
||||||
|
comparison, such as rules for lettercase and accent marks.
|
||||||
|
:param collation: `~pymongo.collation.Collation` or dict with
|
||||||
|
following fields:
|
||||||
|
{
|
||||||
|
locale: str,
|
||||||
|
caseLevel: bool,
|
||||||
|
caseFirst: str,
|
||||||
|
strength: int,
|
||||||
|
numericOrdering: bool,
|
||||||
|
alternate: str,
|
||||||
|
maxVariable: str,
|
||||||
|
backwards: str
|
||||||
|
}
|
||||||
|
Collation should be added to indexes like in test example
|
||||||
|
"""
|
||||||
|
queryset = self.clone()
|
||||||
|
queryset._collation = collation
|
||||||
|
|
||||||
|
if queryset._cursor_obj:
|
||||||
|
queryset._cursor_obj.collation(collation)
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
def batch_size(self, size):
|
def batch_size(self, size):
|
||||||
"""Limit the number of documents returned in a single batch (each
|
"""Limit the number of documents returned in a single batch (each
|
||||||
batch requires a round trip to the server).
|
batch requires a round trip to the server).
|
||||||
@@ -961,7 +1001,7 @@ class BaseQuerySet(object):
|
|||||||
.. versionchanged:: 0.5 - Added subfield support
|
.. versionchanged:: 0.5 - Added subfield support
|
||||||
"""
|
"""
|
||||||
fields = {f: QueryFieldList.ONLY for f in fields}
|
fields = {f: QueryFieldList.ONLY for f in fields}
|
||||||
self.only_fields = fields.keys()
|
self.only_fields = list(fields.keys())
|
||||||
return self.fields(True, **fields)
|
return self.fields(True, **fields)
|
||||||
|
|
||||||
def exclude(self, *fields):
|
def exclude(self, *fields):
|
||||||
@@ -998,9 +1038,11 @@ class BaseQuerySet(object):
|
|||||||
|
|
||||||
posts = BlogPost.objects(...).fields(comments=0)
|
posts = BlogPost.objects(...).fields(comments=0)
|
||||||
|
|
||||||
To retrieve a subrange of array elements:
|
To retrieve a subrange or sublist of array elements,
|
||||||
|
support exist for both the `slice` and `elemMatch` projection operator:
|
||||||
|
|
||||||
posts = BlogPost.objects(...).fields(slice__comments=5)
|
posts = BlogPost.objects(...).fields(slice__comments=5)
|
||||||
|
posts = BlogPost.objects(...).fields(elemMatch__comments="test")
|
||||||
|
|
||||||
:param kwargs: A set of keyword arguments identifying what to
|
:param kwargs: A set of keyword arguments identifying what to
|
||||||
include, exclude, or slice.
|
include, exclude, or slice.
|
||||||
@@ -1009,7 +1051,7 @@ class BaseQuerySet(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Check for an operator and transform to mongo-style if there is
|
# Check for an operator and transform to mongo-style if there is
|
||||||
operators = ["slice"]
|
operators = ["slice", "elemMatch"]
|
||||||
cleaned_fields = []
|
cleaned_fields = []
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
parts = key.split("__")
|
parts = key.split("__")
|
||||||
@@ -1112,7 +1154,7 @@ class BaseQuerySet(object):
|
|||||||
|
|
||||||
def explain(self):
|
def explain(self):
|
||||||
"""Return an explain plan record for the
|
"""Return an explain plan record for the
|
||||||
:class:`~mongoengine.queryset.QuerySet`\ 's cursor.
|
:class:`~mongoengine.queryset.QuerySet` cursor.
|
||||||
"""
|
"""
|
||||||
return self._cursor.explain()
|
return self._cursor.explain()
|
||||||
|
|
||||||
@@ -1142,20 +1184,6 @@ class BaseQuerySet(object):
|
|||||||
queryset._timeout = enabled
|
queryset._timeout = enabled
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
# DEPRECATED. Has no more impact on PyMongo 3+
|
|
||||||
def slave_okay(self, enabled):
|
|
||||||
"""Enable or disable the slave_okay when querying.
|
|
||||||
|
|
||||||
:param enabled: whether or not the slave_okay is enabled
|
|
||||||
|
|
||||||
.. deprecated:: Ignored with PyMongo 3+
|
|
||||||
"""
|
|
||||||
msg = "slave_okay is deprecated as it has no impact when using PyMongo 3+."
|
|
||||||
warnings.warn(msg, DeprecationWarning)
|
|
||||||
queryset = self.clone()
|
|
||||||
queryset._slave_okay = enabled
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def read_preference(self, read_preference):
|
def read_preference(self, read_preference):
|
||||||
"""Change the read_preference when querying.
|
"""Change the read_preference when querying.
|
||||||
|
|
||||||
@@ -1165,9 +1193,23 @@ class BaseQuerySet(object):
|
|||||||
validate_read_preference("read_preference", read_preference)
|
validate_read_preference("read_preference", read_preference)
|
||||||
queryset = self.clone()
|
queryset = self.clone()
|
||||||
queryset._read_preference = read_preference
|
queryset._read_preference = read_preference
|
||||||
queryset._cursor_obj = (
|
queryset._cursor_obj = None # we need to re-create the cursor object whenever we apply read_preference
|
||||||
None
|
return queryset
|
||||||
) # we need to re-create the cursor object whenever we apply read_preference
|
|
||||||
|
def read_concern(self, read_concern):
|
||||||
|
"""Change the read_concern when querying.
|
||||||
|
|
||||||
|
:param read_concern: override ReplicaSetConnection-level
|
||||||
|
preference.
|
||||||
|
"""
|
||||||
|
if read_concern is not None and not isinstance(read_concern, Mapping):
|
||||||
|
raise TypeError("%r is not a valid read concern." % (read_concern,))
|
||||||
|
|
||||||
|
queryset = self.clone()
|
||||||
|
queryset._read_concern = (
|
||||||
|
ReadConcern(**read_concern) if read_concern is not None else None
|
||||||
|
)
|
||||||
|
queryset._cursor_obj = None # we need to re-create the cursor object whenever we apply read_concern
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def scalar(self, *fields):
|
def scalar(self, *fields):
|
||||||
@@ -1229,16 +1271,27 @@ class BaseQuerySet(object):
|
|||||||
for data in son_data
|
for data in son_data
|
||||||
]
|
]
|
||||||
|
|
||||||
def aggregate(self, *pipeline, **kwargs):
|
def aggregate(self, pipeline, *suppl_pipeline, **kwargs):
|
||||||
"""
|
"""Perform a aggregate function based in your queryset params
|
||||||
Perform a aggregate function based in your queryset params
|
|
||||||
:param pipeline: list of aggregation commands,\
|
:param pipeline: list of aggregation commands,\
|
||||||
see: http://docs.mongodb.org/manual/core/aggregation-pipeline/
|
see: http://docs.mongodb.org/manual/core/aggregation-pipeline/
|
||||||
|
:param suppl_pipeline: unpacked list of pipeline (added to support deprecation of the old interface)
|
||||||
|
parameter will be removed shortly
|
||||||
|
:param kwargs: (optional) kwargs dictionary to be passed to pymongo's aggregate call
|
||||||
|
See https://api.mongodb.com/python/current/api/pymongo/collection.html#pymongo.collection.Collection.aggregate
|
||||||
.. versionadded:: 0.9
|
.. versionadded:: 0.9
|
||||||
"""
|
"""
|
||||||
initial_pipeline = []
|
using_deprecated_interface = isinstance(pipeline, dict) or bool(suppl_pipeline)
|
||||||
|
user_pipeline = [pipeline] if isinstance(pipeline, dict) else list(pipeline)
|
||||||
|
|
||||||
|
if using_deprecated_interface:
|
||||||
|
msg = "Calling .aggregate() with un unpacked list (*pipeline) is deprecated, it will soon change and will expect a list (similar to pymongo.Collection.aggregate interface), see documentation"
|
||||||
|
warnings.warn(msg, DeprecationWarning)
|
||||||
|
|
||||||
|
user_pipeline += suppl_pipeline
|
||||||
|
|
||||||
|
initial_pipeline = []
|
||||||
if self._query:
|
if self._query:
|
||||||
initial_pipeline.append({"$match": self._query})
|
initial_pipeline.append({"$match": self._query})
|
||||||
|
|
||||||
@@ -1255,14 +1308,14 @@ class BaseQuerySet(object):
|
|||||||
if self._skip is not None:
|
if self._skip is not None:
|
||||||
initial_pipeline.append({"$skip": self._skip})
|
initial_pipeline.append({"$skip": self._skip})
|
||||||
|
|
||||||
pipeline = initial_pipeline + list(pipeline)
|
final_pipeline = initial_pipeline + user_pipeline
|
||||||
|
|
||||||
|
collection = self._collection
|
||||||
if self._read_preference is not None:
|
if self._read_preference is not None:
|
||||||
return self._collection.with_options(
|
collection = self._collection.with_options(
|
||||||
read_preference=self._read_preference
|
read_preference=self._read_preference
|
||||||
).aggregate(pipeline, cursor={}, **kwargs)
|
)
|
||||||
|
return collection.aggregate(final_pipeline, cursor={}, **kwargs)
|
||||||
return self._collection.aggregate(pipeline, cursor={}, **kwargs)
|
|
||||||
|
|
||||||
# JS functionality
|
# JS functionality
|
||||||
def map_reduce(
|
def map_reduce(
|
||||||
@@ -1315,13 +1368,13 @@ class BaseQuerySet(object):
|
|||||||
map_f_scope = {}
|
map_f_scope = {}
|
||||||
if isinstance(map_f, Code):
|
if isinstance(map_f, Code):
|
||||||
map_f_scope = map_f.scope
|
map_f_scope = map_f.scope
|
||||||
map_f = six.text_type(map_f)
|
map_f = str(map_f)
|
||||||
map_f = Code(queryset._sub_js_fields(map_f), map_f_scope)
|
map_f = Code(queryset._sub_js_fields(map_f), map_f_scope)
|
||||||
|
|
||||||
reduce_f_scope = {}
|
reduce_f_scope = {}
|
||||||
if isinstance(reduce_f, Code):
|
if isinstance(reduce_f, Code):
|
||||||
reduce_f_scope = reduce_f.scope
|
reduce_f_scope = reduce_f.scope
|
||||||
reduce_f = six.text_type(reduce_f)
|
reduce_f = str(reduce_f)
|
||||||
reduce_f_code = queryset._sub_js_fields(reduce_f)
|
reduce_f_code = queryset._sub_js_fields(reduce_f)
|
||||||
reduce_f = Code(reduce_f_code, reduce_f_scope)
|
reduce_f = Code(reduce_f_code, reduce_f_scope)
|
||||||
|
|
||||||
@@ -1331,7 +1384,7 @@ class BaseQuerySet(object):
|
|||||||
finalize_f_scope = {}
|
finalize_f_scope = {}
|
||||||
if isinstance(finalize_f, Code):
|
if isinstance(finalize_f, Code):
|
||||||
finalize_f_scope = finalize_f.scope
|
finalize_f_scope = finalize_f.scope
|
||||||
finalize_f = six.text_type(finalize_f)
|
finalize_f = str(finalize_f)
|
||||||
finalize_f_code = queryset._sub_js_fields(finalize_f)
|
finalize_f_code = queryset._sub_js_fields(finalize_f)
|
||||||
finalize_f = Code(finalize_f_code, finalize_f_scope)
|
finalize_f = Code(finalize_f_code, finalize_f_scope)
|
||||||
mr_args["finalize"] = finalize_f
|
mr_args["finalize"] = finalize_f
|
||||||
@@ -1347,7 +1400,7 @@ class BaseQuerySet(object):
|
|||||||
else:
|
else:
|
||||||
map_reduce_function = "map_reduce"
|
map_reduce_function = "map_reduce"
|
||||||
|
|
||||||
if isinstance(output, six.string_types):
|
if isinstance(output, str):
|
||||||
mr_args["out"] = output
|
mr_args["out"] = output
|
||||||
|
|
||||||
elif isinstance(output, dict):
|
elif isinstance(output, dict):
|
||||||
@@ -1534,7 +1587,7 @@ class BaseQuerySet(object):
|
|||||||
if self._limit == 0 or self._none:
|
if self._limit == 0 or self._none:
|
||||||
raise StopIteration
|
raise StopIteration
|
||||||
|
|
||||||
raw_doc = six.next(self._cursor)
|
raw_doc = next(self._cursor)
|
||||||
|
|
||||||
if self._as_pymongo:
|
if self._as_pymongo:
|
||||||
return raw_doc
|
return raw_doc
|
||||||
@@ -1577,7 +1630,10 @@ class BaseQuerySet(object):
|
|||||||
if self._snapshot:
|
if self._snapshot:
|
||||||
msg = "The snapshot option is not anymore available with PyMongo 3+"
|
msg = "The snapshot option is not anymore available with PyMongo 3+"
|
||||||
warnings.warn(msg, DeprecationWarning)
|
warnings.warn(msg, DeprecationWarning)
|
||||||
cursor_args = {"no_cursor_timeout": not self._timeout}
|
|
||||||
|
cursor_args = {}
|
||||||
|
if not self._timeout:
|
||||||
|
cursor_args["no_cursor_timeout"] = True
|
||||||
|
|
||||||
if self._loaded_fields:
|
if self._loaded_fields:
|
||||||
cursor_args[fields_name] = self._loaded_fields.as_dict()
|
cursor_args[fields_name] = self._loaded_fields.as_dict()
|
||||||
@@ -1602,12 +1658,13 @@ class BaseQuerySet(object):
|
|||||||
# XXX In PyMongo 3+, we define the read preference on a collection
|
# XXX In PyMongo 3+, we define the read preference on a collection
|
||||||
# level, not a cursor level. Thus, we need to get a cloned collection
|
# level, not a cursor level. Thus, we need to get a cloned collection
|
||||||
# object using `with_options` first.
|
# object using `with_options` first.
|
||||||
if self._read_preference is not None:
|
if self._read_preference is not None or self._read_concern is not None:
|
||||||
self._cursor_obj = self._collection.with_options(
|
self._cursor_obj = self._collection.with_options(
|
||||||
read_preference=self._read_preference
|
read_preference=self._read_preference, read_concern=self._read_concern
|
||||||
).find(self._query, **self._cursor_args)
|
).find(self._query, **self._cursor_args)
|
||||||
else:
|
else:
|
||||||
self._cursor_obj = self._collection.find(self._query, **self._cursor_args)
|
self._cursor_obj = self._collection.find(self._query, **self._cursor_args)
|
||||||
|
|
||||||
# Apply "where" clauses to cursor
|
# Apply "where" clauses to cursor
|
||||||
if self._where_clause:
|
if self._where_clause:
|
||||||
where_clause = self._sub_js_fields(self._where_clause)
|
where_clause = self._sub_js_fields(self._where_clause)
|
||||||
@@ -1637,6 +1694,9 @@ class BaseQuerySet(object):
|
|||||||
if self._hint != -1:
|
if self._hint != -1:
|
||||||
self._cursor_obj.hint(self._hint)
|
self._cursor_obj.hint(self._hint)
|
||||||
|
|
||||||
|
if self._collation is not None:
|
||||||
|
self._cursor_obj.collation(self._collation)
|
||||||
|
|
||||||
if self._batch_size is not None:
|
if self._batch_size is not None:
|
||||||
self._cursor_obj.batch_size(self._batch_size)
|
self._cursor_obj.batch_size(self._batch_size)
|
||||||
|
|
||||||
@@ -1772,13 +1832,13 @@ class BaseQuerySet(object):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
total, data, types = self.exec_js(freq_func, field)
|
total, data, types = self.exec_js(freq_func, field)
|
||||||
values = {types.get(k): int(v) for k, v in iteritems(data)}
|
values = {types.get(k): int(v) for k, v in data.items()}
|
||||||
|
|
||||||
if normalize:
|
if normalize:
|
||||||
values = {k: float(v) / total for k, v in values.items()}
|
values = {k: float(v) / total for k, v in values.items()}
|
||||||
|
|
||||||
frequencies = {}
|
frequencies = {}
|
||||||
for k, v in iteritems(values):
|
for k, v in values.items():
|
||||||
if isinstance(k, float):
|
if isinstance(k, float):
|
||||||
if int(k) == k:
|
if int(k) == k:
|
||||||
k = int(k)
|
k = int(k)
|
||||||
@@ -1798,7 +1858,7 @@ class BaseQuerySet(object):
|
|||||||
field_parts = field.split(".")
|
field_parts = field.split(".")
|
||||||
try:
|
try:
|
||||||
field = ".".join(
|
field = ".".join(
|
||||||
f if isinstance(f, six.string_types) else f.db_field
|
f if isinstance(f, str) else f.db_field
|
||||||
for f in self._document._lookup_field(field_parts)
|
for f in self._document._lookup_field(field_parts)
|
||||||
)
|
)
|
||||||
db_field_paths.append(field)
|
db_field_paths.append(field)
|
||||||
@@ -1810,7 +1870,7 @@ class BaseQuerySet(object):
|
|||||||
for subdoc in subclasses:
|
for subdoc in subclasses:
|
||||||
try:
|
try:
|
||||||
subfield = ".".join(
|
subfield = ".".join(
|
||||||
f if isinstance(f, six.string_types) else f.db_field
|
f if isinstance(f, str) else f.db_field
|
||||||
for f in subdoc._lookup_field(field_parts)
|
for f in subdoc._lookup_field(field_parts)
|
||||||
)
|
)
|
||||||
db_field_paths.append(subfield)
|
db_field_paths.append(subfield)
|
||||||
@@ -1884,7 +1944,7 @@ class BaseQuerySet(object):
|
|||||||
field_name = match.group(1).split(".")
|
field_name = match.group(1).split(".")
|
||||||
fields = self._document._lookup_field(field_name)
|
fields = self._document._lookup_field(field_name)
|
||||||
# Substitute the correct name for the field into the javascript
|
# Substitute the correct name for the field into the javascript
|
||||||
return u'["%s"]' % fields[-1].db_field
|
return '["%s"]' % fields[-1].db_field
|
||||||
|
|
||||||
def field_path_sub(match):
|
def field_path_sub(match):
|
||||||
# Extract just the field name, and look up the field objects
|
# Extract just the field name, and look up the field objects
|
||||||
@@ -1914,23 +1974,3 @@ class BaseQuerySet(object):
|
|||||||
setattr(queryset, "_" + method_name, val)
|
setattr(queryset, "_" + method_name, val)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
# Deprecated
|
|
||||||
def ensure_index(self, **kwargs):
|
|
||||||
"""Deprecated use :func:`Document.ensure_index`"""
|
|
||||||
msg = (
|
|
||||||
"Doc.objects()._ensure_index() is deprecated. "
|
|
||||||
"Use Doc.ensure_index() instead."
|
|
||||||
)
|
|
||||||
warnings.warn(msg, DeprecationWarning)
|
|
||||||
self._document.__class__.ensure_index(**kwargs)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _ensure_indexes(self):
|
|
||||||
"""Deprecated use :func:`~Document.ensure_indexes`"""
|
|
||||||
msg = (
|
|
||||||
"Doc.objects()._ensure_indexes() is deprecated. "
|
|
||||||
"Use Doc.ensure_indexes() instead."
|
|
||||||
)
|
|
||||||
warnings.warn(msg, DeprecationWarning)
|
|
||||||
self._document.__class__.ensure_indexes()
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
__all__ = ("QueryFieldList",)
|
__all__ = ("QueryFieldList",)
|
||||||
|
|
||||||
|
|
||||||
class QueryFieldList(object):
|
class QueryFieldList:
|
||||||
"""Object that handles combinations of .only() and .exclude() calls"""
|
"""Object that handles combinations of .only() and .exclude() calls"""
|
||||||
|
|
||||||
ONLY = 1
|
ONLY = 1
|
||||||
@@ -69,8 +69,6 @@ class QueryFieldList(object):
|
|||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
return bool(self.fields)
|
return bool(self.fields)
|
||||||
|
|
||||||
__nonzero__ = __bool__ # For Py2 support
|
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
field_list = {field: self.value for field in self.fields}
|
field_list = {field: self.value for field in self.fields}
|
||||||
if self.slice:
|
if self.slice:
|
||||||
@@ -80,7 +78,7 @@ class QueryFieldList(object):
|
|||||||
return field_list
|
return field_list
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.fields = set([])
|
self.fields = set()
|
||||||
self.slice = {}
|
self.slice = {}
|
||||||
self.value = self.ONLY
|
self.value = self.ONLY
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from mongoengine.queryset.queryset import QuerySet
|
|||||||
__all__ = ("queryset_manager", "QuerySetManager")
|
__all__ = ("queryset_manager", "QuerySetManager")
|
||||||
|
|
||||||
|
|
||||||
class QuerySetManager(object):
|
class QuerySetManager:
|
||||||
"""
|
"""
|
||||||
The default QuerySet Manager.
|
The default QuerySet Manager.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import six
|
|
||||||
|
|
||||||
from mongoengine.errors import OperationError
|
from mongoengine.errors import OperationError
|
||||||
from mongoengine.queryset.base import (
|
from mongoengine.queryset.base import (
|
||||||
BaseQuerySet,
|
BaseQuerySet,
|
||||||
@@ -127,8 +125,8 @@ class QuerySet(BaseQuerySet):
|
|||||||
# Pull in ITER_CHUNK_SIZE docs from the database and store them in
|
# Pull in ITER_CHUNK_SIZE docs from the database and store them in
|
||||||
# the result cache.
|
# the result cache.
|
||||||
try:
|
try:
|
||||||
for _ in six.moves.range(ITER_CHUNK_SIZE):
|
for _ in range(ITER_CHUNK_SIZE):
|
||||||
self._result_cache.append(six.next(self))
|
self._result_cache.append(next(self))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
# Getting this exception means there are no more docs in the
|
# Getting this exception means there are no more docs in the
|
||||||
# db cursor. Set _has_more to False so that we can use that
|
# db cursor. Set _has_more to False so that we can use that
|
||||||
@@ -143,10 +141,10 @@ class QuerySet(BaseQuerySet):
|
|||||||
getting the count
|
getting the count
|
||||||
"""
|
"""
|
||||||
if with_limit_and_skip is False:
|
if with_limit_and_skip is False:
|
||||||
return super(QuerySet, self).count(with_limit_and_skip)
|
return super().count(with_limit_and_skip)
|
||||||
|
|
||||||
if self._len is None:
|
if self._len is None:
|
||||||
self._len = super(QuerySet, self).count(with_limit_and_skip)
|
self._len = super().count(with_limit_and_skip)
|
||||||
|
|
||||||
return self._len
|
return self._len
|
||||||
|
|
||||||
@@ -180,9 +178,9 @@ class QuerySetNoCache(BaseQuerySet):
|
|||||||
return ".. queryset mid-iteration .."
|
return ".. queryset mid-iteration .."
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for _ in six.moves.range(REPR_OUTPUT_SIZE + 1):
|
for _ in range(REPR_OUTPUT_SIZE + 1):
|
||||||
try:
|
try:
|
||||||
data.append(six.next(self))
|
data.append(next(self))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,12 @@ from collections import defaultdict
|
|||||||
from bson import ObjectId, SON
|
from bson import ObjectId, SON
|
||||||
from bson.dbref import DBRef
|
from bson.dbref import DBRef
|
||||||
import pymongo
|
import pymongo
|
||||||
import six
|
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine.base import UPDATE_OPERATORS
|
from mongoengine.base import UPDATE_OPERATORS
|
||||||
from mongoengine.common import _import_class
|
from mongoengine.common import _import_class
|
||||||
from mongoengine.errors import InvalidQueryError
|
from mongoengine.errors import InvalidQueryError
|
||||||
|
|
||||||
__all__ = ("query", "update")
|
__all__ = ("query", "update", "STRING_OPERATORS")
|
||||||
|
|
||||||
COMPARISON_OPERATORS = (
|
COMPARISON_OPERATORS = (
|
||||||
"ne",
|
"ne",
|
||||||
@@ -101,7 +99,7 @@ def query(_doc_cls=None, **kwargs):
|
|||||||
cleaned_fields = []
|
cleaned_fields = []
|
||||||
for field in fields:
|
for field in fields:
|
||||||
append_field = True
|
append_field = True
|
||||||
if isinstance(field, six.string_types):
|
if isinstance(field, str):
|
||||||
parts.append(field)
|
parts.append(field)
|
||||||
append_field = False
|
append_field = False
|
||||||
# is last and CachedReferenceField
|
# is last and CachedReferenceField
|
||||||
@@ -169,9 +167,9 @@ def query(_doc_cls=None, **kwargs):
|
|||||||
|
|
||||||
key = ".".join(parts)
|
key = ".".join(parts)
|
||||||
|
|
||||||
if op is None or key not in mongo_query:
|
if key not in mongo_query:
|
||||||
mongo_query[key] = value
|
mongo_query[key] = value
|
||||||
elif key in mongo_query:
|
else:
|
||||||
if isinstance(mongo_query[key], dict) and isinstance(value, dict):
|
if isinstance(mongo_query[key], dict) and isinstance(value, dict):
|
||||||
mongo_query[key].update(value)
|
mongo_query[key].update(value)
|
||||||
# $max/minDistance needs to come last - convert to SON
|
# $max/minDistance needs to come last - convert to SON
|
||||||
@@ -180,7 +178,7 @@ def query(_doc_cls=None, **kwargs):
|
|||||||
"$near" in value_dict or "$nearSphere" in value_dict
|
"$near" in value_dict or "$nearSphere" in value_dict
|
||||||
):
|
):
|
||||||
value_son = SON()
|
value_son = SON()
|
||||||
for k, v in iteritems(value_dict):
|
for k, v in value_dict.items():
|
||||||
if k == "$maxDistance" or k == "$minDistance":
|
if k == "$maxDistance" or k == "$minDistance":
|
||||||
continue
|
continue
|
||||||
value_son[k] = v
|
value_son[k] = v
|
||||||
@@ -281,7 +279,7 @@ def update(_doc_cls=None, **update):
|
|||||||
appended_sub_field = False
|
appended_sub_field = False
|
||||||
for field in fields:
|
for field in fields:
|
||||||
append_field = True
|
append_field = True
|
||||||
if isinstance(field, six.string_types):
|
if isinstance(field, str):
|
||||||
# Convert the S operator to $
|
# Convert the S operator to $
|
||||||
if field == "S":
|
if field == "S":
|
||||||
field = "$"
|
field = "$"
|
||||||
@@ -435,7 +433,9 @@ def _geo_operator(field, op, value):
|
|||||||
value = {"$near": _infer_geometry(value)}
|
value = {"$near": _infer_geometry(value)}
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'Geo method "%s" has not been implemented for a %s ' % (op, field._name)
|
'Geo method "{}" has not been implemented for a {} '.format(
|
||||||
|
op, field._name
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import copy
|
import copy
|
||||||
|
import warnings
|
||||||
|
|
||||||
from mongoengine.errors import InvalidQueryError
|
from mongoengine.errors import InvalidQueryError
|
||||||
from mongoengine.queryset import transform
|
from mongoengine.queryset import transform
|
||||||
@@ -6,7 +7,7 @@ from mongoengine.queryset import transform
|
|||||||
__all__ = ("Q", "QNode")
|
__all__ = ("Q", "QNode")
|
||||||
|
|
||||||
|
|
||||||
class QNodeVisitor(object):
|
class QNodeVisitor:
|
||||||
"""Base visitor class for visiting Q-object nodes in a query tree.
|
"""Base visitor class for visiting Q-object nodes in a query tree.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -78,7 +79,7 @@ class QueryCompilerVisitor(QNodeVisitor):
|
|||||||
return transform.query(self.document, **query.query)
|
return transform.query(self.document, **query.query)
|
||||||
|
|
||||||
|
|
||||||
class QNode(object):
|
class QNode:
|
||||||
"""Base class for nodes in query trees."""
|
"""Base class for nodes in query trees."""
|
||||||
|
|
||||||
AND = 0
|
AND = 0
|
||||||
@@ -96,9 +97,11 @@ class QNode(object):
|
|||||||
"""Combine this node with another node into a QCombination
|
"""Combine this node with another node into a QCombination
|
||||||
object.
|
object.
|
||||||
"""
|
"""
|
||||||
|
# If the other Q() is empty, ignore it and just use `self`.
|
||||||
if getattr(other, "empty", True):
|
if getattr(other, "empty", True):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
# Or if this Q is empty, ignore it and just use `other`.
|
||||||
if self.empty:
|
if self.empty:
|
||||||
return other
|
return other
|
||||||
|
|
||||||
@@ -106,6 +109,8 @@ class QNode(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def empty(self):
|
def empty(self):
|
||||||
|
msg = "'empty' property is deprecated in favour of using 'not bool(filter)'"
|
||||||
|
warnings.warn(msg, DeprecationWarning)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __or__(self, other):
|
def __or__(self, other):
|
||||||
@@ -135,6 +140,9 @@ class QCombination(QNode):
|
|||||||
op = " & " if self.operation is self.AND else " | "
|
op = " & " if self.operation is self.AND else " | "
|
||||||
return "(%s)" % op.join([repr(node) for node in self.children])
|
return "(%s)" % op.join([repr(node) for node in self.children])
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(self.children)
|
||||||
|
|
||||||
def accept(self, visitor):
|
def accept(self, visitor):
|
||||||
for i in range(len(self.children)):
|
for i in range(len(self.children)):
|
||||||
if isinstance(self.children[i], QNode):
|
if isinstance(self.children[i], QNode):
|
||||||
@@ -144,8 +152,17 @@ class QCombination(QNode):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def empty(self):
|
def empty(self):
|
||||||
|
msg = "'empty' property is deprecated in favour of using 'not bool(filter)'"
|
||||||
|
warnings.warn(msg, DeprecationWarning)
|
||||||
return not bool(self.children)
|
return not bool(self.children)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return (
|
||||||
|
self.__class__ == other.__class__
|
||||||
|
and self.operation == other.operation
|
||||||
|
and self.children == other.children
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Q(QNode):
|
class Q(QNode):
|
||||||
"""A simple query object, used in a query tree to build up more complex
|
"""A simple query object, used in a query tree to build up more complex
|
||||||
@@ -158,6 +175,12 @@ class Q(QNode):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Q(**%s)" % repr(self.query)
|
return "Q(**%s)" % repr(self.query)
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(self.query)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.__class__ == other.__class__ and self.query == other.query
|
||||||
|
|
||||||
def accept(self, visitor):
|
def accept(self, visitor):
|
||||||
return visitor.visit_query(self)
|
return visitor.visit_query(self)
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ try:
|
|||||||
signals_available = True
|
signals_available = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
||||||
class Namespace(object):
|
class Namespace:
|
||||||
def signal(self, name, doc=None):
|
def signal(self, name, doc=None):
|
||||||
return _FakeSignal(name, doc)
|
return _FakeSignal(name, doc)
|
||||||
|
|
||||||
class _FakeSignal(object):
|
class _FakeSignal:
|
||||||
"""If blinker is unavailable, create a fake class with the same
|
"""If blinker is unavailable, create a fake class with the same
|
||||||
interface that allows sending of signals but will fail with an
|
interface that allows sending of signals but will fail with an
|
||||||
error on anything else. Instead of doing anything on send, it
|
error on anything else. Instead of doing anything on send, it
|
||||||
|
|||||||
8
requirements-dev.txt
Normal file
8
requirements-dev.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
black
|
||||||
|
flake8
|
||||||
|
flake8-import-order
|
||||||
|
pre-commit
|
||||||
|
pytest
|
||||||
|
ipdb
|
||||||
|
ipython
|
||||||
|
tox
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
black
|
|
||||||
flake8
|
|
||||||
flake8-import-order
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
nose
|
|
||||||
pymongo>=3.4
|
pymongo>=3.4
|
||||||
six==1.10.0
|
|
||||||
Sphinx==1.5.5
|
Sphinx==1.5.5
|
||||||
sphinx-rtd-theme==0.2.4
|
sphinx-rtd-theme==0.2.4
|
||||||
|
|||||||
11
setup.cfg
11
setup.cfg
@@ -1,11 +1,10 @@
|
|||||||
[nosetests]
|
|
||||||
verbosity=2
|
|
||||||
detailed-errors=1
|
|
||||||
#tests=tests
|
|
||||||
cover-package=mongoengine
|
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
ignore=E501,F401,F403,F405,I201,I202,W504, W605, W503
|
ignore=E501,F401,F403,F405,I201,I202,W504, W605, W503
|
||||||
exclude=build,dist,docs,venv,venv3,.tox,.eggs,tests
|
exclude=build,dist,docs,venv,venv3,.tox,.eggs,tests
|
||||||
max-complexity=47
|
max-complexity=47
|
||||||
application-import-names=mongoengine,tests
|
application-import-names=mongoengine,tests
|
||||||
|
|
||||||
|
[tool:pytest]
|
||||||
|
# Limits the discovery to tests directory
|
||||||
|
# avoids that it runs for instance the benchmark
|
||||||
|
testpaths = tests
|
||||||
|
|||||||
93
setup.py
93
setup.py
@@ -1,6 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from pkg_resources import normalize_path
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
from setuptools.command.test import test as TestCommand
|
||||||
|
|
||||||
# Hack to silence atexit traceback in newer python versions
|
# Hack to silence atexit traceback in newer python versions
|
||||||
try:
|
try:
|
||||||
@@ -24,6 +27,62 @@ def get_version(version_tuple):
|
|||||||
return ".".join(map(str, version_tuple))
|
return ".".join(map(str, version_tuple))
|
||||||
|
|
||||||
|
|
||||||
|
class PyTest(TestCommand):
|
||||||
|
"""Will force pytest to search for tests inside the build directory
|
||||||
|
for 2to3 converted code (used by tox), instead of the current directory.
|
||||||
|
Required as long as we need 2to3
|
||||||
|
|
||||||
|
Known Limitation: https://tox.readthedocs.io/en/latest/example/pytest.html#known-issues-and-limitations
|
||||||
|
Source: https://www.hackzine.org/python-testing-with-pytest-and-2to3-plus-tox-and-travis-ci.html
|
||||||
|
"""
|
||||||
|
|
||||||
|
# https://pytest.readthedocs.io/en/2.7.3/goodpractises.html#integration-with-setuptools-test-commands
|
||||||
|
# Allows to provide pytest command argument through the test runner command `python setup.py test`
|
||||||
|
# e.g: `python setup.py test -a "-k=test"`
|
||||||
|
# This only works for 1 argument though
|
||||||
|
user_options = [("pytest-args=", "a", "Arguments to pass to py.test")]
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
TestCommand.initialize_options(self)
|
||||||
|
self.pytest_args = ""
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
TestCommand.finalize_options(self)
|
||||||
|
self.test_args = ["tests"]
|
||||||
|
self.test_suite = True
|
||||||
|
|
||||||
|
def run_tests(self):
|
||||||
|
# import here, cause outside the eggs aren't loaded
|
||||||
|
from pkg_resources import _namespace_packages
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# Purge modules under test from sys.modules. The test loader will
|
||||||
|
# re-import them from the build location. Required when 2to3 is used
|
||||||
|
# with namespace packages.
|
||||||
|
if sys.version_info >= (3,) and getattr(self.distribution, "use_2to3", False):
|
||||||
|
module = self.test_args[-1].split(".")[0]
|
||||||
|
if module in _namespace_packages:
|
||||||
|
del_modules = []
|
||||||
|
if module in sys.modules:
|
||||||
|
del_modules.append(module)
|
||||||
|
module += "."
|
||||||
|
for name in sys.modules:
|
||||||
|
if name.startswith(module):
|
||||||
|
del_modules.append(name)
|
||||||
|
map(sys.modules.__delitem__, del_modules)
|
||||||
|
|
||||||
|
# Run on the build directory for 2to3-built code
|
||||||
|
# This will prevent the old 2.x code from being found
|
||||||
|
# by py.test discovery mechanism, that apparently
|
||||||
|
# ignores sys.path..
|
||||||
|
ei_cmd = self.get_finalized_command("egg_info")
|
||||||
|
self.test_args = [normalize_path(ei_cmd.egg_base)]
|
||||||
|
|
||||||
|
cmd_args = self.test_args + ([self.pytest_args] if self.pytest_args else [])
|
||||||
|
errno = pytest.main(cmd_args)
|
||||||
|
sys.exit(errno)
|
||||||
|
|
||||||
|
|
||||||
# Dirty hack to get version number from monogengine/__init__.py - we can't
|
# Dirty hack to get version number from monogengine/__init__.py - we can't
|
||||||
# import it as it depends on PyMongo and PyMongo isn't installed until this
|
# import it as it depends on PyMongo and PyMongo isn't installed until this
|
||||||
# file is read
|
# file is read
|
||||||
@@ -33,16 +92,16 @@ version_line = list(filter(lambda l: l.startswith("VERSION"), open(init)))[0]
|
|||||||
VERSION = get_version(eval(version_line.split("=")[-1]))
|
VERSION = get_version(eval(version_line.split("=")[-1]))
|
||||||
|
|
||||||
CLASSIFIERS = [
|
CLASSIFIERS = [
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 2",
|
|
||||||
"Programming Language :: Python :: 2.7",
|
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.5",
|
"Programming Language :: Python :: 3.5",
|
||||||
"Programming Language :: Python :: 3.6",
|
"Programming Language :: Python :: 3.6",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
"Programming Language :: Python :: Implementation :: CPython",
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
"Programming Language :: Python :: Implementation :: PyPy",
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
"Topic :: Database",
|
"Topic :: Database",
|
||||||
@@ -51,17 +110,20 @@ CLASSIFIERS = [
|
|||||||
|
|
||||||
extra_opts = {
|
extra_opts = {
|
||||||
"packages": find_packages(exclude=["tests", "tests.*"]),
|
"packages": find_packages(exclude=["tests", "tests.*"]),
|
||||||
"tests_require": ["nose", "coverage==4.2", "blinker", "Pillow>=2.0.0"],
|
"tests_require": [
|
||||||
|
"pytest<5.0",
|
||||||
|
"pytest-cov",
|
||||||
|
"coverage<5.0", # recent coverage switched to sqlite format for the .coverage file which isn't handled properly by coveralls
|
||||||
|
"blinker",
|
||||||
|
"Pillow>=2.0.0, <7.0.0", # 7.0.0 dropped Python2 support
|
||||||
|
],
|
||||||
}
|
}
|
||||||
if sys.version_info[0] == 3:
|
|
||||||
extra_opts["use_2to3"] = True
|
if "test" in sys.argv:
|
||||||
if "test" in sys.argv or "nosetests" in sys.argv:
|
extra_opts["packages"] = find_packages()
|
||||||
extra_opts["packages"] = find_packages()
|
extra_opts["package_data"] = {
|
||||||
extra_opts["package_data"] = {
|
"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]
|
||||||
"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]
|
}
|
||||||
}
|
|
||||||
else:
|
|
||||||
extra_opts["tests_require"] += ["python-dateutil"]
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="mongoengine",
|
name="mongoengine",
|
||||||
@@ -78,7 +140,8 @@ setup(
|
|||||||
long_description=LONG_DESCRIPTION,
|
long_description=LONG_DESCRIPTION,
|
||||||
platforms=["any"],
|
platforms=["any"],
|
||||||
classifiers=CLASSIFIERS,
|
classifiers=CLASSIFIERS,
|
||||||
install_requires=["pymongo>=3.4", "six"],
|
python_requires=">=3.5",
|
||||||
test_suite="nose.collector",
|
install_requires=["pymongo>=3.4, <4.0"],
|
||||||
|
cmdclass={"test": PyTest},
|
||||||
**extra_opts
|
**extra_opts
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
from .all_warnings import AllWarnings
|
|
||||||
from .document import *
|
|
||||||
from .queryset import *
|
|
||||||
from .fields import *
|
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
"""
|
|
||||||
This test has been put into a module. This is because it tests warnings that
|
|
||||||
only get triggered on first hit. This way we can ensure its imported into the
|
|
||||||
top level and called first by the test suite.
|
|
||||||
"""
|
|
||||||
import unittest
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
from mongoengine import *
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ("AllWarnings",)
|
|
||||||
|
|
||||||
|
|
||||||
class AllWarnings(unittest.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
connect(db="mongoenginetest")
|
|
||||||
self.warning_list = []
|
|
||||||
self.showwarning_default = warnings.showwarning
|
|
||||||
warnings.showwarning = self.append_to_warning_list
|
|
||||||
|
|
||||||
def append_to_warning_list(self, message, category, *args):
|
|
||||||
self.warning_list.append({"message": message, "category": category})
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
# restore default handling of warnings
|
|
||||||
warnings.showwarning = self.showwarning_default
|
|
||||||
|
|
||||||
def test_document_collection_syntax_warning(self):
|
|
||||||
class NonAbstractBase(Document):
|
|
||||||
meta = {"allow_inheritance": True}
|
|
||||||
|
|
||||||
class InheritedDocumentFailTest(NonAbstractBase):
|
|
||||||
meta = {"collection": "fail"}
|
|
||||||
|
|
||||||
warning = self.warning_list[0]
|
|
||||||
self.assertEqual(SyntaxWarning, warning["category"])
|
|
||||||
self.assertEqual(
|
|
||||||
"non_abstract_base", InheritedDocumentFailTest._get_collection_name()
|
|
||||||
)
|
|
||||||
|
|||||||
35
tests/all_warnings/test_warnings.py
Normal file
35
tests/all_warnings/test_warnings.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
This test has been put into a module. This is because it tests warnings that
|
||||||
|
only get triggered on first hit. This way we can ensure its imported into the
|
||||||
|
top level and called first by the test suite.
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
|
|
||||||
|
|
||||||
|
class TestAllWarnings(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
connect(db="mongoenginetest")
|
||||||
|
self.warning_list = []
|
||||||
|
self.showwarning_default = warnings.showwarning
|
||||||
|
warnings.showwarning = self.append_to_warning_list
|
||||||
|
|
||||||
|
def append_to_warning_list(self, message, category, *args):
|
||||||
|
self.warning_list.append({"message": message, "category": category})
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
# restore default handling of warnings
|
||||||
|
warnings.showwarning = self.showwarning_default
|
||||||
|
|
||||||
|
def test_document_collection_syntax_warning(self):
|
||||||
|
class NonAbstractBase(Document):
|
||||||
|
meta = {"allow_inheritance": True}
|
||||||
|
|
||||||
|
class InheritedDocumentFailTest(NonAbstractBase):
|
||||||
|
meta = {"collection": "fail"}
|
||||||
|
|
||||||
|
warning = self.warning_list[0]
|
||||||
|
assert SyntaxWarning == warning["category"]
|
||||||
|
assert "non_abstract_base" == InheritedDocumentFailTest._get_collection_name()
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import unittest
|
|
||||||
|
|
||||||
from .class_methods import *
|
|
||||||
from .delta import *
|
|
||||||
from .dynamic import *
|
|
||||||
from .indexes import *
|
|
||||||
from .inheritance import *
|
|
||||||
from .instance import *
|
|
||||||
from .json_serialisation import *
|
|
||||||
from .validation import *
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
|
|||||||
@@ -2,15 +2,12 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.pymongo_support import list_collection_names
|
|
||||||
|
|
||||||
from mongoengine.queryset import NULLIFY, PULL
|
|
||||||
from mongoengine.connection import get_db
|
from mongoengine.connection import get_db
|
||||||
|
from mongoengine.pymongo_support import list_collection_names
|
||||||
__all__ = ("ClassMethodsTest",)
|
from mongoengine.queryset import NULLIFY, PULL
|
||||||
|
|
||||||
|
|
||||||
class ClassMethodsTest(unittest.TestCase):
|
class TestClassMethods(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
connect(db="mongoenginetest")
|
connect(db="mongoenginetest")
|
||||||
self.db = get_db()
|
self.db = get_db()
|
||||||
@@ -32,43 +29,40 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
def test_definition(self):
|
def test_definition(self):
|
||||||
"""Ensure that document may be defined using fields.
|
"""Ensure that document may be defined using fields.
|
||||||
"""
|
"""
|
||||||
self.assertEqual(
|
assert ["_cls", "age", "id", "name"] == sorted(self.Person._fields.keys())
|
||||||
["_cls", "age", "id", "name"], sorted(self.Person._fields.keys())
|
assert ["IntField", "ObjectIdField", "StringField", "StringField"] == sorted(
|
||||||
)
|
[x.__class__.__name__ for x in self.Person._fields.values()]
|
||||||
self.assertEqual(
|
|
||||||
["IntField", "ObjectIdField", "StringField", "StringField"],
|
|
||||||
sorted([x.__class__.__name__ for x in self.Person._fields.values()]),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_get_db(self):
|
def test_get_db(self):
|
||||||
"""Ensure that get_db returns the expected db.
|
"""Ensure that get_db returns the expected db.
|
||||||
"""
|
"""
|
||||||
db = self.Person._get_db()
|
db = self.Person._get_db()
|
||||||
self.assertEqual(self.db, db)
|
assert self.db == db
|
||||||
|
|
||||||
def test_get_collection_name(self):
|
def test_get_collection_name(self):
|
||||||
"""Ensure that get_collection_name returns the expected collection
|
"""Ensure that get_collection_name returns the expected collection
|
||||||
name.
|
name.
|
||||||
"""
|
"""
|
||||||
collection_name = "person"
|
collection_name = "person"
|
||||||
self.assertEqual(collection_name, self.Person._get_collection_name())
|
assert collection_name == self.Person._get_collection_name()
|
||||||
|
|
||||||
def test_get_collection(self):
|
def test_get_collection(self):
|
||||||
"""Ensure that get_collection returns the expected collection.
|
"""Ensure that get_collection returns the expected collection.
|
||||||
"""
|
"""
|
||||||
collection_name = "person"
|
collection_name = "person"
|
||||||
collection = self.Person._get_collection()
|
collection = self.Person._get_collection()
|
||||||
self.assertEqual(self.db[collection_name], collection)
|
assert self.db[collection_name] == collection
|
||||||
|
|
||||||
def test_drop_collection(self):
|
def test_drop_collection(self):
|
||||||
"""Ensure that the collection may be dropped from the database.
|
"""Ensure that the collection may be dropped from the database.
|
||||||
"""
|
"""
|
||||||
collection_name = "person"
|
collection_name = "person"
|
||||||
self.Person(name="Test").save()
|
self.Person(name="Test").save()
|
||||||
self.assertIn(collection_name, list_collection_names(self.db))
|
assert collection_name in list_collection_names(self.db)
|
||||||
|
|
||||||
self.Person.drop_collection()
|
self.Person.drop_collection()
|
||||||
self.assertNotIn(collection_name, list_collection_names(self.db))
|
assert collection_name not in list_collection_names(self.db)
|
||||||
|
|
||||||
def test_register_delete_rule(self):
|
def test_register_delete_rule(self):
|
||||||
"""Ensure that register delete rule adds a delete rule to the document
|
"""Ensure that register delete rule adds a delete rule to the document
|
||||||
@@ -78,12 +72,10 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
class Job(Document):
|
class Job(Document):
|
||||||
employee = ReferenceField(self.Person)
|
employee = ReferenceField(self.Person)
|
||||||
|
|
||||||
self.assertEqual(self.Person._meta.get("delete_rules"), None)
|
assert self.Person._meta.get("delete_rules") is None
|
||||||
|
|
||||||
self.Person.register_delete_rule(Job, "employee", NULLIFY)
|
self.Person.register_delete_rule(Job, "employee", NULLIFY)
|
||||||
self.assertEqual(
|
assert self.Person._meta["delete_rules"] == {(Job, "employee"): NULLIFY}
|
||||||
self.Person._meta["delete_rules"], {(Job, "employee"): NULLIFY}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_compare_indexes(self):
|
def test_compare_indexes(self):
|
||||||
""" Ensure that the indexes are properly created and that
|
""" Ensure that the indexes are properly created and that
|
||||||
@@ -101,22 +93,22 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
BlogPost.ensure_indexes()
|
BlogPost.ensure_indexes()
|
||||||
self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
|
assert BlogPost.compare_indexes() == {"missing": [], "extra": []}
|
||||||
|
|
||||||
BlogPost.ensure_index(["author", "description"])
|
BlogPost.ensure_index(["author", "description"])
|
||||||
self.assertEqual(
|
assert BlogPost.compare_indexes() == {
|
||||||
BlogPost.compare_indexes(),
|
"missing": [],
|
||||||
{"missing": [], "extra": [[("author", 1), ("description", 1)]]},
|
"extra": [[("author", 1), ("description", 1)]],
|
||||||
)
|
}
|
||||||
|
|
||||||
BlogPost._get_collection().drop_index("author_1_description_1")
|
BlogPost._get_collection().drop_index("author_1_description_1")
|
||||||
self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
|
assert BlogPost.compare_indexes() == {"missing": [], "extra": []}
|
||||||
|
|
||||||
BlogPost._get_collection().drop_index("author_1_title_1")
|
BlogPost._get_collection().drop_index("author_1_title_1")
|
||||||
self.assertEqual(
|
assert BlogPost.compare_indexes() == {
|
||||||
BlogPost.compare_indexes(),
|
"missing": [[("author", 1), ("title", 1)]],
|
||||||
{"missing": [[("author", 1), ("title", 1)]], "extra": []},
|
"extra": [],
|
||||||
)
|
}
|
||||||
|
|
||||||
def test_compare_indexes_inheritance(self):
|
def test_compare_indexes_inheritance(self):
|
||||||
""" Ensure that the indexes are properly created and that
|
""" Ensure that the indexes are properly created and that
|
||||||
@@ -141,22 +133,22 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
|
|
||||||
BlogPost.ensure_indexes()
|
BlogPost.ensure_indexes()
|
||||||
BlogPostWithTags.ensure_indexes()
|
BlogPostWithTags.ensure_indexes()
|
||||||
self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
|
assert BlogPost.compare_indexes() == {"missing": [], "extra": []}
|
||||||
|
|
||||||
BlogPostWithTags.ensure_index(["author", "tag_list"])
|
BlogPostWithTags.ensure_index(["author", "tag_list"])
|
||||||
self.assertEqual(
|
assert BlogPost.compare_indexes() == {
|
||||||
BlogPost.compare_indexes(),
|
"missing": [],
|
||||||
{"missing": [], "extra": [[("_cls", 1), ("author", 1), ("tag_list", 1)]]},
|
"extra": [[("_cls", 1), ("author", 1), ("tag_list", 1)]],
|
||||||
)
|
}
|
||||||
|
|
||||||
BlogPostWithTags._get_collection().drop_index("_cls_1_author_1_tag_list_1")
|
BlogPostWithTags._get_collection().drop_index("_cls_1_author_1_tag_list_1")
|
||||||
self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
|
assert BlogPost.compare_indexes() == {"missing": [], "extra": []}
|
||||||
|
|
||||||
BlogPostWithTags._get_collection().drop_index("_cls_1_author_1_tags_1")
|
BlogPostWithTags._get_collection().drop_index("_cls_1_author_1_tags_1")
|
||||||
self.assertEqual(
|
assert BlogPost.compare_indexes() == {
|
||||||
BlogPost.compare_indexes(),
|
"missing": [[("_cls", 1), ("author", 1), ("tags", 1)]],
|
||||||
{"missing": [[("_cls", 1), ("author", 1), ("tags", 1)]], "extra": []},
|
"extra": [],
|
||||||
)
|
}
|
||||||
|
|
||||||
def test_compare_indexes_multiple_subclasses(self):
|
def test_compare_indexes_multiple_subclasses(self):
|
||||||
""" Ensure that compare_indexes behaves correctly if called from a
|
""" Ensure that compare_indexes behaves correctly if called from a
|
||||||
@@ -185,13 +177,9 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
BlogPostWithTags.ensure_indexes()
|
BlogPostWithTags.ensure_indexes()
|
||||||
BlogPostWithCustomField.ensure_indexes()
|
BlogPostWithCustomField.ensure_indexes()
|
||||||
|
|
||||||
self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
|
assert BlogPost.compare_indexes() == {"missing": [], "extra": []}
|
||||||
self.assertEqual(
|
assert BlogPostWithTags.compare_indexes() == {"missing": [], "extra": []}
|
||||||
BlogPostWithTags.compare_indexes(), {"missing": [], "extra": []}
|
assert BlogPostWithCustomField.compare_indexes() == {"missing": [], "extra": []}
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
BlogPostWithCustomField.compare_indexes(), {"missing": [], "extra": []}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_compare_indexes_for_text_indexes(self):
|
def test_compare_indexes_for_text_indexes(self):
|
||||||
""" Ensure that compare_indexes behaves correctly for text indexes """
|
""" Ensure that compare_indexes behaves correctly for text indexes """
|
||||||
@@ -213,7 +201,7 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
Doc.ensure_indexes()
|
Doc.ensure_indexes()
|
||||||
actual = Doc.compare_indexes()
|
actual = Doc.compare_indexes()
|
||||||
expected = {"missing": [], "extra": []}
|
expected = {"missing": [], "extra": []}
|
||||||
self.assertEqual(actual, expected)
|
assert actual == expected
|
||||||
|
|
||||||
def test_list_indexes_inheritance(self):
|
def test_list_indexes_inheritance(self):
|
||||||
""" ensure that all of the indexes are listed regardless of the super-
|
""" ensure that all of the indexes are listed regardless of the super-
|
||||||
@@ -243,19 +231,14 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
BlogPostWithTags.ensure_indexes()
|
BlogPostWithTags.ensure_indexes()
|
||||||
BlogPostWithTagsAndExtraText.ensure_indexes()
|
BlogPostWithTagsAndExtraText.ensure_indexes()
|
||||||
|
|
||||||
self.assertEqual(BlogPost.list_indexes(), BlogPostWithTags.list_indexes())
|
assert BlogPost.list_indexes() == BlogPostWithTags.list_indexes()
|
||||||
self.assertEqual(
|
assert BlogPost.list_indexes() == BlogPostWithTagsAndExtraText.list_indexes()
|
||||||
BlogPost.list_indexes(), BlogPostWithTagsAndExtraText.list_indexes()
|
assert BlogPost.list_indexes() == [
|
||||||
)
|
[("_cls", 1), ("author", 1), ("tags", 1)],
|
||||||
self.assertEqual(
|
[("_cls", 1), ("author", 1), ("tags", 1), ("extra_text", 1)],
|
||||||
BlogPost.list_indexes(),
|
[(u"_id", 1)],
|
||||||
[
|
[("_cls", 1)],
|
||||||
[("_cls", 1), ("author", 1), ("tags", 1)],
|
]
|
||||||
[("_cls", 1), ("author", 1), ("tags", 1), ("extra_text", 1)],
|
|
||||||
[(u"_id", 1)],
|
|
||||||
[("_cls", 1)],
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_register_delete_rule_inherited(self):
|
def test_register_delete_rule_inherited(self):
|
||||||
class Vaccine(Document):
|
class Vaccine(Document):
|
||||||
@@ -274,8 +257,8 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
class Cat(Animal):
|
class Cat(Animal):
|
||||||
name = StringField(required=True)
|
name = StringField(required=True)
|
||||||
|
|
||||||
self.assertEqual(Vaccine._meta["delete_rules"][(Animal, "vaccine_made")], PULL)
|
assert Vaccine._meta["delete_rules"][(Animal, "vaccine_made")] == PULL
|
||||||
self.assertEqual(Vaccine._meta["delete_rules"][(Cat, "vaccine_made")], PULL)
|
assert Vaccine._meta["delete_rules"][(Cat, "vaccine_made")] == PULL
|
||||||
|
|
||||||
def test_collection_naming(self):
|
def test_collection_naming(self):
|
||||||
"""Ensure that a collection with a specified name may be used.
|
"""Ensure that a collection with a specified name may be used.
|
||||||
@@ -284,19 +267,17 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
class DefaultNamingTest(Document):
|
class DefaultNamingTest(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(
|
assert "default_naming_test" == DefaultNamingTest._get_collection_name()
|
||||||
"default_naming_test", DefaultNamingTest._get_collection_name()
|
|
||||||
)
|
|
||||||
|
|
||||||
class CustomNamingTest(Document):
|
class CustomNamingTest(Document):
|
||||||
meta = {"collection": "pimp_my_collection"}
|
meta = {"collection": "pimp_my_collection"}
|
||||||
|
|
||||||
self.assertEqual("pimp_my_collection", CustomNamingTest._get_collection_name())
|
assert "pimp_my_collection" == CustomNamingTest._get_collection_name()
|
||||||
|
|
||||||
class DynamicNamingTest(Document):
|
class DynamicNamingTest(Document):
|
||||||
meta = {"collection": lambda c: "DYNAMO"}
|
meta = {"collection": lambda c: "DYNAMO"}
|
||||||
|
|
||||||
self.assertEqual("DYNAMO", DynamicNamingTest._get_collection_name())
|
assert "DYNAMO" == DynamicNamingTest._get_collection_name()
|
||||||
|
|
||||||
# Use Abstract class to handle backwards compatibility
|
# Use Abstract class to handle backwards compatibility
|
||||||
class BaseDocument(Document):
|
class BaseDocument(Document):
|
||||||
@@ -305,14 +286,12 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
class OldNamingConvention(BaseDocument):
|
class OldNamingConvention(BaseDocument):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(
|
assert "oldnamingconvention" == OldNamingConvention._get_collection_name()
|
||||||
"oldnamingconvention", OldNamingConvention._get_collection_name()
|
|
||||||
)
|
|
||||||
|
|
||||||
class InheritedAbstractNamingTest(BaseDocument):
|
class InheritedAbstractNamingTest(BaseDocument):
|
||||||
meta = {"collection": "wibble"}
|
meta = {"collection": "wibble"}
|
||||||
|
|
||||||
self.assertEqual("wibble", InheritedAbstractNamingTest._get_collection_name())
|
assert "wibble" == InheritedAbstractNamingTest._get_collection_name()
|
||||||
|
|
||||||
# Mixin tests
|
# Mixin tests
|
||||||
class BaseMixin(object):
|
class BaseMixin(object):
|
||||||
@@ -321,8 +300,9 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
class OldMixinNamingConvention(Document, BaseMixin):
|
class OldMixinNamingConvention(Document, BaseMixin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
"oldmixinnamingconvention", OldMixinNamingConvention._get_collection_name()
|
"oldmixinnamingconvention"
|
||||||
|
== OldMixinNamingConvention._get_collection_name()
|
||||||
)
|
)
|
||||||
|
|
||||||
class BaseMixin(object):
|
class BaseMixin(object):
|
||||||
@@ -334,7 +314,7 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
class MyDocument(BaseDocument):
|
class MyDocument(BaseDocument):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual("basedocument", MyDocument._get_collection_name())
|
assert "basedocument" == MyDocument._get_collection_name()
|
||||||
|
|
||||||
def test_custom_collection_name_operations(self):
|
def test_custom_collection_name_operations(self):
|
||||||
"""Ensure that a collection with a specified name is used as expected.
|
"""Ensure that a collection with a specified name is used as expected.
|
||||||
@@ -346,16 +326,16 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
meta = {"collection": collection_name}
|
meta = {"collection": collection_name}
|
||||||
|
|
||||||
Person(name="Test User").save()
|
Person(name="Test User").save()
|
||||||
self.assertIn(collection_name, list_collection_names(self.db))
|
assert collection_name in list_collection_names(self.db)
|
||||||
|
|
||||||
user_obj = self.db[collection_name].find_one()
|
user_obj = self.db[collection_name].find_one()
|
||||||
self.assertEqual(user_obj["name"], "Test User")
|
assert user_obj["name"] == "Test User"
|
||||||
|
|
||||||
user_obj = Person.objects[0]
|
user_obj = Person.objects[0]
|
||||||
self.assertEqual(user_obj.name, "Test User")
|
assert user_obj.name == "Test User"
|
||||||
|
|
||||||
Person.drop_collection()
|
Person.drop_collection()
|
||||||
self.assertNotIn(collection_name, list_collection_names(self.db))
|
assert collection_name not in list_collection_names(self.db)
|
||||||
|
|
||||||
def test_collection_name_and_primary(self):
|
def test_collection_name_and_primary(self):
|
||||||
"""Ensure that a collection with a specified name may be used.
|
"""Ensure that a collection with a specified name may be used.
|
||||||
@@ -368,7 +348,7 @@ class ClassMethodsTest(unittest.TestCase):
|
|||||||
Person(name="Test User").save()
|
Person(name="Test User").save()
|
||||||
|
|
||||||
user_obj = Person.objects.first()
|
user_obj = Person.objects.first()
|
||||||
self.assertEqual(user_obj.name, "Test User")
|
assert user_obj.name == "Test User"
|
||||||
|
|
||||||
Person.drop_collection()
|
Person.drop_collection()
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
@@ -25,15 +27,15 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
p.name = "James"
|
p.name = "James"
|
||||||
p.age = 34
|
p.age = 34
|
||||||
|
|
||||||
self.assertEqual(p.to_mongo(), {"_cls": "Person", "name": "James", "age": 34})
|
assert p.to_mongo() == {"_cls": "Person", "name": "James", "age": 34}
|
||||||
self.assertEqual(p.to_mongo().keys(), ["_cls", "name", "age"])
|
assert p.to_mongo().keys() == ["_cls", "name", "age"]
|
||||||
p.save()
|
p.save()
|
||||||
self.assertEqual(p.to_mongo().keys(), ["_id", "_cls", "name", "age"])
|
assert p.to_mongo().keys() == ["_id", "_cls", "name", "age"]
|
||||||
|
|
||||||
self.assertEqual(self.Person.objects.first().age, 34)
|
assert self.Person.objects.first().age == 34
|
||||||
|
|
||||||
# Confirm no changes to self.Person
|
# Confirm no changes to self.Person
|
||||||
self.assertFalse(hasattr(self.Person, "age"))
|
assert not hasattr(self.Person, "age")
|
||||||
|
|
||||||
def test_change_scope_of_variable(self):
|
def test_change_scope_of_variable(self):
|
||||||
"""Test changing the scope of a dynamic field has no adverse effects"""
|
"""Test changing the scope of a dynamic field has no adverse effects"""
|
||||||
@@ -47,7 +49,7 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
p = self.Person.objects.get()
|
p = self.Person.objects.get()
|
||||||
self.assertEqual(p.misc, {"hello": "world"})
|
assert p.misc == {"hello": "world"}
|
||||||
|
|
||||||
def test_delete_dynamic_field(self):
|
def test_delete_dynamic_field(self):
|
||||||
"""Test deleting a dynamic field works"""
|
"""Test deleting a dynamic field works"""
|
||||||
@@ -62,19 +64,19 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
p = self.Person.objects.get()
|
p = self.Person.objects.get()
|
||||||
self.assertEqual(p.misc, {"hello": "world"})
|
assert p.misc == {"hello": "world"}
|
||||||
collection = self.db[self.Person._get_collection_name()]
|
collection = self.db[self.Person._get_collection_name()]
|
||||||
obj = collection.find_one()
|
obj = collection.find_one()
|
||||||
self.assertEqual(sorted(obj.keys()), ["_cls", "_id", "misc", "name"])
|
assert sorted(obj.keys()) == ["_cls", "_id", "misc", "name"]
|
||||||
|
|
||||||
del p.misc
|
del p.misc
|
||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
p = self.Person.objects.get()
|
p = self.Person.objects.get()
|
||||||
self.assertFalse(hasattr(p, "misc"))
|
assert not hasattr(p, "misc")
|
||||||
|
|
||||||
obj = collection.find_one()
|
obj = collection.find_one()
|
||||||
self.assertEqual(sorted(obj.keys()), ["_cls", "_id", "name"])
|
assert sorted(obj.keys()) == ["_cls", "_id", "name"]
|
||||||
|
|
||||||
def test_reload_after_unsetting(self):
|
def test_reload_after_unsetting(self):
|
||||||
p = self.Person()
|
p = self.Person()
|
||||||
@@ -88,12 +90,12 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
p = self.Person.objects.create()
|
p = self.Person.objects.create()
|
||||||
p.update(age=1)
|
p.update(age=1)
|
||||||
|
|
||||||
self.assertEqual(len(p._data), 3)
|
assert len(p._data) == 3
|
||||||
self.assertEqual(sorted(p._data.keys()), ["_cls", "id", "name"])
|
assert sorted(p._data.keys()) == ["_cls", "id", "name"]
|
||||||
|
|
||||||
p.reload()
|
p.reload()
|
||||||
self.assertEqual(len(p._data), 4)
|
assert len(p._data) == 4
|
||||||
self.assertEqual(sorted(p._data.keys()), ["_cls", "age", "id", "name"])
|
assert sorted(p._data.keys()) == ["_cls", "age", "id", "name"]
|
||||||
|
|
||||||
def test_fields_without_underscore(self):
|
def test_fields_without_underscore(self):
|
||||||
"""Ensure we can query dynamic fields"""
|
"""Ensure we can query dynamic fields"""
|
||||||
@@ -103,16 +105,18 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
raw_p = Person.objects.as_pymongo().get(id=p.id)
|
raw_p = Person.objects.as_pymongo().get(id=p.id)
|
||||||
self.assertEqual(raw_p, {"_cls": u"Person", "_id": p.id, "name": u"Dean"})
|
assert raw_p == {"_cls": u"Person", "_id": p.id, "name": u"Dean"}
|
||||||
|
|
||||||
p.name = "OldDean"
|
p.name = "OldDean"
|
||||||
p.newattr = "garbage"
|
p.newattr = "garbage"
|
||||||
p.save()
|
p.save()
|
||||||
raw_p = Person.objects.as_pymongo().get(id=p.id)
|
raw_p = Person.objects.as_pymongo().get(id=p.id)
|
||||||
self.assertEqual(
|
assert raw_p == {
|
||||||
raw_p,
|
"_cls": u"Person",
|
||||||
{"_cls": u"Person", "_id": p.id, "name": "OldDean", "newattr": u"garbage"},
|
"_id": p.id,
|
||||||
)
|
"name": "OldDean",
|
||||||
|
"newattr": u"garbage",
|
||||||
|
}
|
||||||
|
|
||||||
def test_fields_containing_underscore(self):
|
def test_fields_containing_underscore(self):
|
||||||
"""Ensure we can query dynamic fields"""
|
"""Ensure we can query dynamic fields"""
|
||||||
@@ -127,14 +131,14 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
raw_p = WeirdPerson.objects.as_pymongo().get(id=p.id)
|
raw_p = WeirdPerson.objects.as_pymongo().get(id=p.id)
|
||||||
self.assertEqual(raw_p, {"_id": p.id, "_name": u"Dean", "name": u"Dean"})
|
assert raw_p == {"_id": p.id, "_name": u"Dean", "name": u"Dean"}
|
||||||
|
|
||||||
p.name = "OldDean"
|
p.name = "OldDean"
|
||||||
p._name = "NewDean"
|
p._name = "NewDean"
|
||||||
p._newattr1 = "garbage" # Unknown fields won't be added
|
p._newattr1 = "garbage" # Unknown fields won't be added
|
||||||
p.save()
|
p.save()
|
||||||
raw_p = WeirdPerson.objects.as_pymongo().get(id=p.id)
|
raw_p = WeirdPerson.objects.as_pymongo().get(id=p.id)
|
||||||
self.assertEqual(raw_p, {"_id": p.id, "_name": u"NewDean", "name": u"OldDean"})
|
assert raw_p == {"_id": p.id, "_name": u"NewDean", "name": u"OldDean"}
|
||||||
|
|
||||||
def test_dynamic_document_queries(self):
|
def test_dynamic_document_queries(self):
|
||||||
"""Ensure we can query dynamic fields"""
|
"""Ensure we can query dynamic fields"""
|
||||||
@@ -143,10 +147,10 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
p.age = 22
|
p.age = 22
|
||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
self.assertEqual(1, self.Person.objects(age=22).count())
|
assert 1 == self.Person.objects(age=22).count()
|
||||||
p = self.Person.objects(age=22)
|
p = self.Person.objects(age=22)
|
||||||
p = p.get()
|
p = p.get()
|
||||||
self.assertEqual(22, p.age)
|
assert 22 == p.age
|
||||||
|
|
||||||
def test_complex_dynamic_document_queries(self):
|
def test_complex_dynamic_document_queries(self):
|
||||||
class Person(DynamicDocument):
|
class Person(DynamicDocument):
|
||||||
@@ -166,8 +170,8 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
p2.age = 10
|
p2.age = 10
|
||||||
p2.save()
|
p2.save()
|
||||||
|
|
||||||
self.assertEqual(Person.objects(age__icontains="ten").count(), 2)
|
assert Person.objects(age__icontains="ten").count() == 2
|
||||||
self.assertEqual(Person.objects(age__gte=10).count(), 1)
|
assert Person.objects(age__gte=10).count() == 1
|
||||||
|
|
||||||
def test_complex_data_lookups(self):
|
def test_complex_data_lookups(self):
|
||||||
"""Ensure you can query dynamic document dynamic fields"""
|
"""Ensure you can query dynamic document dynamic fields"""
|
||||||
@@ -175,12 +179,12 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
p.misc = {"hello": "world"}
|
p.misc = {"hello": "world"}
|
||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
self.assertEqual(1, self.Person.objects(misc__hello="world").count())
|
assert 1 == self.Person.objects(misc__hello="world").count()
|
||||||
|
|
||||||
def test_three_level_complex_data_lookups(self):
|
def test_three_level_complex_data_lookups(self):
|
||||||
"""Ensure you can query three level document dynamic fields"""
|
"""Ensure you can query three level document dynamic fields"""
|
||||||
p = self.Person.objects.create(misc={"hello": {"hello2": "world"}})
|
self.Person.objects.create(misc={"hello": {"hello2": "world"}})
|
||||||
self.assertEqual(1, self.Person.objects(misc__hello__hello2="world").count())
|
assert 1 == self.Person.objects(misc__hello__hello2="world").count()
|
||||||
|
|
||||||
def test_complex_embedded_document_validation(self):
|
def test_complex_embedded_document_validation(self):
|
||||||
"""Ensure embedded dynamic documents may be validated"""
|
"""Ensure embedded dynamic documents may be validated"""
|
||||||
@@ -198,11 +202,13 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
embedded_doc_1.validate()
|
embedded_doc_1.validate()
|
||||||
|
|
||||||
embedded_doc_2 = Embedded(content="this is not a url")
|
embedded_doc_2 = Embedded(content="this is not a url")
|
||||||
self.assertRaises(ValidationError, embedded_doc_2.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
embedded_doc_2.validate()
|
||||||
|
|
||||||
doc.embedded_field_1 = embedded_doc_1
|
doc.embedded_field_1 = embedded_doc_1
|
||||||
doc.embedded_field_2 = embedded_doc_2
|
doc.embedded_field_2 = embedded_doc_2
|
||||||
self.assertRaises(ValidationError, doc.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
doc.validate()
|
||||||
|
|
||||||
def test_inheritance(self):
|
def test_inheritance(self):
|
||||||
"""Ensure that dynamic document plays nice with inheritance"""
|
"""Ensure that dynamic document plays nice with inheritance"""
|
||||||
@@ -212,11 +218,9 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
|
|
||||||
Employee.drop_collection()
|
Employee.drop_collection()
|
||||||
|
|
||||||
self.assertIn("name", Employee._fields)
|
assert "name" in Employee._fields
|
||||||
self.assertIn("salary", Employee._fields)
|
assert "salary" in Employee._fields
|
||||||
self.assertEqual(
|
assert Employee._get_collection_name() == self.Person._get_collection_name()
|
||||||
Employee._get_collection_name(), self.Person._get_collection_name()
|
|
||||||
)
|
|
||||||
|
|
||||||
joe_bloggs = Employee()
|
joe_bloggs = Employee()
|
||||||
joe_bloggs.name = "Joe Bloggs"
|
joe_bloggs.name = "Joe Bloggs"
|
||||||
@@ -224,11 +228,11 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
joe_bloggs.age = 20
|
joe_bloggs.age = 20
|
||||||
joe_bloggs.save()
|
joe_bloggs.save()
|
||||||
|
|
||||||
self.assertEqual(1, self.Person.objects(age=20).count())
|
assert 1 == self.Person.objects(age=20).count()
|
||||||
self.assertEqual(1, Employee.objects(age=20).count())
|
assert 1 == Employee.objects(age=20).count()
|
||||||
|
|
||||||
joe_bloggs = self.Person.objects.first()
|
joe_bloggs = self.Person.objects.first()
|
||||||
self.assertIsInstance(joe_bloggs, Employee)
|
assert isinstance(joe_bloggs, Employee)
|
||||||
|
|
||||||
def test_embedded_dynamic_document(self):
|
def test_embedded_dynamic_document(self):
|
||||||
"""Test dynamic embedded documents"""
|
"""Test dynamic embedded documents"""
|
||||||
@@ -249,26 +253,23 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
embedded_1.list_field = ["1", 2, {"hello": "world"}]
|
embedded_1.list_field = ["1", 2, {"hello": "world"}]
|
||||||
doc.embedded_field = embedded_1
|
doc.embedded_field = embedded_1
|
||||||
|
|
||||||
self.assertEqual(
|
assert doc.to_mongo() == {
|
||||||
doc.to_mongo(),
|
"embedded_field": {
|
||||||
{
|
"_cls": "Embedded",
|
||||||
"embedded_field": {
|
"string_field": "hello",
|
||||||
"_cls": "Embedded",
|
"int_field": 1,
|
||||||
"string_field": "hello",
|
"dict_field": {"hello": "world"},
|
||||||
"int_field": 1,
|
"list_field": ["1", 2, {"hello": "world"}],
|
||||||
"dict_field": {"hello": "world"},
|
}
|
||||||
"list_field": ["1", 2, {"hello": "world"}],
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
doc.save()
|
doc.save()
|
||||||
|
|
||||||
doc = Doc.objects.first()
|
doc = Doc.objects.first()
|
||||||
self.assertEqual(doc.embedded_field.__class__, Embedded)
|
assert doc.embedded_field.__class__ == Embedded
|
||||||
self.assertEqual(doc.embedded_field.string_field, "hello")
|
assert doc.embedded_field.string_field == "hello"
|
||||||
self.assertEqual(doc.embedded_field.int_field, 1)
|
assert doc.embedded_field.int_field == 1
|
||||||
self.assertEqual(doc.embedded_field.dict_field, {"hello": "world"})
|
assert doc.embedded_field.dict_field == {"hello": "world"}
|
||||||
self.assertEqual(doc.embedded_field.list_field, ["1", 2, {"hello": "world"}])
|
assert doc.embedded_field.list_field == ["1", 2, {"hello": "world"}]
|
||||||
|
|
||||||
def test_complex_embedded_documents(self):
|
def test_complex_embedded_documents(self):
|
||||||
"""Test complex dynamic embedded documents setups"""
|
"""Test complex dynamic embedded documents setups"""
|
||||||
@@ -296,44 +297,41 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
embedded_1.list_field = ["1", 2, embedded_2]
|
embedded_1.list_field = ["1", 2, embedded_2]
|
||||||
doc.embedded_field = embedded_1
|
doc.embedded_field = embedded_1
|
||||||
|
|
||||||
self.assertEqual(
|
assert doc.to_mongo() == {
|
||||||
doc.to_mongo(),
|
"embedded_field": {
|
||||||
{
|
"_cls": "Embedded",
|
||||||
"embedded_field": {
|
"string_field": "hello",
|
||||||
"_cls": "Embedded",
|
"int_field": 1,
|
||||||
"string_field": "hello",
|
"dict_field": {"hello": "world"},
|
||||||
"int_field": 1,
|
"list_field": [
|
||||||
"dict_field": {"hello": "world"},
|
"1",
|
||||||
"list_field": [
|
2,
|
||||||
"1",
|
{
|
||||||
2,
|
"_cls": "Embedded",
|
||||||
{
|
"string_field": "hello",
|
||||||
"_cls": "Embedded",
|
"int_field": 1,
|
||||||
"string_field": "hello",
|
"dict_field": {"hello": "world"},
|
||||||
"int_field": 1,
|
"list_field": ["1", 2, {"hello": "world"}],
|
||||||
"dict_field": {"hello": "world"},
|
},
|
||||||
"list_field": ["1", 2, {"hello": "world"}],
|
],
|
||||||
},
|
}
|
||||||
],
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
doc.save()
|
doc.save()
|
||||||
doc = Doc.objects.first()
|
doc = Doc.objects.first()
|
||||||
self.assertEqual(doc.embedded_field.__class__, Embedded)
|
assert doc.embedded_field.__class__ == Embedded
|
||||||
self.assertEqual(doc.embedded_field.string_field, "hello")
|
assert doc.embedded_field.string_field == "hello"
|
||||||
self.assertEqual(doc.embedded_field.int_field, 1)
|
assert doc.embedded_field.int_field == 1
|
||||||
self.assertEqual(doc.embedded_field.dict_field, {"hello": "world"})
|
assert doc.embedded_field.dict_field == {"hello": "world"}
|
||||||
self.assertEqual(doc.embedded_field.list_field[0], "1")
|
assert doc.embedded_field.list_field[0] == "1"
|
||||||
self.assertEqual(doc.embedded_field.list_field[1], 2)
|
assert doc.embedded_field.list_field[1] == 2
|
||||||
|
|
||||||
embedded_field = doc.embedded_field.list_field[2]
|
embedded_field = doc.embedded_field.list_field[2]
|
||||||
|
|
||||||
self.assertEqual(embedded_field.__class__, Embedded)
|
assert embedded_field.__class__ == Embedded
|
||||||
self.assertEqual(embedded_field.string_field, "hello")
|
assert embedded_field.string_field == "hello"
|
||||||
self.assertEqual(embedded_field.int_field, 1)
|
assert embedded_field.int_field == 1
|
||||||
self.assertEqual(embedded_field.dict_field, {"hello": "world"})
|
assert embedded_field.dict_field == {"hello": "world"}
|
||||||
self.assertEqual(embedded_field.list_field, ["1", 2, {"hello": "world"}])
|
assert embedded_field.list_field == ["1", 2, {"hello": "world"}]
|
||||||
|
|
||||||
def test_dynamic_and_embedded(self):
|
def test_dynamic_and_embedded(self):
|
||||||
"""Ensure embedded documents play nicely"""
|
"""Ensure embedded documents play nicely"""
|
||||||
@@ -352,18 +350,18 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
person.address.city = "Lundenne"
|
person.address.city = "Lundenne"
|
||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
self.assertEqual(Person.objects.first().address.city, "Lundenne")
|
assert Person.objects.first().address.city == "Lundenne"
|
||||||
|
|
||||||
person = Person.objects.first()
|
person = Person.objects.first()
|
||||||
person.address = Address(city="Londinium")
|
person.address = Address(city="Londinium")
|
||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
self.assertEqual(Person.objects.first().address.city, "Londinium")
|
assert Person.objects.first().address.city == "Londinium"
|
||||||
|
|
||||||
person = Person.objects.first()
|
person = Person.objects.first()
|
||||||
person.age = 35
|
person.age = 35
|
||||||
person.save()
|
person.save()
|
||||||
self.assertEqual(Person.objects.first().age, 35)
|
assert Person.objects.first().age == 35
|
||||||
|
|
||||||
def test_dynamic_embedded_works_with_only(self):
|
def test_dynamic_embedded_works_with_only(self):
|
||||||
"""Ensure custom fieldnames on a dynamic embedded document are found by qs.only()"""
|
"""Ensure custom fieldnames on a dynamic embedded document are found by qs.only()"""
|
||||||
@@ -380,10 +378,10 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
name="Eric", address=Address(city="San Francisco", street_number="1337")
|
name="Eric", address=Address(city="San Francisco", street_number="1337")
|
||||||
).save()
|
).save()
|
||||||
|
|
||||||
self.assertEqual(Person.objects.first().address.street_number, "1337")
|
assert Person.objects.first().address.street_number == "1337"
|
||||||
self.assertEqual(
|
assert (
|
||||||
Person.objects.only("address__street_number").first().address.street_number,
|
Person.objects.only("address__street_number").first().address.street_number
|
||||||
"1337",
|
== "1337"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_dynamic_and_embedded_dict_access(self):
|
def test_dynamic_and_embedded_dict_access(self):
|
||||||
@@ -408,20 +406,20 @@ class TestDynamicDocument(MongoDBTestCase):
|
|||||||
person["address"]["city"] = "Lundenne"
|
person["address"]["city"] = "Lundenne"
|
||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
self.assertEqual(Person.objects.first().address.city, "Lundenne")
|
assert Person.objects.first().address.city == "Lundenne"
|
||||||
|
|
||||||
self.assertEqual(Person.objects.first().phone, "555-1212")
|
assert Person.objects.first().phone == "555-1212"
|
||||||
|
|
||||||
person = Person.objects.first()
|
person = Person.objects.first()
|
||||||
person.address = Address(city="Londinium")
|
person.address = Address(city="Londinium")
|
||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
self.assertEqual(Person.objects.first().address.city, "Londinium")
|
assert Person.objects.first().address.city == "Londinium"
|
||||||
|
|
||||||
person = Person.objects.first()
|
person = Person.objects.first()
|
||||||
person["age"] = 35
|
person["age"] = 35
|
||||||
person.save()
|
person.save()
|
||||||
self.assertEqual(Person.objects.first().age, 35)
|
assert Person.objects.first().age == 35
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -2,18 +2,15 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from nose.plugins.skip import SkipTest
|
from pymongo.collation import Collation
|
||||||
from pymongo.errors import OperationFailure
|
from pymongo.errors import OperationFailure
|
||||||
import pymongo
|
import pytest
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.connection import get_db
|
from mongoengine.connection import get_db
|
||||||
|
|
||||||
__all__ = ("IndexesTest",)
|
|
||||||
|
|
||||||
|
class TestIndexes(unittest.TestCase):
|
||||||
class IndexesTest(unittest.TestCase):
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.connection = connect(db="mongoenginetest")
|
self.connection = connect(db="mongoenginetest")
|
||||||
self.db = get_db()
|
self.db = get_db()
|
||||||
@@ -55,15 +52,15 @@ class IndexesTest(unittest.TestCase):
|
|||||||
{"fields": [("tags", 1)]},
|
{"fields": [("tags", 1)]},
|
||||||
{"fields": [("category", 1), ("addDate", -1)]},
|
{"fields": [("category", 1), ("addDate", -1)]},
|
||||||
]
|
]
|
||||||
self.assertEqual(expected_specs, BlogPost._meta["index_specs"])
|
assert expected_specs == BlogPost._meta["index_specs"]
|
||||||
|
|
||||||
BlogPost.ensure_indexes()
|
BlogPost.ensure_indexes()
|
||||||
info = BlogPost.objects._collection.index_information()
|
info = BlogPost.objects._collection.index_information()
|
||||||
# _id, '-date', 'tags', ('cat', 'date')
|
# _id, '-date', 'tags', ('cat', 'date')
|
||||||
self.assertEqual(len(info), 4)
|
assert len(info) == 4
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
for expected in expected_specs:
|
for expected in expected_specs:
|
||||||
self.assertIn(expected["fields"], info)
|
assert expected["fields"] in info
|
||||||
|
|
||||||
def _index_test_inheritance(self, InheritFrom):
|
def _index_test_inheritance(self, InheritFrom):
|
||||||
class BlogPost(InheritFrom):
|
class BlogPost(InheritFrom):
|
||||||
@@ -80,7 +77,7 @@ class IndexesTest(unittest.TestCase):
|
|||||||
{"fields": [("_cls", 1), ("tags", 1)]},
|
{"fields": [("_cls", 1), ("tags", 1)]},
|
||||||
{"fields": [("_cls", 1), ("category", 1), ("addDate", -1)]},
|
{"fields": [("_cls", 1), ("category", 1), ("addDate", -1)]},
|
||||||
]
|
]
|
||||||
self.assertEqual(expected_specs, BlogPost._meta["index_specs"])
|
assert expected_specs == BlogPost._meta["index_specs"]
|
||||||
|
|
||||||
BlogPost.ensure_indexes()
|
BlogPost.ensure_indexes()
|
||||||
info = BlogPost.objects._collection.index_information()
|
info = BlogPost.objects._collection.index_information()
|
||||||
@@ -88,25 +85,25 @@ class IndexesTest(unittest.TestCase):
|
|||||||
# NB: there is no index on _cls by itself, since
|
# NB: there is no index on _cls by itself, since
|
||||||
# the indices on -date and tags will both contain
|
# the indices on -date and tags will both contain
|
||||||
# _cls as first element in the key
|
# _cls as first element in the key
|
||||||
self.assertEqual(len(info), 4)
|
assert len(info) == 4
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
for expected in expected_specs:
|
for expected in expected_specs:
|
||||||
self.assertIn(expected["fields"], info)
|
assert expected["fields"] in info
|
||||||
|
|
||||||
class ExtendedBlogPost(BlogPost):
|
class ExtendedBlogPost(BlogPost):
|
||||||
title = StringField()
|
title = StringField()
|
||||||
meta = {"indexes": ["title"]}
|
meta = {"indexes": ["title"]}
|
||||||
|
|
||||||
expected_specs.append({"fields": [("_cls", 1), ("title", 1)]})
|
expected_specs.append({"fields": [("_cls", 1), ("title", 1)]})
|
||||||
self.assertEqual(expected_specs, ExtendedBlogPost._meta["index_specs"])
|
assert expected_specs == ExtendedBlogPost._meta["index_specs"]
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
ExtendedBlogPost.ensure_indexes()
|
ExtendedBlogPost.ensure_indexes()
|
||||||
info = ExtendedBlogPost.objects._collection.index_information()
|
info = ExtendedBlogPost.objects._collection.index_information()
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
for expected in expected_specs:
|
for expected in expected_specs:
|
||||||
self.assertIn(expected["fields"], info)
|
assert expected["fields"] in info
|
||||||
|
|
||||||
def test_indexes_document_inheritance(self):
|
def test_indexes_document_inheritance(self):
|
||||||
"""Ensure that indexes are used when meta[indexes] is specified for
|
"""Ensure that indexes are used when meta[indexes] is specified for
|
||||||
@@ -130,10 +127,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
class B(A):
|
class B(A):
|
||||||
description = StringField()
|
description = StringField()
|
||||||
|
|
||||||
self.assertEqual(A._meta["index_specs"], B._meta["index_specs"])
|
assert A._meta["index_specs"] == B._meta["index_specs"]
|
||||||
self.assertEqual(
|
assert [{"fields": [("_cls", 1), ("title", 1)]}] == A._meta["index_specs"]
|
||||||
[{"fields": [("_cls", 1), ("title", 1)]}], A._meta["index_specs"]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_index_no_cls(self):
|
def test_index_no_cls(self):
|
||||||
"""Ensure index specs are inhertited correctly"""
|
"""Ensure index specs are inhertited correctly"""
|
||||||
@@ -146,11 +141,11 @@ class IndexesTest(unittest.TestCase):
|
|||||||
"index_cls": False,
|
"index_cls": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assertEqual([("title", 1)], A._meta["index_specs"][0]["fields"])
|
assert [("title", 1)] == A._meta["index_specs"][0]["fields"]
|
||||||
A._get_collection().drop_indexes()
|
A._get_collection().drop_indexes()
|
||||||
A.ensure_indexes()
|
A.ensure_indexes()
|
||||||
info = A._get_collection().index_information()
|
info = A._get_collection().index_information()
|
||||||
self.assertEqual(len(info.keys()), 2)
|
assert len(info.keys()) == 2
|
||||||
|
|
||||||
class B(A):
|
class B(A):
|
||||||
c = StringField()
|
c = StringField()
|
||||||
@@ -160,8 +155,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
"allow_inheritance": True,
|
"allow_inheritance": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assertEqual([("c", 1)], B._meta["index_specs"][1]["fields"])
|
assert [("c", 1)] == B._meta["index_specs"][1]["fields"]
|
||||||
self.assertEqual([("_cls", 1), ("d", 1)], B._meta["index_specs"][2]["fields"])
|
assert [("_cls", 1), ("d", 1)] == B._meta["index_specs"][2]["fields"]
|
||||||
|
|
||||||
def test_build_index_spec_is_not_destructive(self):
|
def test_build_index_spec_is_not_destructive(self):
|
||||||
class MyDoc(Document):
|
class MyDoc(Document):
|
||||||
@@ -169,12 +164,12 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
meta = {"indexes": ["keywords"], "allow_inheritance": False}
|
meta = {"indexes": ["keywords"], "allow_inheritance": False}
|
||||||
|
|
||||||
self.assertEqual(MyDoc._meta["index_specs"], [{"fields": [("keywords", 1)]}])
|
assert MyDoc._meta["index_specs"] == [{"fields": [("keywords", 1)]}]
|
||||||
|
|
||||||
# Force index creation
|
# Force index creation
|
||||||
MyDoc.ensure_indexes()
|
MyDoc.ensure_indexes()
|
||||||
|
|
||||||
self.assertEqual(MyDoc._meta["index_specs"], [{"fields": [("keywords", 1)]}])
|
assert MyDoc._meta["index_specs"] == [{"fields": [("keywords", 1)]}]
|
||||||
|
|
||||||
def test_embedded_document_index_meta(self):
|
def test_embedded_document_index_meta(self):
|
||||||
"""Ensure that embedded document indexes are created explicitly
|
"""Ensure that embedded document indexes are created explicitly
|
||||||
@@ -189,15 +184,15 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
meta = {"indexes": ["rank.title"], "allow_inheritance": False}
|
meta = {"indexes": ["rank.title"], "allow_inheritance": False}
|
||||||
|
|
||||||
self.assertEqual([{"fields": [("rank.title", 1)]}], Person._meta["index_specs"])
|
assert [{"fields": [("rank.title", 1)]}] == Person._meta["index_specs"]
|
||||||
|
|
||||||
Person.drop_collection()
|
Person.drop_collection()
|
||||||
|
|
||||||
# Indexes are lazy so use list() to perform query
|
# Indexes are lazy so use list() to perform query
|
||||||
list(Person.objects)
|
list(Person.objects)
|
||||||
info = Person.objects._collection.index_information()
|
info = Person.objects._collection.index_information()
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
self.assertIn([("rank.title", 1)], info)
|
assert [("rank.title", 1)] in info
|
||||||
|
|
||||||
def test_explicit_geo2d_index(self):
|
def test_explicit_geo2d_index(self):
|
||||||
"""Ensure that geo2d indexes work when created via meta[indexes]
|
"""Ensure that geo2d indexes work when created via meta[indexes]
|
||||||
@@ -207,14 +202,12 @@ class IndexesTest(unittest.TestCase):
|
|||||||
location = DictField()
|
location = DictField()
|
||||||
meta = {"allow_inheritance": True, "indexes": ["*location.point"]}
|
meta = {"allow_inheritance": True, "indexes": ["*location.point"]}
|
||||||
|
|
||||||
self.assertEqual(
|
assert [{"fields": [("location.point", "2d")]}] == Place._meta["index_specs"]
|
||||||
[{"fields": [("location.point", "2d")]}], Place._meta["index_specs"]
|
|
||||||
)
|
|
||||||
|
|
||||||
Place.ensure_indexes()
|
Place.ensure_indexes()
|
||||||
info = Place._get_collection().index_information()
|
info = Place._get_collection().index_information()
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
self.assertIn([("location.point", "2d")], info)
|
assert [("location.point", "2d")] in info
|
||||||
|
|
||||||
def test_explicit_geo2d_index_embedded(self):
|
def test_explicit_geo2d_index_embedded(self):
|
||||||
"""Ensure that geo2d indexes work when created via meta[indexes]
|
"""Ensure that geo2d indexes work when created via meta[indexes]
|
||||||
@@ -227,14 +220,14 @@ class IndexesTest(unittest.TestCase):
|
|||||||
current = DictField(field=EmbeddedDocumentField("EmbeddedLocation"))
|
current = DictField(field=EmbeddedDocumentField("EmbeddedLocation"))
|
||||||
meta = {"allow_inheritance": True, "indexes": ["*current.location.point"]}
|
meta = {"allow_inheritance": True, "indexes": ["*current.location.point"]}
|
||||||
|
|
||||||
self.assertEqual(
|
assert [{"fields": [("current.location.point", "2d")]}] == Place._meta[
|
||||||
[{"fields": [("current.location.point", "2d")]}], Place._meta["index_specs"]
|
"index_specs"
|
||||||
)
|
]
|
||||||
|
|
||||||
Place.ensure_indexes()
|
Place.ensure_indexes()
|
||||||
info = Place._get_collection().index_information()
|
info = Place._get_collection().index_information()
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
self.assertIn([("current.location.point", "2d")], info)
|
assert [("current.location.point", "2d")] in info
|
||||||
|
|
||||||
def test_explicit_geosphere_index(self):
|
def test_explicit_geosphere_index(self):
|
||||||
"""Ensure that geosphere indexes work when created via meta[indexes]
|
"""Ensure that geosphere indexes work when created via meta[indexes]
|
||||||
@@ -244,19 +237,19 @@ class IndexesTest(unittest.TestCase):
|
|||||||
location = DictField()
|
location = DictField()
|
||||||
meta = {"allow_inheritance": True, "indexes": ["(location.point"]}
|
meta = {"allow_inheritance": True, "indexes": ["(location.point"]}
|
||||||
|
|
||||||
self.assertEqual(
|
assert [{"fields": [("location.point", "2dsphere")]}] == Place._meta[
|
||||||
[{"fields": [("location.point", "2dsphere")]}], Place._meta["index_specs"]
|
"index_specs"
|
||||||
)
|
]
|
||||||
|
|
||||||
Place.ensure_indexes()
|
Place.ensure_indexes()
|
||||||
info = Place._get_collection().index_information()
|
info = Place._get_collection().index_information()
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
self.assertIn([("location.point", "2dsphere")], info)
|
assert [("location.point", "2dsphere")] in info
|
||||||
|
|
||||||
def test_explicit_geohaystack_index(self):
|
def test_explicit_geohaystack_index(self):
|
||||||
"""Ensure that geohaystack indexes work when created via meta[indexes]
|
"""Ensure that geohaystack indexes work when created via meta[indexes]
|
||||||
"""
|
"""
|
||||||
raise SkipTest(
|
pytest.skip(
|
||||||
"GeoHaystack index creation is not supported for now"
|
"GeoHaystack index creation is not supported for now"
|
||||||
"from meta, as it requires a bucketSize parameter."
|
"from meta, as it requires a bucketSize parameter."
|
||||||
)
|
)
|
||||||
@@ -266,15 +259,14 @@ class IndexesTest(unittest.TestCase):
|
|||||||
name = StringField()
|
name = StringField()
|
||||||
meta = {"indexes": [(")location.point", "name")]}
|
meta = {"indexes": [(")location.point", "name")]}
|
||||||
|
|
||||||
self.assertEqual(
|
assert [
|
||||||
[{"fields": [("location.point", "geoHaystack"), ("name", 1)]}],
|
{"fields": [("location.point", "geoHaystack"), ("name", 1)]}
|
||||||
Place._meta["index_specs"],
|
] == Place._meta["index_specs"]
|
||||||
)
|
|
||||||
|
|
||||||
Place.ensure_indexes()
|
Place.ensure_indexes()
|
||||||
info = Place._get_collection().index_information()
|
info = Place._get_collection().index_information()
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
self.assertIn([("location.point", "geoHaystack")], info)
|
assert [("location.point", "geoHaystack")] in info
|
||||||
|
|
||||||
def test_create_geohaystack_index(self):
|
def test_create_geohaystack_index(self):
|
||||||
"""Ensure that geohaystack indexes can be created
|
"""Ensure that geohaystack indexes can be created
|
||||||
@@ -286,8 +278,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
Place.create_index({"fields": (")location.point", "name")}, bucketSize=10)
|
Place.create_index({"fields": (")location.point", "name")}, bucketSize=10)
|
||||||
info = Place._get_collection().index_information()
|
info = Place._get_collection().index_information()
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
self.assertIn([("location.point", "geoHaystack"), ("name", 1)], info)
|
assert [("location.point", "geoHaystack"), ("name", 1)] in info
|
||||||
|
|
||||||
def test_dictionary_indexes(self):
|
def test_dictionary_indexes(self):
|
||||||
"""Ensure that indexes are used when meta[indexes] contains
|
"""Ensure that indexes are used when meta[indexes] contains
|
||||||
@@ -300,25 +292,24 @@ class IndexesTest(unittest.TestCase):
|
|||||||
tags = ListField(StringField())
|
tags = ListField(StringField())
|
||||||
meta = {"indexes": [{"fields": ["-date"], "unique": True, "sparse": True}]}
|
meta = {"indexes": [{"fields": ["-date"], "unique": True, "sparse": True}]}
|
||||||
|
|
||||||
self.assertEqual(
|
assert [
|
||||||
[{"fields": [("addDate", -1)], "unique": True, "sparse": True}],
|
{"fields": [("addDate", -1)], "unique": True, "sparse": True}
|
||||||
BlogPost._meta["index_specs"],
|
] == BlogPost._meta["index_specs"]
|
||||||
)
|
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
info = BlogPost.objects._collection.index_information()
|
info = BlogPost.objects._collection.index_information()
|
||||||
# _id, '-date'
|
# _id, '-date'
|
||||||
self.assertEqual(len(info), 2)
|
assert len(info) == 2
|
||||||
|
|
||||||
# Indexes are lazy so use list() to perform query
|
# Indexes are lazy so use list() to perform query
|
||||||
list(BlogPost.objects)
|
list(BlogPost.objects)
|
||||||
info = BlogPost.objects._collection.index_information()
|
info = BlogPost.objects._collection.index_information()
|
||||||
info = [
|
info = [
|
||||||
(value["key"], value.get("unique", False), value.get("sparse", False))
|
(value["key"], value.get("unique", False), value.get("sparse", False))
|
||||||
for key, value in iteritems(info)
|
for key, value in info.items()
|
||||||
]
|
]
|
||||||
self.assertIn(([("addDate", -1)], True, True), info)
|
assert ([("addDate", -1)], True, True) in info
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
@@ -340,11 +331,9 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
Person(name="test", user_guid="123").save()
|
Person(name="test", user_guid="123").save()
|
||||||
|
|
||||||
self.assertEqual(1, Person.objects.count())
|
assert 1 == Person.objects.count()
|
||||||
info = Person.objects._collection.index_information()
|
info = Person.objects._collection.index_information()
|
||||||
self.assertEqual(
|
assert sorted(info.keys()) == ["_cls_1_name_1", "_cls_1_user_guid_1", "_id_"]
|
||||||
sorted(info.keys()), ["_cls_1_name_1", "_cls_1_user_guid_1", "_id_"]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_disable_index_creation(self):
|
def test_disable_index_creation(self):
|
||||||
"""Tests setting auto_create_index to False on the connection will
|
"""Tests setting auto_create_index to False on the connection will
|
||||||
@@ -367,13 +356,13 @@ class IndexesTest(unittest.TestCase):
|
|||||||
User(user_guid="123").save()
|
User(user_guid="123").save()
|
||||||
MongoUser(user_guid="123").save()
|
MongoUser(user_guid="123").save()
|
||||||
|
|
||||||
self.assertEqual(2, User.objects.count())
|
assert 2 == User.objects.count()
|
||||||
info = User.objects._collection.index_information()
|
info = User.objects._collection.index_information()
|
||||||
self.assertEqual(list(info.keys()), ["_id_"])
|
assert list(info.keys()) == ["_id_"]
|
||||||
|
|
||||||
User.ensure_indexes()
|
User.ensure_indexes()
|
||||||
info = User.objects._collection.index_information()
|
info = User.objects._collection.index_information()
|
||||||
self.assertEqual(sorted(info.keys()), ["_cls_1_user_guid_1", "_id_"])
|
assert sorted(info.keys()) == ["_cls_1_user_guid_1", "_id_"]
|
||||||
|
|
||||||
def test_embedded_document_index(self):
|
def test_embedded_document_index(self):
|
||||||
"""Tests settings an index on an embedded document
|
"""Tests settings an index on an embedded document
|
||||||
@@ -391,7 +380,7 @@ class IndexesTest(unittest.TestCase):
|
|||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
info = BlogPost.objects._collection.index_information()
|
info = BlogPost.objects._collection.index_information()
|
||||||
self.assertEqual(sorted(info.keys()), ["_id_", "date.yr_-1"])
|
assert sorted(info.keys()) == ["_id_", "date.yr_-1"]
|
||||||
|
|
||||||
def test_list_embedded_document_index(self):
|
def test_list_embedded_document_index(self):
|
||||||
"""Ensure list embedded documents can be indexed
|
"""Ensure list embedded documents can be indexed
|
||||||
@@ -410,7 +399,7 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
info = BlogPost.objects._collection.index_information()
|
info = BlogPost.objects._collection.index_information()
|
||||||
# we don't use _cls in with list fields by default
|
# we don't use _cls in with list fields by default
|
||||||
self.assertEqual(sorted(info.keys()), ["_id_", "tags.tag_1"])
|
assert sorted(info.keys()) == ["_id_", "tags.tag_1"]
|
||||||
|
|
||||||
post1 = BlogPost(
|
post1 = BlogPost(
|
||||||
title="Embedded Indexes tests in place",
|
title="Embedded Indexes tests in place",
|
||||||
@@ -428,7 +417,7 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
RecursiveDocument.ensure_indexes()
|
RecursiveDocument.ensure_indexes()
|
||||||
info = RecursiveDocument._get_collection().index_information()
|
info = RecursiveDocument._get_collection().index_information()
|
||||||
self.assertEqual(sorted(info.keys()), ["_cls_1", "_id_"])
|
assert sorted(info.keys()) == ["_cls_1", "_id_"]
|
||||||
|
|
||||||
def test_covered_index(self):
|
def test_covered_index(self):
|
||||||
"""Ensure that covered indexes can be used
|
"""Ensure that covered indexes can be used
|
||||||
@@ -448,46 +437,45 @@ class IndexesTest(unittest.TestCase):
|
|||||||
# Need to be explicit about covered indexes as mongoDB doesn't know if
|
# Need to be explicit about covered indexes as mongoDB doesn't know if
|
||||||
# the documents returned might have more keys in that here.
|
# the documents returned might have more keys in that here.
|
||||||
query_plan = Test.objects(id=obj.id).exclude("a").explain()
|
query_plan = Test.objects(id=obj.id).exclude("a").explain()
|
||||||
self.assertEqual(
|
assert (
|
||||||
query_plan.get("queryPlanner")
|
query_plan.get("queryPlanner")
|
||||||
.get("winningPlan")
|
.get("winningPlan")
|
||||||
.get("inputStage")
|
.get("inputStage")
|
||||||
.get("stage"),
|
.get("stage")
|
||||||
"IDHACK",
|
== "IDHACK"
|
||||||
)
|
)
|
||||||
|
|
||||||
query_plan = Test.objects(id=obj.id).only("id").explain()
|
query_plan = Test.objects(id=obj.id).only("id").explain()
|
||||||
self.assertEqual(
|
assert (
|
||||||
query_plan.get("queryPlanner")
|
query_plan.get("queryPlanner")
|
||||||
.get("winningPlan")
|
.get("winningPlan")
|
||||||
.get("inputStage")
|
.get("inputStage")
|
||||||
.get("stage"),
|
.get("stage")
|
||||||
"IDHACK",
|
== "IDHACK"
|
||||||
)
|
)
|
||||||
|
|
||||||
query_plan = Test.objects(a=1).only("a").exclude("id").explain()
|
query_plan = Test.objects(a=1).only("a").exclude("id").explain()
|
||||||
self.assertEqual(
|
assert (
|
||||||
query_plan.get("queryPlanner")
|
query_plan.get("queryPlanner")
|
||||||
.get("winningPlan")
|
.get("winningPlan")
|
||||||
.get("inputStage")
|
.get("inputStage")
|
||||||
.get("stage"),
|
.get("stage")
|
||||||
"IXSCAN",
|
== "IXSCAN"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert (
|
||||||
query_plan.get("queryPlanner").get("winningPlan").get("stage"), "PROJECTION"
|
query_plan.get("queryPlanner").get("winningPlan").get("stage")
|
||||||
|
== "PROJECTION"
|
||||||
)
|
)
|
||||||
|
|
||||||
query_plan = Test.objects(a=1).explain()
|
query_plan = Test.objects(a=1).explain()
|
||||||
self.assertEqual(
|
assert (
|
||||||
query_plan.get("queryPlanner")
|
query_plan.get("queryPlanner")
|
||||||
.get("winningPlan")
|
.get("winningPlan")
|
||||||
.get("inputStage")
|
.get("inputStage")
|
||||||
.get("stage"),
|
.get("stage")
|
||||||
"IXSCAN",
|
== "IXSCAN"
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
query_plan.get("queryPlanner").get("winningPlan").get("stage"), "FETCH"
|
|
||||||
)
|
)
|
||||||
|
assert query_plan.get("queryPlanner").get("winningPlan").get("stage") == "FETCH"
|
||||||
|
|
||||||
def test_index_on_id(self):
|
def test_index_on_id(self):
|
||||||
class BlogPost(Document):
|
class BlogPost(Document):
|
||||||
@@ -500,9 +488,7 @@ class IndexesTest(unittest.TestCase):
|
|||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
indexes = BlogPost.objects._collection.index_information()
|
indexes = BlogPost.objects._collection.index_information()
|
||||||
self.assertEqual(
|
assert indexes["categories_1__id_1"]["key"] == [("categories", 1), ("_id", 1)]
|
||||||
indexes["categories_1__id_1"]["key"], [("categories", 1), ("_id", 1)]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_hint(self):
|
def test_hint(self):
|
||||||
TAGS_INDEX_NAME = "tags_1"
|
TAGS_INDEX_NAME = "tags_1"
|
||||||
@@ -518,27 +504,59 @@ class IndexesTest(unittest.TestCase):
|
|||||||
BlogPost(tags=tags).save()
|
BlogPost(tags=tags).save()
|
||||||
|
|
||||||
# Hinting by shape should work.
|
# Hinting by shape should work.
|
||||||
self.assertEqual(BlogPost.objects.hint([("tags", 1)]).count(), 10)
|
assert BlogPost.objects.hint([("tags", 1)]).count() == 10
|
||||||
|
|
||||||
# Hinting by index name should work.
|
# Hinting by index name should work.
|
||||||
self.assertEqual(BlogPost.objects.hint(TAGS_INDEX_NAME).count(), 10)
|
assert BlogPost.objects.hint(TAGS_INDEX_NAME).count() == 10
|
||||||
|
|
||||||
# Clearing the hint should work fine.
|
# Clearing the hint should work fine.
|
||||||
self.assertEqual(BlogPost.objects.hint().count(), 10)
|
assert BlogPost.objects.hint().count() == 10
|
||||||
self.assertEqual(BlogPost.objects.hint([("ZZ", 1)]).hint().count(), 10)
|
assert BlogPost.objects.hint([("ZZ", 1)]).hint().count() == 10
|
||||||
|
|
||||||
# Hinting on a non-existent index shape should fail.
|
# Hinting on a non-existent index shape should fail.
|
||||||
with self.assertRaises(OperationFailure):
|
with pytest.raises(OperationFailure):
|
||||||
BlogPost.objects.hint([("ZZ", 1)]).count()
|
BlogPost.objects.hint([("ZZ", 1)]).count()
|
||||||
|
|
||||||
# Hinting on a non-existent index name should fail.
|
# Hinting on a non-existent index name should fail.
|
||||||
with self.assertRaises(OperationFailure):
|
with pytest.raises(OperationFailure):
|
||||||
BlogPost.objects.hint("Bad Name").count()
|
BlogPost.objects.hint("Bad Name").count()
|
||||||
|
|
||||||
# Invalid shape argument (missing list brackets) should fail.
|
# Invalid shape argument (missing list brackets) should fail.
|
||||||
with self.assertRaises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
BlogPost.objects.hint(("tags", 1)).count()
|
BlogPost.objects.hint(("tags", 1)).count()
|
||||||
|
|
||||||
|
def test_collation(self):
|
||||||
|
base = {"locale": "en", "strength": 2}
|
||||||
|
|
||||||
|
class BlogPost(Document):
|
||||||
|
name = StringField()
|
||||||
|
meta = {
|
||||||
|
"indexes": [
|
||||||
|
{"fields": ["name"], "name": "name_index", "collation": base}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
names = ["tag1", "Tag2", "tag3", "Tag4", "tag5"]
|
||||||
|
for name in names:
|
||||||
|
BlogPost(name=name).save()
|
||||||
|
|
||||||
|
query_result = BlogPost.objects.collation(base).order_by("name")
|
||||||
|
assert [x.name for x in query_result] == sorted(names, key=lambda x: x.lower())
|
||||||
|
assert 5 == query_result.count()
|
||||||
|
|
||||||
|
query_result = BlogPost.objects.collation(Collation(**base)).order_by("name")
|
||||||
|
assert [x.name for x in query_result] == sorted(names, key=lambda x: x.lower())
|
||||||
|
assert 5 == query_result.count()
|
||||||
|
|
||||||
|
incorrect_collation = {"arndom": "wrdo"}
|
||||||
|
with pytest.raises(OperationFailure):
|
||||||
|
BlogPost.objects.collation(incorrect_collation).count()
|
||||||
|
|
||||||
|
query_result = BlogPost.objects.collation({}).order_by("name")
|
||||||
|
assert [x.name for x in query_result] == sorted(names)
|
||||||
|
|
||||||
def test_unique(self):
|
def test_unique(self):
|
||||||
"""Ensure that uniqueness constraints are applied to fields.
|
"""Ensure that uniqueness constraints are applied to fields.
|
||||||
"""
|
"""
|
||||||
@@ -554,11 +572,14 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Two posts with the same slug is not allowed
|
# Two posts with the same slug is not allowed
|
||||||
post2 = BlogPost(title="test2", slug="test")
|
post2 = BlogPost(title="test2", slug="test")
|
||||||
self.assertRaises(NotUniqueError, post2.save)
|
with pytest.raises(NotUniqueError):
|
||||||
self.assertRaises(NotUniqueError, BlogPost.objects.insert, post2)
|
post2.save()
|
||||||
|
with pytest.raises(NotUniqueError):
|
||||||
|
BlogPost.objects.insert(post2)
|
||||||
|
|
||||||
# Ensure backwards compatibility for errors
|
# Ensure backwards compatibility for errors
|
||||||
self.assertRaises(OperationError, post2.save)
|
with pytest.raises(OperationError):
|
||||||
|
post2.save()
|
||||||
|
|
||||||
def test_primary_key_unique_not_working(self):
|
def test_primary_key_unique_not_working(self):
|
||||||
"""Relates to #1445"""
|
"""Relates to #1445"""
|
||||||
@@ -568,23 +589,21 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
Blog.drop_collection()
|
Blog.drop_collection()
|
||||||
|
|
||||||
with self.assertRaises(OperationFailure) as ctx_err:
|
with pytest.raises(OperationFailure) as exc_info:
|
||||||
Blog(id="garbage").save()
|
Blog(id="garbage").save()
|
||||||
|
|
||||||
# One of the errors below should happen. Which one depends on the
|
# One of the errors below should happen. Which one depends on the
|
||||||
# PyMongo version and dict order.
|
# PyMongo version and dict order.
|
||||||
err_msg = str(ctx_err.exception)
|
err_msg = str(exc_info.value)
|
||||||
self.assertTrue(
|
assert any(
|
||||||
any(
|
[
|
||||||
[
|
"The field 'unique' is not valid for an _id index specification"
|
||||||
"The field 'unique' is not valid for an _id index specification"
|
in err_msg,
|
||||||
in err_msg,
|
"The field 'background' is not valid for an _id index specification"
|
||||||
"The field 'background' is not valid for an _id index specification"
|
in err_msg,
|
||||||
in err_msg,
|
"The field 'sparse' is not valid for an _id index specification"
|
||||||
"The field 'sparse' is not valid for an _id index specification"
|
in err_msg,
|
||||||
in err_msg,
|
]
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_unique_with(self):
|
def test_unique_with(self):
|
||||||
@@ -610,7 +629,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Now there will be two docs with the same slug and the same day: fail
|
# Now there will be two docs with the same slug and the same day: fail
|
||||||
post3 = BlogPost(title="test3", date=Date(year=2010), slug="test")
|
post3 = BlogPost(title="test3", date=Date(year=2010), slug="test")
|
||||||
self.assertRaises(OperationError, post3.save)
|
with pytest.raises(OperationError):
|
||||||
|
post3.save()
|
||||||
|
|
||||||
def test_unique_embedded_document(self):
|
def test_unique_embedded_document(self):
|
||||||
"""Ensure that uniqueness constraints are applied to fields on embedded documents.
|
"""Ensure that uniqueness constraints are applied to fields on embedded documents.
|
||||||
@@ -635,7 +655,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Now there will be two docs with the same sub.slug
|
# Now there will be two docs with the same sub.slug
|
||||||
post3 = BlogPost(title="test3", sub=SubDocument(year=2010, slug="test"))
|
post3 = BlogPost(title="test3", sub=SubDocument(year=2010, slug="test"))
|
||||||
self.assertRaises(NotUniqueError, post3.save)
|
with pytest.raises(NotUniqueError):
|
||||||
|
post3.save()
|
||||||
|
|
||||||
def test_unique_embedded_document_in_list(self):
|
def test_unique_embedded_document_in_list(self):
|
||||||
"""
|
"""
|
||||||
@@ -665,7 +686,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
post2 = BlogPost(title="test2", subs=[SubDocument(year=2014, slug="conflict")])
|
post2 = BlogPost(title="test2", subs=[SubDocument(year=2014, slug="conflict")])
|
||||||
|
|
||||||
self.assertRaises(NotUniqueError, post2.save)
|
with pytest.raises(NotUniqueError):
|
||||||
|
post2.save()
|
||||||
|
|
||||||
def test_unique_embedded_document_in_sorted_list(self):
|
def test_unique_embedded_document_in_sorted_list(self):
|
||||||
"""
|
"""
|
||||||
@@ -695,12 +717,13 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
# confirm that the unique index is created
|
# confirm that the unique index is created
|
||||||
indexes = BlogPost._get_collection().index_information()
|
indexes = BlogPost._get_collection().index_information()
|
||||||
self.assertIn("subs.slug_1", indexes)
|
assert "subs.slug_1" in indexes
|
||||||
self.assertTrue(indexes["subs.slug_1"]["unique"])
|
assert indexes["subs.slug_1"]["unique"]
|
||||||
|
|
||||||
post2 = BlogPost(title="test2", subs=[SubDocument(year=2014, slug="conflict")])
|
post2 = BlogPost(title="test2", subs=[SubDocument(year=2014, slug="conflict")])
|
||||||
|
|
||||||
self.assertRaises(NotUniqueError, post2.save)
|
with pytest.raises(NotUniqueError):
|
||||||
|
post2.save()
|
||||||
|
|
||||||
def test_unique_embedded_document_in_embedded_document_list(self):
|
def test_unique_embedded_document_in_embedded_document_list(self):
|
||||||
"""
|
"""
|
||||||
@@ -730,12 +753,13 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
# confirm that the unique index is created
|
# confirm that the unique index is created
|
||||||
indexes = BlogPost._get_collection().index_information()
|
indexes = BlogPost._get_collection().index_information()
|
||||||
self.assertIn("subs.slug_1", indexes)
|
assert "subs.slug_1" in indexes
|
||||||
self.assertTrue(indexes["subs.slug_1"]["unique"])
|
assert indexes["subs.slug_1"]["unique"]
|
||||||
|
|
||||||
post2 = BlogPost(title="test2", subs=[SubDocument(year=2014, slug="conflict")])
|
post2 = BlogPost(title="test2", subs=[SubDocument(year=2014, slug="conflict")])
|
||||||
|
|
||||||
self.assertRaises(NotUniqueError, post2.save)
|
with pytest.raises(NotUniqueError):
|
||||||
|
post2.save()
|
||||||
|
|
||||||
def test_unique_with_embedded_document_and_embedded_unique(self):
|
def test_unique_with_embedded_document_and_embedded_unique(self):
|
||||||
"""Ensure that uniqueness constraints are applied to fields on
|
"""Ensure that uniqueness constraints are applied to fields on
|
||||||
@@ -761,11 +785,13 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Now there will be two docs with the same sub.slug
|
# Now there will be two docs with the same sub.slug
|
||||||
post3 = BlogPost(title="test3", sub=SubDocument(year=2010, slug="test"))
|
post3 = BlogPost(title="test3", sub=SubDocument(year=2010, slug="test"))
|
||||||
self.assertRaises(NotUniqueError, post3.save)
|
with pytest.raises(NotUniqueError):
|
||||||
|
post3.save()
|
||||||
|
|
||||||
# Now there will be two docs with the same title and year
|
# Now there will be two docs with the same title and year
|
||||||
post3 = BlogPost(title="test1", sub=SubDocument(year=2009, slug="test-1"))
|
post3 = BlogPost(title="test1", sub=SubDocument(year=2009, slug="test-1"))
|
||||||
self.assertRaises(NotUniqueError, post3.save)
|
with pytest.raises(NotUniqueError):
|
||||||
|
post3.save()
|
||||||
|
|
||||||
def test_ttl_indexes(self):
|
def test_ttl_indexes(self):
|
||||||
class Log(Document):
|
class Log(Document):
|
||||||
@@ -777,19 +803,7 @@ class IndexesTest(unittest.TestCase):
|
|||||||
# Indexes are lazy so use list() to perform query
|
# Indexes are lazy so use list() to perform query
|
||||||
list(Log.objects)
|
list(Log.objects)
|
||||||
info = Log.objects._collection.index_information()
|
info = Log.objects._collection.index_information()
|
||||||
self.assertEqual(3600, info["created_1"]["expireAfterSeconds"])
|
assert 3600 == info["created_1"]["expireAfterSeconds"]
|
||||||
|
|
||||||
def test_index_drop_dups_silently_ignored(self):
|
|
||||||
class Customer(Document):
|
|
||||||
cust_id = IntField(unique=True, required=True)
|
|
||||||
meta = {
|
|
||||||
"indexes": ["cust_id"],
|
|
||||||
"index_drop_dups": True,
|
|
||||||
"allow_inheritance": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
Customer.drop_collection()
|
|
||||||
Customer.objects.first()
|
|
||||||
|
|
||||||
def test_unique_and_indexes(self):
|
def test_unique_and_indexes(self):
|
||||||
"""Ensure that 'unique' constraints aren't overridden by
|
"""Ensure that 'unique' constraints aren't overridden by
|
||||||
@@ -805,14 +819,14 @@ class IndexesTest(unittest.TestCase):
|
|||||||
cust.save()
|
cust.save()
|
||||||
|
|
||||||
cust_dupe = Customer(cust_id=1)
|
cust_dupe = Customer(cust_id=1)
|
||||||
with self.assertRaises(NotUniqueError):
|
with pytest.raises(NotUniqueError):
|
||||||
cust_dupe.save()
|
cust_dupe.save()
|
||||||
|
|
||||||
cust = Customer(cust_id=2)
|
cust = Customer(cust_id=2)
|
||||||
cust.save()
|
cust.save()
|
||||||
|
|
||||||
# duplicate key on update
|
# duplicate key on update
|
||||||
with self.assertRaises(NotUniqueError):
|
with pytest.raises(NotUniqueError):
|
||||||
cust.cust_id = 1
|
cust.cust_id = 1
|
||||||
cust.save()
|
cust.save()
|
||||||
|
|
||||||
@@ -833,8 +847,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
user = User(name="huangz", password="secret2")
|
user = User(name="huangz", password="secret2")
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
self.assertEqual(User.objects.count(), 1)
|
assert User.objects.count() == 1
|
||||||
self.assertEqual(User.objects.get().password, "secret2")
|
assert User.objects.get().password == "secret2"
|
||||||
|
|
||||||
def test_unique_and_primary_create(self):
|
def test_unique_and_primary_create(self):
|
||||||
"""Create a new record with a duplicate primary key
|
"""Create a new record with a duplicate primary key
|
||||||
@@ -848,11 +862,11 @@ class IndexesTest(unittest.TestCase):
|
|||||||
User.drop_collection()
|
User.drop_collection()
|
||||||
|
|
||||||
User.objects.create(name="huangz", password="secret")
|
User.objects.create(name="huangz", password="secret")
|
||||||
with self.assertRaises(NotUniqueError):
|
with pytest.raises(NotUniqueError):
|
||||||
User.objects.create(name="huangz", password="secret2")
|
User.objects.create(name="huangz", password="secret2")
|
||||||
|
|
||||||
self.assertEqual(User.objects.count(), 1)
|
assert User.objects.count() == 1
|
||||||
self.assertEqual(User.objects.get().password, "secret")
|
assert User.objects.get().password == "secret"
|
||||||
|
|
||||||
def test_index_with_pk(self):
|
def test_index_with_pk(self):
|
||||||
"""Ensure you can use `pk` as part of a query"""
|
"""Ensure you can use `pk` as part of a query"""
|
||||||
@@ -874,9 +888,9 @@ class IndexesTest(unittest.TestCase):
|
|||||||
self.fail("Unbound local error at index + pk definition")
|
self.fail("Unbound local error at index + pk definition")
|
||||||
|
|
||||||
info = BlogPost.objects._collection.index_information()
|
info = BlogPost.objects._collection.index_information()
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
index_item = [("_id", 1), ("comments.comment_id", 1)]
|
index_item = [("_id", 1), ("comments.comment_id", 1)]
|
||||||
self.assertIn(index_item, info)
|
assert index_item in info
|
||||||
|
|
||||||
def test_compound_key_embedded(self):
|
def test_compound_key_embedded(self):
|
||||||
class CompoundKey(EmbeddedDocument):
|
class CompoundKey(EmbeddedDocument):
|
||||||
@@ -890,10 +904,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
my_key = CompoundKey(name="n", term="ok")
|
my_key = CompoundKey(name="n", term="ok")
|
||||||
report = ReportEmbedded(text="OK", key=my_key).save()
|
report = ReportEmbedded(text="OK", key=my_key).save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert {"text": "OK", "_id": {"term": "ok", "name": "n"}} == report.to_mongo()
|
||||||
{"text": "OK", "_id": {"term": "ok", "name": "n"}}, report.to_mongo()
|
assert report == ReportEmbedded.objects.get(pk=my_key)
|
||||||
)
|
|
||||||
self.assertEqual(report, ReportEmbedded.objects.get(pk=my_key))
|
|
||||||
|
|
||||||
def test_compound_key_dictfield(self):
|
def test_compound_key_dictfield(self):
|
||||||
class ReportDictField(Document):
|
class ReportDictField(Document):
|
||||||
@@ -903,15 +915,13 @@ class IndexesTest(unittest.TestCase):
|
|||||||
my_key = {"name": "n", "term": "ok"}
|
my_key = {"name": "n", "term": "ok"}
|
||||||
report = ReportDictField(text="OK", key=my_key).save()
|
report = ReportDictField(text="OK", key=my_key).save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert {"text": "OK", "_id": {"term": "ok", "name": "n"}} == report.to_mongo()
|
||||||
{"text": "OK", "_id": {"term": "ok", "name": "n"}}, report.to_mongo()
|
|
||||||
)
|
|
||||||
|
|
||||||
# We can't directly call ReportDictField.objects.get(pk=my_key),
|
# We can't directly call ReportDictField.objects.get(pk=my_key),
|
||||||
# because dicts are unordered, and if the order in MongoDB is
|
# because dicts are unordered, and if the order in MongoDB is
|
||||||
# different than the one in `my_key`, this test will fail.
|
# different than the one in `my_key`, this test will fail.
|
||||||
self.assertEqual(report, ReportDictField.objects.get(pk__name=my_key["name"]))
|
assert report == ReportDictField.objects.get(pk__name=my_key["name"])
|
||||||
self.assertEqual(report, ReportDictField.objects.get(pk__term=my_key["term"]))
|
assert report == ReportDictField.objects.get(pk__term=my_key["term"])
|
||||||
|
|
||||||
def test_string_indexes(self):
|
def test_string_indexes(self):
|
||||||
class MyDoc(Document):
|
class MyDoc(Document):
|
||||||
@@ -919,9 +929,9 @@ class IndexesTest(unittest.TestCase):
|
|||||||
meta = {"indexes": ["provider_ids.foo", "provider_ids.bar"]}
|
meta = {"indexes": ["provider_ids.foo", "provider_ids.bar"]}
|
||||||
|
|
||||||
info = MyDoc.objects._collection.index_information()
|
info = MyDoc.objects._collection.index_information()
|
||||||
info = [value["key"] for key, value in iteritems(info)]
|
info = [value["key"] for key, value in info.items()]
|
||||||
self.assertIn([("provider_ids.foo", 1)], info)
|
assert [("provider_ids.foo", 1)] in info
|
||||||
self.assertIn([("provider_ids.bar", 1)], info)
|
assert [("provider_ids.bar", 1)] in info
|
||||||
|
|
||||||
def test_sparse_compound_indexes(self):
|
def test_sparse_compound_indexes(self):
|
||||||
class MyDoc(Document):
|
class MyDoc(Document):
|
||||||
@@ -933,11 +943,10 @@ class IndexesTest(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
info = MyDoc.objects._collection.index_information()
|
info = MyDoc.objects._collection.index_information()
|
||||||
self.assertEqual(
|
assert [("provider_ids.foo", 1), ("provider_ids.bar", 1)] == info[
|
||||||
[("provider_ids.foo", 1), ("provider_ids.bar", 1)],
|
"provider_ids.foo_1_provider_ids.bar_1"
|
||||||
info["provider_ids.foo_1_provider_ids.bar_1"]["key"],
|
]["key"]
|
||||||
)
|
assert info["provider_ids.foo_1_provider_ids.bar_1"]["sparse"]
|
||||||
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):
|
||||||
@@ -945,9 +954,9 @@ class IndexesTest(unittest.TestCase):
|
|||||||
meta = {"indexes": ["$title"]}
|
meta = {"indexes": ["$title"]}
|
||||||
|
|
||||||
indexes = Book.objects._collection.index_information()
|
indexes = Book.objects._collection.index_information()
|
||||||
self.assertIn("title_text", indexes)
|
assert "title_text" in indexes
|
||||||
key = indexes["title_text"]["key"]
|
key = indexes["title_text"]["key"]
|
||||||
self.assertIn(("_fts", "text"), key)
|
assert ("_fts", "text") in key
|
||||||
|
|
||||||
def test_hashed_indexes(self):
|
def test_hashed_indexes(self):
|
||||||
class Book(Document):
|
class Book(Document):
|
||||||
@@ -955,8 +964,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
meta = {"indexes": ["#ref_id"]}
|
meta = {"indexes": ["#ref_id"]}
|
||||||
|
|
||||||
indexes = Book.objects._collection.index_information()
|
indexes = Book.objects._collection.index_information()
|
||||||
self.assertIn("ref_id_hashed", indexes)
|
assert "ref_id_hashed" in indexes
|
||||||
self.assertIn(("ref_id", "hashed"), indexes["ref_id_hashed"]["key"])
|
assert ("ref_id", "hashed") in indexes["ref_id_hashed"]["key"]
|
||||||
|
|
||||||
def test_indexes_after_database_drop(self):
|
def test_indexes_after_database_drop(self):
|
||||||
"""
|
"""
|
||||||
@@ -993,7 +1002,8 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Create Post #2
|
# Create Post #2
|
||||||
post2 = BlogPost(title="test2", slug="test")
|
post2 = BlogPost(title="test2", slug="test")
|
||||||
self.assertRaises(NotUniqueError, post2.save)
|
with pytest.raises(NotUniqueError):
|
||||||
|
post2.save()
|
||||||
finally:
|
finally:
|
||||||
# Drop the temporary database at the end
|
# Drop the temporary database at the end
|
||||||
connection.drop_database("tempdatabase")
|
connection.drop_database("tempdatabase")
|
||||||
@@ -1035,20 +1045,13 @@ class IndexesTest(unittest.TestCase):
|
|||||||
del index_info[key][
|
del index_info[key][
|
||||||
"ns"
|
"ns"
|
||||||
] # drop the index namespace - we don't care about that here, MongoDB 3+
|
] # drop the index namespace - we don't care about that here, MongoDB 3+
|
||||||
if "dropDups" in index_info[key]:
|
|
||||||
del index_info[key][
|
|
||||||
"dropDups"
|
|
||||||
] # drop the index dropDups - it is deprecated in MongoDB 3+
|
|
||||||
|
|
||||||
self.assertEqual(
|
assert index_info == {
|
||||||
index_info,
|
"txt_1": {"key": [("txt", 1)], "background": False},
|
||||||
{
|
"_id_": {"key": [("_id", 1)]},
|
||||||
"txt_1": {"key": [("txt", 1)], "background": False},
|
"txt2_1": {"key": [("txt2", 1)], "background": False},
|
||||||
"_id_": {"key": [("_id", 1)]},
|
"_cls_1": {"key": [("_cls", 1)], "background": False},
|
||||||
"txt2_1": {"key": [("txt2", 1)], "background": False},
|
}
|
||||||
"_cls_1": {"key": [("_cls", 1)], "background": False},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_compound_index_underscore_cls_not_overwritten(self):
|
def test_compound_index_underscore_cls_not_overwritten(self):
|
||||||
"""
|
"""
|
||||||
@@ -1071,7 +1074,7 @@ class IndexesTest(unittest.TestCase):
|
|||||||
TestDoc.ensure_indexes()
|
TestDoc.ensure_indexes()
|
||||||
|
|
||||||
index_info = TestDoc._get_collection().index_information()
|
index_info = TestDoc._get_collection().index_information()
|
||||||
self.assertIn("shard_1_1__cls_1_txt_1_1", index_info)
|
assert "shard_1_1__cls_1_txt_1_1" in index_info
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from six import iteritems
|
import pytest
|
||||||
|
|
||||||
from mongoengine import (
|
from mongoengine import (
|
||||||
BooleanField,
|
BooleanField,
|
||||||
@@ -15,13 +15,11 @@ from mongoengine import (
|
|||||||
StringField,
|
StringField,
|
||||||
)
|
)
|
||||||
from mongoengine.pymongo_support import list_collection_names
|
from mongoengine.pymongo_support import list_collection_names
|
||||||
from tests.utils import MongoDBTestCase
|
|
||||||
from tests.fixtures import Base
|
from tests.fixtures import Base
|
||||||
|
from tests.utils import MongoDBTestCase
|
||||||
__all__ = ("InheritanceTest",)
|
|
||||||
|
|
||||||
|
|
||||||
class InheritanceTest(MongoDBTestCase):
|
class TestInheritance(MongoDBTestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
for collection in list_collection_names(self.db):
|
for collection in list_collection_names(self.db):
|
||||||
self.db.drop_collection(collection)
|
self.db.drop_collection(collection)
|
||||||
@@ -39,12 +37,12 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
meta = {"allow_inheritance": True}
|
meta = {"allow_inheritance": True}
|
||||||
|
|
||||||
test_doc = DataDoc(name="test", embed=EmbedData(data="data"))
|
test_doc = DataDoc(name="test", embed=EmbedData(data="data"))
|
||||||
self.assertEqual(test_doc._cls, "DataDoc")
|
assert test_doc._cls == "DataDoc"
|
||||||
self.assertEqual(test_doc.embed._cls, "EmbedData")
|
assert test_doc.embed._cls == "EmbedData"
|
||||||
test_doc.save()
|
test_doc.save()
|
||||||
saved_doc = DataDoc.objects.with_id(test_doc.id)
|
saved_doc = DataDoc.objects.with_id(test_doc.id)
|
||||||
self.assertEqual(test_doc._cls, saved_doc._cls)
|
assert test_doc._cls == saved_doc._cls
|
||||||
self.assertEqual(test_doc.embed._cls, saved_doc.embed._cls)
|
assert test_doc.embed._cls == saved_doc.embed._cls
|
||||||
test_doc.delete()
|
test_doc.delete()
|
||||||
|
|
||||||
def test_superclasses(self):
|
def test_superclasses(self):
|
||||||
@@ -69,12 +67,12 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Human(Mammal):
|
class Human(Mammal):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(Animal._superclasses, ())
|
assert Animal._superclasses == ()
|
||||||
self.assertEqual(Fish._superclasses, ("Animal",))
|
assert Fish._superclasses == ("Animal",)
|
||||||
self.assertEqual(Guppy._superclasses, ("Animal", "Animal.Fish"))
|
assert Guppy._superclasses == ("Animal", "Animal.Fish")
|
||||||
self.assertEqual(Mammal._superclasses, ("Animal",))
|
assert Mammal._superclasses == ("Animal",)
|
||||||
self.assertEqual(Dog._superclasses, ("Animal", "Animal.Mammal"))
|
assert Dog._superclasses == ("Animal", "Animal.Mammal")
|
||||||
self.assertEqual(Human._superclasses, ("Animal", "Animal.Mammal"))
|
assert Human._superclasses == ("Animal", "Animal.Mammal")
|
||||||
|
|
||||||
def test_external_superclasses(self):
|
def test_external_superclasses(self):
|
||||||
"""Ensure that the correct list of super classes is assembled when
|
"""Ensure that the correct list of super classes is assembled when
|
||||||
@@ -99,18 +97,12 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Human(Mammal):
|
class Human(Mammal):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(Animal._superclasses, ("Base",))
|
assert Animal._superclasses == ("Base",)
|
||||||
self.assertEqual(Fish._superclasses, ("Base", "Base.Animal"))
|
assert Fish._superclasses == ("Base", "Base.Animal")
|
||||||
self.assertEqual(
|
assert Guppy._superclasses == ("Base", "Base.Animal", "Base.Animal.Fish")
|
||||||
Guppy._superclasses, ("Base", "Base.Animal", "Base.Animal.Fish")
|
assert Mammal._superclasses == ("Base", "Base.Animal")
|
||||||
)
|
assert Dog._superclasses == ("Base", "Base.Animal", "Base.Animal.Mammal")
|
||||||
self.assertEqual(Mammal._superclasses, ("Base", "Base.Animal"))
|
assert Human._superclasses == ("Base", "Base.Animal", "Base.Animal.Mammal")
|
||||||
self.assertEqual(
|
|
||||||
Dog._superclasses, ("Base", "Base.Animal", "Base.Animal.Mammal")
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
Human._superclasses, ("Base", "Base.Animal", "Base.Animal.Mammal")
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_subclasses(self):
|
def test_subclasses(self):
|
||||||
"""Ensure that the correct list of _subclasses (subclasses) is
|
"""Ensure that the correct list of _subclasses (subclasses) is
|
||||||
@@ -135,24 +127,22 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Human(Mammal):
|
class Human(Mammal):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(
|
assert Animal._subclasses == (
|
||||||
Animal._subclasses,
|
"Animal",
|
||||||
(
|
"Animal.Fish",
|
||||||
"Animal",
|
"Animal.Fish.Guppy",
|
||||||
"Animal.Fish",
|
"Animal.Mammal",
|
||||||
"Animal.Fish.Guppy",
|
"Animal.Mammal.Dog",
|
||||||
"Animal.Mammal",
|
"Animal.Mammal.Human",
|
||||||
"Animal.Mammal.Dog",
|
|
||||||
"Animal.Mammal.Human",
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
self.assertEqual(Fish._subclasses, ("Animal.Fish", "Animal.Fish.Guppy"))
|
assert Fish._subclasses == ("Animal.Fish", "Animal.Fish.Guppy")
|
||||||
self.assertEqual(Guppy._subclasses, ("Animal.Fish.Guppy",))
|
assert Guppy._subclasses == ("Animal.Fish.Guppy",)
|
||||||
self.assertEqual(
|
assert Mammal._subclasses == (
|
||||||
Mammal._subclasses,
|
"Animal.Mammal",
|
||||||
("Animal.Mammal", "Animal.Mammal.Dog", "Animal.Mammal.Human"),
|
"Animal.Mammal.Dog",
|
||||||
|
"Animal.Mammal.Human",
|
||||||
)
|
)
|
||||||
self.assertEqual(Human._subclasses, ("Animal.Mammal.Human",))
|
assert Human._subclasses == ("Animal.Mammal.Human",)
|
||||||
|
|
||||||
def test_external_subclasses(self):
|
def test_external_subclasses(self):
|
||||||
"""Ensure that the correct list of _subclasses (subclasses) is
|
"""Ensure that the correct list of _subclasses (subclasses) is
|
||||||
@@ -177,30 +167,22 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Human(Mammal):
|
class Human(Mammal):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(
|
assert Animal._subclasses == (
|
||||||
Animal._subclasses,
|
"Base.Animal",
|
||||||
(
|
"Base.Animal.Fish",
|
||||||
"Base.Animal",
|
"Base.Animal.Fish.Guppy",
|
||||||
"Base.Animal.Fish",
|
"Base.Animal.Mammal",
|
||||||
"Base.Animal.Fish.Guppy",
|
"Base.Animal.Mammal.Dog",
|
||||||
"Base.Animal.Mammal",
|
"Base.Animal.Mammal.Human",
|
||||||
"Base.Animal.Mammal.Dog",
|
|
||||||
"Base.Animal.Mammal.Human",
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert Fish._subclasses == ("Base.Animal.Fish", "Base.Animal.Fish.Guppy")
|
||||||
Fish._subclasses, ("Base.Animal.Fish", "Base.Animal.Fish.Guppy")
|
assert Guppy._subclasses == ("Base.Animal.Fish.Guppy",)
|
||||||
|
assert Mammal._subclasses == (
|
||||||
|
"Base.Animal.Mammal",
|
||||||
|
"Base.Animal.Mammal.Dog",
|
||||||
|
"Base.Animal.Mammal.Human",
|
||||||
)
|
)
|
||||||
self.assertEqual(Guppy._subclasses, ("Base.Animal.Fish.Guppy",))
|
assert Human._subclasses == ("Base.Animal.Mammal.Human",)
|
||||||
self.assertEqual(
|
|
||||||
Mammal._subclasses,
|
|
||||||
(
|
|
||||||
"Base.Animal.Mammal",
|
|
||||||
"Base.Animal.Mammal.Dog",
|
|
||||||
"Base.Animal.Mammal.Human",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
self.assertEqual(Human._subclasses, ("Base.Animal.Mammal.Human",))
|
|
||||||
|
|
||||||
def test_dynamic_declarations(self):
|
def test_dynamic_declarations(self):
|
||||||
"""Test that declaring an extra class updates meta data"""
|
"""Test that declaring an extra class updates meta data"""
|
||||||
@@ -208,33 +190,31 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
meta = {"allow_inheritance": True}
|
meta = {"allow_inheritance": True}
|
||||||
|
|
||||||
self.assertEqual(Animal._superclasses, ())
|
assert Animal._superclasses == ()
|
||||||
self.assertEqual(Animal._subclasses, ("Animal",))
|
assert Animal._subclasses == ("Animal",)
|
||||||
|
|
||||||
# Test dynamically adding a class changes the meta data
|
# Test dynamically adding a class changes the meta data
|
||||||
class Fish(Animal):
|
class Fish(Animal):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(Animal._superclasses, ())
|
assert Animal._superclasses == ()
|
||||||
self.assertEqual(Animal._subclasses, ("Animal", "Animal.Fish"))
|
assert Animal._subclasses == ("Animal", "Animal.Fish")
|
||||||
|
|
||||||
self.assertEqual(Fish._superclasses, ("Animal",))
|
assert Fish._superclasses == ("Animal",)
|
||||||
self.assertEqual(Fish._subclasses, ("Animal.Fish",))
|
assert Fish._subclasses == ("Animal.Fish",)
|
||||||
|
|
||||||
# Test dynamically adding an inherited class changes the meta data
|
# Test dynamically adding an inherited class changes the meta data
|
||||||
class Pike(Fish):
|
class Pike(Fish):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(Animal._superclasses, ())
|
assert Animal._superclasses == ()
|
||||||
self.assertEqual(
|
assert Animal._subclasses == ("Animal", "Animal.Fish", "Animal.Fish.Pike")
|
||||||
Animal._subclasses, ("Animal", "Animal.Fish", "Animal.Fish.Pike")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(Fish._superclasses, ("Animal",))
|
assert Fish._superclasses == ("Animal",)
|
||||||
self.assertEqual(Fish._subclasses, ("Animal.Fish", "Animal.Fish.Pike"))
|
assert Fish._subclasses == ("Animal.Fish", "Animal.Fish.Pike")
|
||||||
|
|
||||||
self.assertEqual(Pike._superclasses, ("Animal", "Animal.Fish"))
|
assert Pike._superclasses == ("Animal", "Animal.Fish")
|
||||||
self.assertEqual(Pike._subclasses, ("Animal.Fish.Pike",))
|
assert Pike._subclasses == ("Animal.Fish.Pike",)
|
||||||
|
|
||||||
def test_inheritance_meta_data(self):
|
def test_inheritance_meta_data(self):
|
||||||
"""Ensure that document may inherit fields from a superclass document.
|
"""Ensure that document may inherit fields from a superclass document.
|
||||||
@@ -249,10 +229,10 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Employee(Person):
|
class Employee(Person):
|
||||||
salary = IntField()
|
salary = IntField()
|
||||||
|
|
||||||
self.assertEqual(
|
assert ["_cls", "age", "id", "name", "salary"] == sorted(
|
||||||
["_cls", "age", "id", "name", "salary"], sorted(Employee._fields.keys())
|
Employee._fields.keys()
|
||||||
)
|
)
|
||||||
self.assertEqual(Employee._get_collection_name(), Person._get_collection_name())
|
assert Employee._get_collection_name() == Person._get_collection_name()
|
||||||
|
|
||||||
def test_inheritance_to_mongo_keys(self):
|
def test_inheritance_to_mongo_keys(self):
|
||||||
"""Ensure that document may inherit fields from a superclass document.
|
"""Ensure that document may inherit fields from a superclass document.
|
||||||
@@ -267,17 +247,17 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Employee(Person):
|
class Employee(Person):
|
||||||
salary = IntField()
|
salary = IntField()
|
||||||
|
|
||||||
self.assertEqual(
|
assert ["_cls", "age", "id", "name", "salary"] == sorted(
|
||||||
["_cls", "age", "id", "name", "salary"], sorted(Employee._fields.keys())
|
Employee._fields.keys()
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert Person(name="Bob", age=35).to_mongo().keys() == ["_cls", "name", "age"]
|
||||||
Person(name="Bob", age=35).to_mongo().keys(), ["_cls", "name", "age"]
|
assert Employee(name="Bob", age=35, salary=0).to_mongo().keys() == [
|
||||||
)
|
"_cls",
|
||||||
self.assertEqual(
|
"name",
|
||||||
Employee(name="Bob", age=35, salary=0).to_mongo().keys(),
|
"age",
|
||||||
["_cls", "name", "age", "salary"],
|
"salary",
|
||||||
)
|
]
|
||||||
self.assertEqual(Employee._get_collection_name(), Person._get_collection_name())
|
assert Employee._get_collection_name() == Person._get_collection_name()
|
||||||
|
|
||||||
def test_indexes_and_multiple_inheritance(self):
|
def test_indexes_and_multiple_inheritance(self):
|
||||||
""" Ensure that all of the indexes are created for a document with
|
""" Ensure that all of the indexes are created for a document with
|
||||||
@@ -303,13 +283,10 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
|
|
||||||
C.ensure_indexes()
|
C.ensure_indexes()
|
||||||
|
|
||||||
self.assertEqual(
|
assert sorted(
|
||||||
sorted(
|
[idx["key"] for idx in C._get_collection().index_information().values()]
|
||||||
[idx["key"] for idx in C._get_collection().index_information().values()]
|
) == sorted(
|
||||||
),
|
[[(u"_cls", 1), (u"b", 1)], [(u"_id", 1)], [(u"_cls", 1), (u"a", 1)]]
|
||||||
sorted(
|
|
||||||
[[(u"_cls", 1), (u"b", 1)], [(u"_id", 1)], [(u"_cls", 1), (u"a", 1)]]
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_polymorphic_queries(self):
|
def test_polymorphic_queries(self):
|
||||||
@@ -340,13 +317,13 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
Human().save()
|
Human().save()
|
||||||
|
|
||||||
classes = [obj.__class__ for obj in Animal.objects]
|
classes = [obj.__class__ for obj in Animal.objects]
|
||||||
self.assertEqual(classes, [Animal, Fish, Mammal, Dog, Human])
|
assert classes == [Animal, Fish, Mammal, Dog, Human]
|
||||||
|
|
||||||
classes = [obj.__class__ for obj in Mammal.objects]
|
classes = [obj.__class__ for obj in Mammal.objects]
|
||||||
self.assertEqual(classes, [Mammal, Dog, Human])
|
assert classes == [Mammal, Dog, Human]
|
||||||
|
|
||||||
classes = [obj.__class__ for obj in Human.objects]
|
classes = [obj.__class__ for obj in Human.objects]
|
||||||
self.assertEqual(classes, [Human])
|
assert classes == [Human]
|
||||||
|
|
||||||
def test_allow_inheritance(self):
|
def test_allow_inheritance(self):
|
||||||
"""Ensure that inheritance is disabled by default on simple
|
"""Ensure that inheritance is disabled by default on simple
|
||||||
@@ -357,20 +334,18 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
# can't inherit because Animal didn't explicitly allow inheritance
|
# can't inherit because Animal didn't explicitly allow inheritance
|
||||||
with self.assertRaises(ValueError) as cm:
|
with pytest.raises(ValueError, match="Document Animal may not be subclassed"):
|
||||||
|
|
||||||
class Dog(Animal):
|
class Dog(Animal):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertIn("Document Animal may not be subclassed", str(cm.exception))
|
|
||||||
|
|
||||||
# Check that _cls etc aren't present on simple documents
|
# Check that _cls etc aren't present on simple documents
|
||||||
dog = Animal(name="dog").save()
|
dog = Animal(name="dog").save()
|
||||||
self.assertEqual(dog.to_mongo().keys(), ["_id", "name"])
|
assert dog.to_mongo().keys() == ["_id", "name"]
|
||||||
|
|
||||||
collection = self.db[Animal._get_collection_name()]
|
collection = self.db[Animal._get_collection_name()]
|
||||||
obj = collection.find_one()
|
obj = collection.find_one()
|
||||||
self.assertNotIn("_cls", obj)
|
assert "_cls" not in obj
|
||||||
|
|
||||||
def test_cant_turn_off_inheritance_on_subclass(self):
|
def test_cant_turn_off_inheritance_on_subclass(self):
|
||||||
"""Ensure if inheritance is on in a subclass you cant turn it off.
|
"""Ensure if inheritance is on in a subclass you cant turn it off.
|
||||||
@@ -380,14 +355,14 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
name = StringField()
|
name = StringField()
|
||||||
meta = {"allow_inheritance": True}
|
meta = {"allow_inheritance": True}
|
||||||
|
|
||||||
with self.assertRaises(ValueError) as cm:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
|
|
||||||
class Mammal(Animal):
|
class Mammal(Animal):
|
||||||
meta = {"allow_inheritance": False}
|
meta = {"allow_inheritance": False}
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
str(cm.exception),
|
str(exc_info.value)
|
||||||
'Only direct subclasses of Document may set "allow_inheritance" to False',
|
== 'Only direct subclasses of Document may set "allow_inheritance" to False'
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_allow_inheritance_abstract_document(self):
|
def test_allow_inheritance_abstract_document(self):
|
||||||
@@ -401,14 +376,14 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Animal(FinalDocument):
|
class Animal(FinalDocument):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
with self.assertRaises(ValueError) as cm:
|
with pytest.raises(ValueError):
|
||||||
|
|
||||||
class Mammal(Animal):
|
class Mammal(Animal):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Check that _cls isn't present in simple documents
|
# Check that _cls isn't present in simple documents
|
||||||
doc = Animal(name="dog")
|
doc = Animal(name="dog")
|
||||||
self.assertNotIn("_cls", doc.to_mongo())
|
assert "_cls" not in doc.to_mongo()
|
||||||
|
|
||||||
def test_using_abstract_class_in_reference_field(self):
|
def test_using_abstract_class_in_reference_field(self):
|
||||||
# Ensures no regression of #1920
|
# Ensures no regression of #1920
|
||||||
@@ -454,10 +429,10 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
berlin = EuropeanCity(name="Berlin", continent="Europe")
|
berlin = EuropeanCity(name="Berlin", continent="Europe")
|
||||||
self.assertEqual(len(berlin._db_field_map), len(berlin._fields_ordered))
|
assert len(berlin._db_field_map) == len(berlin._fields_ordered)
|
||||||
self.assertEqual(len(berlin._reverse_db_field_map), len(berlin._fields_ordered))
|
assert len(berlin._reverse_db_field_map) == len(berlin._fields_ordered)
|
||||||
self.assertEqual(len(berlin._fields_ordered), 3)
|
assert len(berlin._fields_ordered) == 3
|
||||||
self.assertEqual(berlin._fields_ordered[0], "id")
|
assert berlin._fields_ordered[0] == "id"
|
||||||
|
|
||||||
def test_auto_id_not_set_if_specific_in_parent_class(self):
|
def test_auto_id_not_set_if_specific_in_parent_class(self):
|
||||||
class City(Document):
|
class City(Document):
|
||||||
@@ -469,10 +444,10 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
berlin = EuropeanCity(name="Berlin", continent="Europe")
|
berlin = EuropeanCity(name="Berlin", continent="Europe")
|
||||||
self.assertEqual(len(berlin._db_field_map), len(berlin._fields_ordered))
|
assert len(berlin._db_field_map) == len(berlin._fields_ordered)
|
||||||
self.assertEqual(len(berlin._reverse_db_field_map), len(berlin._fields_ordered))
|
assert len(berlin._reverse_db_field_map) == len(berlin._fields_ordered)
|
||||||
self.assertEqual(len(berlin._fields_ordered), 3)
|
assert len(berlin._fields_ordered) == 3
|
||||||
self.assertEqual(berlin._fields_ordered[0], "city_id")
|
assert berlin._fields_ordered[0] == "city_id"
|
||||||
|
|
||||||
def test_auto_id_vs_non_pk_id_field(self):
|
def test_auto_id_vs_non_pk_id_field(self):
|
||||||
class City(Document):
|
class City(Document):
|
||||||
@@ -484,12 +459,12 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
berlin = EuropeanCity(name="Berlin", continent="Europe")
|
berlin = EuropeanCity(name="Berlin", continent="Europe")
|
||||||
self.assertEqual(len(berlin._db_field_map), len(berlin._fields_ordered))
|
assert len(berlin._db_field_map) == len(berlin._fields_ordered)
|
||||||
self.assertEqual(len(berlin._reverse_db_field_map), len(berlin._fields_ordered))
|
assert len(berlin._reverse_db_field_map) == len(berlin._fields_ordered)
|
||||||
self.assertEqual(len(berlin._fields_ordered), 4)
|
assert len(berlin._fields_ordered) == 4
|
||||||
self.assertEqual(berlin._fields_ordered[0], "auto_id_0")
|
assert berlin._fields_ordered[0] == "auto_id_0"
|
||||||
berlin.save()
|
berlin.save()
|
||||||
self.assertEqual(berlin.pk, berlin.auto_id_0)
|
assert berlin.pk == berlin.auto_id_0
|
||||||
|
|
||||||
def test_abstract_document_creation_does_not_fail(self):
|
def test_abstract_document_creation_does_not_fail(self):
|
||||||
class City(Document):
|
class City(Document):
|
||||||
@@ -497,9 +472,9 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
meta = {"abstract": True, "allow_inheritance": False}
|
meta = {"abstract": True, "allow_inheritance": False}
|
||||||
|
|
||||||
city = City(continent="asia")
|
city = City(continent="asia")
|
||||||
self.assertEqual(None, city.pk)
|
assert city.pk is None
|
||||||
# TODO: expected error? Shouldn't we create a new error type?
|
# TODO: expected error? Shouldn't we create a new error type?
|
||||||
with self.assertRaises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
setattr(city, "pk", 1)
|
setattr(city, "pk", 1)
|
||||||
|
|
||||||
def test_allow_inheritance_embedded_document(self):
|
def test_allow_inheritance_embedded_document(self):
|
||||||
@@ -508,20 +483,20 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Comment(EmbeddedDocument):
|
class Comment(EmbeddedDocument):
|
||||||
content = StringField()
|
content = StringField()
|
||||||
|
|
||||||
with self.assertRaises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
|
|
||||||
class SpecialComment(Comment):
|
class SpecialComment(Comment):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
doc = Comment(content="test")
|
doc = Comment(content="test")
|
||||||
self.assertNotIn("_cls", doc.to_mongo())
|
assert "_cls" not in doc.to_mongo()
|
||||||
|
|
||||||
class Comment(EmbeddedDocument):
|
class Comment(EmbeddedDocument):
|
||||||
content = StringField()
|
content = StringField()
|
||||||
meta = {"allow_inheritance": True}
|
meta = {"allow_inheritance": True}
|
||||||
|
|
||||||
doc = Comment(content="test")
|
doc = Comment(content="test")
|
||||||
self.assertIn("_cls", doc.to_mongo())
|
assert "_cls" in doc.to_mongo()
|
||||||
|
|
||||||
def test_document_inheritance(self):
|
def test_document_inheritance(self):
|
||||||
"""Ensure mutliple inheritance of abstract documents
|
"""Ensure mutliple inheritance of abstract documents
|
||||||
@@ -539,7 +514,7 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
self.assertTrue(False, "Couldn't create MyDocument class")
|
assert False, "Couldn't create MyDocument class"
|
||||||
|
|
||||||
def test_abstract_documents(self):
|
def test_abstract_documents(self):
|
||||||
"""Ensure that a document superclass can be marked as abstract
|
"""Ensure that a document superclass can be marked as abstract
|
||||||
@@ -547,7 +522,6 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
|
|
||||||
defaults = {
|
defaults = {
|
||||||
"index_background": True,
|
"index_background": True,
|
||||||
"index_drop_dups": True,
|
|
||||||
"index_opts": {"hello": "world"},
|
"index_opts": {"hello": "world"},
|
||||||
"allow_inheritance": True,
|
"allow_inheritance": True,
|
||||||
"queryset_class": "QuerySet",
|
"queryset_class": "QuerySet",
|
||||||
@@ -574,22 +548,22 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class Human(Mammal):
|
class Human(Mammal):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
for k, v in iteritems(defaults):
|
for k, v in defaults.items():
|
||||||
for cls in [Animal, Fish, Guppy]:
|
for cls in [Animal, Fish, Guppy]:
|
||||||
self.assertEqual(cls._meta[k], v)
|
assert cls._meta[k] == v
|
||||||
|
|
||||||
self.assertNotIn("collection", Animal._meta)
|
assert "collection" not in Animal._meta
|
||||||
self.assertNotIn("collection", Mammal._meta)
|
assert "collection" not in Mammal._meta
|
||||||
|
|
||||||
self.assertEqual(Animal._get_collection_name(), None)
|
assert Animal._get_collection_name() is None
|
||||||
self.assertEqual(Mammal._get_collection_name(), None)
|
assert Mammal._get_collection_name() is None
|
||||||
|
|
||||||
self.assertEqual(Fish._get_collection_name(), "fish")
|
assert Fish._get_collection_name() == "fish"
|
||||||
self.assertEqual(Guppy._get_collection_name(), "fish")
|
assert Guppy._get_collection_name() == "fish"
|
||||||
self.assertEqual(Human._get_collection_name(), "human")
|
assert Human._get_collection_name() == "human"
|
||||||
|
|
||||||
# ensure that a subclass of a non-abstract class can't be abstract
|
# ensure that a subclass of a non-abstract class can't be abstract
|
||||||
with self.assertRaises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
|
|
||||||
class EvilHuman(Human):
|
class EvilHuman(Human):
|
||||||
evil = BooleanField(default=True)
|
evil = BooleanField(default=True)
|
||||||
@@ -603,7 +577,7 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
class B(A):
|
class B(A):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertFalse(B._meta["abstract"])
|
assert not B._meta["abstract"]
|
||||||
|
|
||||||
def test_inherited_collections(self):
|
def test_inherited_collections(self):
|
||||||
"""Ensure that subclassed documents don't override parents'
|
"""Ensure that subclassed documents don't override parents'
|
||||||
@@ -649,8 +623,8 @@ class InheritanceTest(MongoDBTestCase):
|
|||||||
real_person = Drinker(drink=beer)
|
real_person = Drinker(drink=beer)
|
||||||
real_person.save()
|
real_person.save()
|
||||||
|
|
||||||
self.assertEqual(Drinker.objects[0].drink.name, red_bull.name)
|
assert Drinker.objects[0].drink.name == red_bull.name
|
||||||
self.assertEqual(Drinker.objects[1].drink.name, beer.name)
|
assert Drinker.objects[1].drink.name == beer.name
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,14 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from nose.plugins.skip import SkipTest
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
|
|
||||||
import pymongo
|
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
|
from tests.utils import MongoDBTestCase
|
||||||
__all__ = ("TestJson",)
|
|
||||||
|
|
||||||
|
|
||||||
class TestJson(unittest.TestCase):
|
class TestJson(MongoDBTestCase):
|
||||||
def setUp(self):
|
|
||||||
connect(db="mongoenginetest")
|
|
||||||
|
|
||||||
def test_json_names(self):
|
def test_json_names(self):
|
||||||
"""
|
"""
|
||||||
Going to test reported issue:
|
Going to test reported issue:
|
||||||
@@ -39,7 +32,7 @@ class TestJson(unittest.TestCase):
|
|||||||
|
|
||||||
expected_json = """{"embedded":{"string":"Inner Hello"},"string":"Hello"}"""
|
expected_json = """{"embedded":{"string":"Inner Hello"},"string":"Hello"}"""
|
||||||
|
|
||||||
self.assertEqual(doc_json, expected_json)
|
assert doc_json == expected_json
|
||||||
|
|
||||||
def test_json_simple(self):
|
def test_json_simple(self):
|
||||||
class Embedded(EmbeddedDocument):
|
class Embedded(EmbeddedDocument):
|
||||||
@@ -59,9 +52,9 @@ class TestJson(unittest.TestCase):
|
|||||||
|
|
||||||
doc_json = doc.to_json(sort_keys=True, separators=(",", ":"))
|
doc_json = doc.to_json(sort_keys=True, separators=(",", ":"))
|
||||||
expected_json = """{"embedded_field":{"string":"Hi"},"string":"Hi"}"""
|
expected_json = """{"embedded_field":{"string":"Hi"},"string":"Hi"}"""
|
||||||
self.assertEqual(doc_json, expected_json)
|
assert doc_json == expected_json
|
||||||
|
|
||||||
self.assertEqual(doc, Doc.from_json(doc.to_json()))
|
assert doc == Doc.from_json(doc.to_json())
|
||||||
|
|
||||||
def test_json_complex(self):
|
def test_json_complex(self):
|
||||||
class EmbeddedDoc(EmbeddedDocument):
|
class EmbeddedDoc(EmbeddedDocument):
|
||||||
@@ -106,7 +99,7 @@ class TestJson(unittest.TestCase):
|
|||||||
return json.loads(self.to_json()) == json.loads(other.to_json())
|
return json.loads(self.to_json()) == json.loads(other.to_json())
|
||||||
|
|
||||||
doc = Doc()
|
doc = Doc()
|
||||||
self.assertEqual(doc, Doc.from_json(doc.to_json()))
|
assert doc == Doc.from_json(doc.to_json())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -2,25 +2,23 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
|
from tests.utils import MongoDBTestCase
|
||||||
__all__ = ("ValidatorErrorTest",)
|
|
||||||
|
|
||||||
|
|
||||||
class ValidatorErrorTest(unittest.TestCase):
|
class TestValidatorError(MongoDBTestCase):
|
||||||
def setUp(self):
|
|
||||||
connect(db="mongoenginetest")
|
|
||||||
|
|
||||||
def test_to_dict(self):
|
def test_to_dict(self):
|
||||||
"""Ensure a ValidationError handles error to_dict correctly.
|
"""Ensure a ValidationError handles error to_dict correctly.
|
||||||
"""
|
"""
|
||||||
error = ValidationError("root")
|
error = ValidationError("root")
|
||||||
self.assertEqual(error.to_dict(), {})
|
assert error.to_dict() == {}
|
||||||
|
|
||||||
# 1st level error schema
|
# 1st level error schema
|
||||||
error.errors = {"1st": ValidationError("bad 1st")}
|
error.errors = {"1st": ValidationError("bad 1st")}
|
||||||
self.assertIn("1st", error.to_dict())
|
assert "1st" in error.to_dict()
|
||||||
self.assertEqual(error.to_dict()["1st"], "bad 1st")
|
assert error.to_dict()["1st"] == "bad 1st"
|
||||||
|
|
||||||
# 2nd level error schema
|
# 2nd level error schema
|
||||||
error.errors = {
|
error.errors = {
|
||||||
@@ -28,10 +26,10 @@ class ValidatorErrorTest(unittest.TestCase):
|
|||||||
"bad 1st", errors={"2nd": ValidationError("bad 2nd")}
|
"bad 1st", errors={"2nd": ValidationError("bad 2nd")}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
self.assertIn("1st", error.to_dict())
|
assert "1st" in error.to_dict()
|
||||||
self.assertIsInstance(error.to_dict()["1st"], dict)
|
assert isinstance(error.to_dict()["1st"], dict)
|
||||||
self.assertIn("2nd", error.to_dict()["1st"])
|
assert "2nd" in error.to_dict()["1st"]
|
||||||
self.assertEqual(error.to_dict()["1st"]["2nd"], "bad 2nd")
|
assert error.to_dict()["1st"]["2nd"] == "bad 2nd"
|
||||||
|
|
||||||
# moar levels
|
# moar levels
|
||||||
error.errors = {
|
error.errors = {
|
||||||
@@ -49,13 +47,13 @@ class ValidatorErrorTest(unittest.TestCase):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
self.assertIn("1st", error.to_dict())
|
assert "1st" in error.to_dict()
|
||||||
self.assertIn("2nd", error.to_dict()["1st"])
|
assert "2nd" in error.to_dict()["1st"]
|
||||||
self.assertIn("3rd", error.to_dict()["1st"]["2nd"])
|
assert "3rd" in error.to_dict()["1st"]["2nd"]
|
||||||
self.assertIn("4th", error.to_dict()["1st"]["2nd"]["3rd"])
|
assert "4th" in error.to_dict()["1st"]["2nd"]["3rd"]
|
||||||
self.assertEqual(error.to_dict()["1st"]["2nd"]["3rd"]["4th"], "Inception")
|
assert error.to_dict()["1st"]["2nd"]["3rd"]["4th"] == "Inception"
|
||||||
|
|
||||||
self.assertEqual(error.message, "root(2nd.3rd.4th.Inception: ['1st'])")
|
assert error.message == "root(2nd.3rd.4th.Inception: ['1st'])"
|
||||||
|
|
||||||
def test_model_validation(self):
|
def test_model_validation(self):
|
||||||
class User(Document):
|
class User(Document):
|
||||||
@@ -65,19 +63,19 @@ class ValidatorErrorTest(unittest.TestCase):
|
|||||||
try:
|
try:
|
||||||
User().validate()
|
User().validate()
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertIn("User:None", e.message)
|
assert "User:None" in e.message
|
||||||
self.assertEqual(
|
assert e.to_dict() == {
|
||||||
e.to_dict(),
|
"username": "Field is required",
|
||||||
{"username": "Field is required", "name": "Field is required"},
|
"name": "Field is required",
|
||||||
)
|
}
|
||||||
|
|
||||||
user = User(username="RossC0", name="Ross").save()
|
user = User(username="RossC0", name="Ross").save()
|
||||||
user.name = None
|
user.name = None
|
||||||
try:
|
try:
|
||||||
user.save()
|
user.save()
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertIn("User:RossC0", e.message)
|
assert "User:RossC0" in e.message
|
||||||
self.assertEqual(e.to_dict(), {"name": "Field is required"})
|
assert e.to_dict() == {"name": "Field is required"}
|
||||||
|
|
||||||
def test_fields_rewrite(self):
|
def test_fields_rewrite(self):
|
||||||
class BasePerson(Document):
|
class BasePerson(Document):
|
||||||
@@ -89,7 +87,8 @@ class ValidatorErrorTest(unittest.TestCase):
|
|||||||
name = StringField(required=True)
|
name = StringField(required=True)
|
||||||
|
|
||||||
p = Person(age=15)
|
p = Person(age=15)
|
||||||
self.assertRaises(ValidationError, p.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
p.validate()
|
||||||
|
|
||||||
def test_embedded_document_validation(self):
|
def test_embedded_document_validation(self):
|
||||||
"""Ensure that embedded documents may be validated.
|
"""Ensure that embedded documents may be validated.
|
||||||
@@ -100,17 +99,19 @@ class ValidatorErrorTest(unittest.TestCase):
|
|||||||
content = StringField(required=True)
|
content = StringField(required=True)
|
||||||
|
|
||||||
comment = Comment()
|
comment = Comment()
|
||||||
self.assertRaises(ValidationError, comment.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
comment.validate()
|
||||||
|
|
||||||
comment.content = "test"
|
comment.content = "test"
|
||||||
comment.validate()
|
comment.validate()
|
||||||
|
|
||||||
comment.date = 4
|
comment.date = 4
|
||||||
self.assertRaises(ValidationError, comment.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
comment.validate()
|
||||||
|
|
||||||
comment.date = datetime.now()
|
comment.date = datetime.now()
|
||||||
comment.validate()
|
comment.validate()
|
||||||
self.assertEqual(comment._instance, None)
|
assert comment._instance is None
|
||||||
|
|
||||||
def test_embedded_db_field_validate(self):
|
def test_embedded_db_field_validate(self):
|
||||||
class SubDoc(EmbeddedDocument):
|
class SubDoc(EmbeddedDocument):
|
||||||
@@ -123,10 +124,8 @@ class ValidatorErrorTest(unittest.TestCase):
|
|||||||
try:
|
try:
|
||||||
Doc(id="bad").validate()
|
Doc(id="bad").validate()
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertIn("SubDoc:None", e.message)
|
assert "SubDoc:None" in e.message
|
||||||
self.assertEqual(
|
assert e.to_dict() == {"e": {"val": "OK could not be converted to int"}}
|
||||||
e.to_dict(), {"e": {"val": "OK could not be converted to int"}}
|
|
||||||
)
|
|
||||||
|
|
||||||
Doc.drop_collection()
|
Doc.drop_collection()
|
||||||
|
|
||||||
@@ -134,18 +133,16 @@ class ValidatorErrorTest(unittest.TestCase):
|
|||||||
|
|
||||||
doc = Doc.objects.first()
|
doc = Doc.objects.first()
|
||||||
keys = doc._data.keys()
|
keys = doc._data.keys()
|
||||||
self.assertEqual(2, len(keys))
|
assert 2 == len(keys)
|
||||||
self.assertIn("e", keys)
|
assert "e" in keys
|
||||||
self.assertIn("id", keys)
|
assert "id" in keys
|
||||||
|
|
||||||
doc.e.val = "OK"
|
doc.e.val = "OK"
|
||||||
try:
|
try:
|
||||||
doc.save()
|
doc.save()
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertIn("Doc:test", e.message)
|
assert "Doc:test" in e.message
|
||||||
self.assertEqual(
|
assert e.to_dict() == {"e": {"val": "OK could not be converted to int"}}
|
||||||
e.to_dict(), {"e": {"val": "OK could not be converted to int"}}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_embedded_weakref(self):
|
def test_embedded_weakref(self):
|
||||||
class SubDoc(EmbeddedDocument):
|
class SubDoc(EmbeddedDocument):
|
||||||
@@ -161,14 +158,16 @@ class ValidatorErrorTest(unittest.TestCase):
|
|||||||
|
|
||||||
s = SubDoc()
|
s = SubDoc()
|
||||||
|
|
||||||
self.assertRaises(ValidationError, s.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
s.validate()
|
||||||
|
|
||||||
d1.e = s
|
d1.e = s
|
||||||
d2.e = s
|
d2.e = s
|
||||||
|
|
||||||
del d1
|
del d1
|
||||||
|
|
||||||
self.assertRaises(ValidationError, d2.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
d2.validate()
|
||||||
|
|
||||||
def test_parent_reference_in_child_document(self):
|
def test_parent_reference_in_child_document(self):
|
||||||
"""
|
"""
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
from .fields import *
|
|
||||||
from .file_tests import *
|
|
||||||
from .geo import *
|
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from nose.plugins.skip import SkipTest
|
|
||||||
import six
|
|
||||||
|
|
||||||
from bson import Binary
|
from bson import Binary
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
BIN_VALUE = six.b(
|
BIN_VALUE = "\xa9\xf3\x8d(\xd7\x03\x84\xb4k[\x0f\xe3\xa2\x19\x85p[J\xa3\xd2>\xde\xe6\x87\xb1\x7f\xc6\xe6\xd9r\x18\xf5".encode(
|
||||||
"\xa9\xf3\x8d(\xd7\x03\x84\xb4k[\x0f\xe3\xa2\x19\x85p[J\xa3\xd2>\xde\xe6\x87\xb1\x7f\xc6\xe6\xd9r\x18\xf5"
|
"latin-1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +21,7 @@ class TestBinaryField(MongoDBTestCase):
|
|||||||
content_type = StringField()
|
content_type = StringField()
|
||||||
blob = BinaryField()
|
blob = BinaryField()
|
||||||
|
|
||||||
BLOB = six.b("\xe6\x00\xc4\xff\x07")
|
BLOB = "\xe6\x00\xc4\xff\x07".encode("latin-1")
|
||||||
MIME_TYPE = "application/octet-stream"
|
MIME_TYPE = "application/octet-stream"
|
||||||
|
|
||||||
Attachment.drop_collection()
|
Attachment.drop_collection()
|
||||||
@@ -32,8 +30,8 @@ class TestBinaryField(MongoDBTestCase):
|
|||||||
attachment.save()
|
attachment.save()
|
||||||
|
|
||||||
attachment_1 = Attachment.objects().first()
|
attachment_1 = Attachment.objects().first()
|
||||||
self.assertEqual(MIME_TYPE, attachment_1.content_type)
|
assert MIME_TYPE == attachment_1.content_type
|
||||||
self.assertEqual(BLOB, six.binary_type(attachment_1.blob))
|
assert BLOB == bytes(attachment_1.blob)
|
||||||
|
|
||||||
def test_validation_succeeds(self):
|
def test_validation_succeeds(self):
|
||||||
"""Ensure that valid values can be assigned to binary fields.
|
"""Ensure that valid values can be assigned to binary fields.
|
||||||
@@ -46,13 +44,15 @@ class TestBinaryField(MongoDBTestCase):
|
|||||||
blob = BinaryField(max_bytes=4)
|
blob = BinaryField(max_bytes=4)
|
||||||
|
|
||||||
attachment_required = AttachmentRequired()
|
attachment_required = AttachmentRequired()
|
||||||
self.assertRaises(ValidationError, attachment_required.validate)
|
with pytest.raises(ValidationError):
|
||||||
attachment_required.blob = Binary(six.b("\xe6\x00\xc4\xff\x07"))
|
attachment_required.validate()
|
||||||
|
attachment_required.blob = Binary("\xe6\x00\xc4\xff\x07".encode("latin-1"))
|
||||||
attachment_required.validate()
|
attachment_required.validate()
|
||||||
|
|
||||||
_5_BYTES = six.b("\xe6\x00\xc4\xff\x07")
|
_5_BYTES = "\xe6\x00\xc4\xff\x07".encode("latin-1")
|
||||||
_4_BYTES = six.b("\xe6\x00\xc4\xff")
|
_4_BYTES = "\xe6\x00\xc4\xff".encode("latin-1")
|
||||||
self.assertRaises(ValidationError, AttachmentSizeLimit(blob=_5_BYTES).validate)
|
with pytest.raises(ValidationError):
|
||||||
|
AttachmentSizeLimit(blob=_5_BYTES).validate()
|
||||||
AttachmentSizeLimit(blob=_4_BYTES).validate()
|
AttachmentSizeLimit(blob=_4_BYTES).validate()
|
||||||
|
|
||||||
def test_validation_fails(self):
|
def test_validation_fails(self):
|
||||||
@@ -62,7 +62,8 @@ class TestBinaryField(MongoDBTestCase):
|
|||||||
blob = BinaryField()
|
blob = BinaryField()
|
||||||
|
|
||||||
for invalid_data in (2, u"Im_a_unicode", ["some_str"]):
|
for invalid_data in (2, u"Im_a_unicode", ["some_str"]):
|
||||||
self.assertRaises(ValidationError, Attachment(blob=invalid_data).validate)
|
with pytest.raises(ValidationError):
|
||||||
|
Attachment(blob=invalid_data).validate()
|
||||||
|
|
||||||
def test__primary(self):
|
def test__primary(self):
|
||||||
class Attachment(Document):
|
class Attachment(Document):
|
||||||
@@ -71,23 +72,21 @@ class TestBinaryField(MongoDBTestCase):
|
|||||||
Attachment.drop_collection()
|
Attachment.drop_collection()
|
||||||
binary_id = uuid.uuid4().bytes
|
binary_id = uuid.uuid4().bytes
|
||||||
att = Attachment(id=binary_id).save()
|
att = Attachment(id=binary_id).save()
|
||||||
self.assertEqual(1, Attachment.objects.count())
|
assert 1 == Attachment.objects.count()
|
||||||
self.assertEqual(1, Attachment.objects.filter(id=att.id).count())
|
assert 1 == Attachment.objects.filter(id=att.id).count()
|
||||||
att.delete()
|
att.delete()
|
||||||
self.assertEqual(0, Attachment.objects.count())
|
assert 0 == Attachment.objects.count()
|
||||||
|
|
||||||
def test_primary_filter_by_binary_pk_as_str(self):
|
def test_primary_filter_by_binary_pk_as_str(self):
|
||||||
raise SkipTest("Querying by id as string is not currently supported")
|
|
||||||
|
|
||||||
class Attachment(Document):
|
class Attachment(Document):
|
||||||
id = BinaryField(primary_key=True)
|
id = BinaryField(primary_key=True)
|
||||||
|
|
||||||
Attachment.drop_collection()
|
Attachment.drop_collection()
|
||||||
binary_id = uuid.uuid4().bytes
|
binary_id = uuid.uuid4().bytes
|
||||||
att = Attachment(id=binary_id).save()
|
att = Attachment(id=binary_id).save()
|
||||||
self.assertEqual(1, Attachment.objects.filter(id=binary_id).count())
|
assert 1 == Attachment.objects.filter(id=binary_id).count()
|
||||||
att.delete()
|
att.delete()
|
||||||
self.assertEqual(0, Attachment.objects.count())
|
assert 0 == Attachment.objects.count()
|
||||||
|
|
||||||
def test_match_querying_with_bytes(self):
|
def test_match_querying_with_bytes(self):
|
||||||
class MyDocument(Document):
|
class MyDocument(Document):
|
||||||
@@ -97,7 +96,7 @@ class TestBinaryField(MongoDBTestCase):
|
|||||||
|
|
||||||
doc = MyDocument(bin_field=BIN_VALUE).save()
|
doc = MyDocument(bin_field=BIN_VALUE).save()
|
||||||
matched_doc = MyDocument.objects(bin_field=BIN_VALUE).first()
|
matched_doc = MyDocument.objects(bin_field=BIN_VALUE).first()
|
||||||
self.assertEqual(matched_doc.id, doc.id)
|
assert matched_doc.id == doc.id
|
||||||
|
|
||||||
def test_match_querying_with_binary(self):
|
def test_match_querying_with_binary(self):
|
||||||
class MyDocument(Document):
|
class MyDocument(Document):
|
||||||
@@ -108,7 +107,7 @@ class TestBinaryField(MongoDBTestCase):
|
|||||||
doc = MyDocument(bin_field=BIN_VALUE).save()
|
doc = MyDocument(bin_field=BIN_VALUE).save()
|
||||||
|
|
||||||
matched_doc = MyDocument.objects(bin_field=Binary(BIN_VALUE)).first()
|
matched_doc = MyDocument.objects(bin_field=Binary(BIN_VALUE)).first()
|
||||||
self.assertEqual(matched_doc.id, doc.id)
|
assert matched_doc.id == doc.id
|
||||||
|
|
||||||
def test_modify_operation__set(self):
|
def test_modify_operation__set(self):
|
||||||
"""Ensures no regression of bug #1127"""
|
"""Ensures no regression of bug #1127"""
|
||||||
@@ -122,11 +121,8 @@ class TestBinaryField(MongoDBTestCase):
|
|||||||
doc = MyDocument.objects(some_field="test").modify(
|
doc = MyDocument.objects(some_field="test").modify(
|
||||||
upsert=True, new=True, set__bin_field=BIN_VALUE
|
upsert=True, new=True, set__bin_field=BIN_VALUE
|
||||||
)
|
)
|
||||||
self.assertEqual(doc.some_field, "test")
|
assert doc.some_field == "test"
|
||||||
if six.PY3:
|
assert doc.bin_field == BIN_VALUE
|
||||||
self.assertEqual(doc.bin_field, BIN_VALUE)
|
|
||||||
else:
|
|
||||||
self.assertEqual(doc.bin_field, Binary(BIN_VALUE))
|
|
||||||
|
|
||||||
def test_update_one(self):
|
def test_update_one(self):
|
||||||
"""Ensures no regression of bug #1127"""
|
"""Ensures no regression of bug #1127"""
|
||||||
@@ -136,15 +132,12 @@ class TestBinaryField(MongoDBTestCase):
|
|||||||
|
|
||||||
MyDocument.drop_collection()
|
MyDocument.drop_collection()
|
||||||
|
|
||||||
bin_data = six.b("\xe6\x00\xc4\xff\x07")
|
bin_data = "\xe6\x00\xc4\xff\x07".encode("latin-1")
|
||||||
doc = MyDocument(bin_field=bin_data).save()
|
doc = MyDocument(bin_field=bin_data).save()
|
||||||
|
|
||||||
n_updated = MyDocument.objects(bin_field=bin_data).update_one(
|
n_updated = MyDocument.objects(bin_field=bin_data).update_one(
|
||||||
bin_field=BIN_VALUE
|
bin_field=BIN_VALUE
|
||||||
)
|
)
|
||||||
self.assertEqual(n_updated, 1)
|
assert n_updated == 1
|
||||||
fetched = MyDocument.objects.with_id(doc.id)
|
fetched = MyDocument.objects.with_id(doc.id)
|
||||||
if six.PY3:
|
assert fetched.bin_field == BIN_VALUE
|
||||||
self.assertEqual(fetched.bin_field, BIN_VALUE)
|
|
||||||
else:
|
|
||||||
self.assertEqual(fetched.bin_field, Binary(BIN_VALUE))
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from mongoengine import *
|
import pytest
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
from tests.utils import MongoDBTestCase, get_as_pymongo
|
from tests.utils import MongoDBTestCase, get_as_pymongo
|
||||||
|
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ class TestBooleanField(MongoDBTestCase):
|
|||||||
|
|
||||||
person = Person(admin=True)
|
person = Person(admin=True)
|
||||||
person.save()
|
person.save()
|
||||||
self.assertEqual(get_as_pymongo(person), {"_id": person.id, "admin": True})
|
assert get_as_pymongo(person) == {"_id": person.id, "admin": True}
|
||||||
|
|
||||||
def test_validation(self):
|
def test_validation(self):
|
||||||
"""Ensure that invalid values cannot be assigned to boolean
|
"""Ensure that invalid values cannot be assigned to boolean
|
||||||
@@ -26,11 +27,14 @@ class TestBooleanField(MongoDBTestCase):
|
|||||||
person.validate()
|
person.validate()
|
||||||
|
|
||||||
person.admin = 2
|
person.admin = 2
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
person.admin = "Yes"
|
person.admin = "Yes"
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
person.admin = "False"
|
person.admin = "False"
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
def test_weirdness_constructor(self):
|
def test_weirdness_constructor(self):
|
||||||
"""When attribute is set in contructor, it gets cast into a bool
|
"""When attribute is set in contructor, it gets cast into a bool
|
||||||
@@ -42,7 +46,7 @@ class TestBooleanField(MongoDBTestCase):
|
|||||||
admin = BooleanField()
|
admin = BooleanField()
|
||||||
|
|
||||||
new_person = Person(admin="False")
|
new_person = Person(admin="False")
|
||||||
self.assertTrue(new_person.admin)
|
assert new_person.admin
|
||||||
|
|
||||||
new_person = Person(admin="0")
|
new_person = Person(admin="0")
|
||||||
self.assertTrue(new_person.admin)
|
assert new_person.admin
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from mongoengine import *
|
import pytest
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -46,29 +47,29 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
a = Animal(name="Leopard", tag="heavy")
|
a = Animal(name="Leopard", tag="heavy")
|
||||||
a.save()
|
a.save()
|
||||||
|
|
||||||
self.assertEqual(Animal._cached_reference_fields, [Ocorrence.animal])
|
assert Animal._cached_reference_fields == [Ocorrence.animal]
|
||||||
o = Ocorrence(person="teste", animal=a)
|
o = Ocorrence(person="teste", animal=a)
|
||||||
o.save()
|
o.save()
|
||||||
|
|
||||||
p = Ocorrence(person="Wilson")
|
p = Ocorrence(person="Wilson")
|
||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
self.assertEqual(Ocorrence.objects(animal=None).count(), 1)
|
assert Ocorrence.objects(animal=None).count() == 1
|
||||||
|
|
||||||
self.assertEqual(a.to_mongo(fields=["tag"]), {"tag": "heavy", "_id": a.pk})
|
assert a.to_mongo(fields=["tag"]) == {"tag": "heavy", "_id": a.pk}
|
||||||
|
|
||||||
self.assertEqual(o.to_mongo()["animal"]["tag"], "heavy")
|
assert o.to_mongo()["animal"]["tag"] == "heavy"
|
||||||
|
|
||||||
# counts
|
# counts
|
||||||
Ocorrence(person="teste 2").save()
|
Ocorrence(person="teste 2").save()
|
||||||
Ocorrence(person="teste 3").save()
|
Ocorrence(person="teste 3").save()
|
||||||
|
|
||||||
count = Ocorrence.objects(animal__tag="heavy").count()
|
count = Ocorrence.objects(animal__tag="heavy").count()
|
||||||
self.assertEqual(count, 1)
|
assert count == 1
|
||||||
|
|
||||||
ocorrence = Ocorrence.objects(animal__tag="heavy").first()
|
ocorrence = Ocorrence.objects(animal__tag="heavy").first()
|
||||||
self.assertEqual(ocorrence.person, "teste")
|
assert ocorrence.person == "teste"
|
||||||
self.assertIsInstance(ocorrence.animal, Animal)
|
assert isinstance(ocorrence.animal, Animal)
|
||||||
|
|
||||||
def test_with_decimal(self):
|
def test_with_decimal(self):
|
||||||
class PersonAuto(Document):
|
class PersonAuto(Document):
|
||||||
@@ -88,10 +89,11 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
s = SocialTest(group="dev", person=p)
|
s = SocialTest(group="dev", person=p)
|
||||||
s.save()
|
s.save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert SocialTest.objects._collection.find_one({"person.salary": 7000.00}) == {
|
||||||
SocialTest.objects._collection.find_one({"person.salary": 7000.00}),
|
"_id": s.pk,
|
||||||
{"_id": s.pk, "group": s.group, "person": {"_id": p.pk, "salary": 7000.00}},
|
"group": s.group,
|
||||||
)
|
"person": {"_id": p.pk, "salary": 7000.00},
|
||||||
|
}
|
||||||
|
|
||||||
def test_cached_reference_field_reference(self):
|
def test_cached_reference_field_reference(self):
|
||||||
class Group(Document):
|
class Group(Document):
|
||||||
@@ -131,18 +133,15 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
s2 = SocialData(obs="testing 321", person=p3, tags=["tag3", "tag4"])
|
s2 = SocialData(obs="testing 321", person=p3, tags=["tag3", "tag4"])
|
||||||
s2.save()
|
s2.save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert SocialData.objects._collection.find_one({"tags": "tag2"}) == {
|
||||||
SocialData.objects._collection.find_one({"tags": "tag2"}),
|
"_id": s1.pk,
|
||||||
{
|
"obs": "testing 123",
|
||||||
"_id": s1.pk,
|
"tags": ["tag1", "tag2"],
|
||||||
"obs": "testing 123",
|
"person": {"_id": p1.pk, "group": g1.pk},
|
||||||
"tags": ["tag1", "tag2"],
|
}
|
||||||
"person": {"_id": p1.pk, "group": g1.pk},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(SocialData.objects(person__group=g2).count(), 1)
|
assert SocialData.objects(person__group=g2).count() == 1
|
||||||
self.assertEqual(SocialData.objects(person__group=g2).first(), s2)
|
assert SocialData.objects(person__group=g2).first() == s2
|
||||||
|
|
||||||
def test_cached_reference_field_push_with_fields(self):
|
def test_cached_reference_field_push_with_fields(self):
|
||||||
class Product(Document):
|
class Product(Document):
|
||||||
@@ -157,26 +156,20 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
product1 = Product(name="abc").save()
|
product1 = Product(name="abc").save()
|
||||||
product2 = Product(name="def").save()
|
product2 = Product(name="def").save()
|
||||||
basket = Basket(products=[product1]).save()
|
basket = Basket(products=[product1]).save()
|
||||||
self.assertEqual(
|
assert Basket.objects._collection.find_one() == {
|
||||||
Basket.objects._collection.find_one(),
|
"_id": basket.pk,
|
||||||
{
|
"products": [{"_id": product1.pk, "name": product1.name}],
|
||||||
"_id": basket.pk,
|
}
|
||||||
"products": [{"_id": product1.pk, "name": product1.name}],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
# push to list
|
# push to list
|
||||||
basket.update(push__products=product2)
|
basket.update(push__products=product2)
|
||||||
basket.reload()
|
basket.reload()
|
||||||
self.assertEqual(
|
assert Basket.objects._collection.find_one() == {
|
||||||
Basket.objects._collection.find_one(),
|
"_id": basket.pk,
|
||||||
{
|
"products": [
|
||||||
"_id": basket.pk,
|
{"_id": product1.pk, "name": product1.name},
|
||||||
"products": [
|
{"_id": product2.pk, "name": product2.name},
|
||||||
{"_id": product1.pk, "name": product1.name},
|
],
|
||||||
{"_id": product2.pk, "name": product2.name},
|
}
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_cached_reference_field_update_all(self):
|
def test_cached_reference_field_update_all(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -194,37 +187,31 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
a2.save()
|
a2.save()
|
||||||
|
|
||||||
a2 = Person.objects.with_id(a2.id)
|
a2 = Person.objects.with_id(a2.id)
|
||||||
self.assertEqual(a2.father.tp, a1.tp)
|
assert a2.father.tp == a1.tp
|
||||||
|
|
||||||
self.assertEqual(
|
assert dict(a2.to_mongo()) == {
|
||||||
dict(a2.to_mongo()),
|
"_id": a2.pk,
|
||||||
{
|
"name": u"Wilson Junior",
|
||||||
"_id": a2.pk,
|
"tp": u"pf",
|
||||||
"name": u"Wilson Junior",
|
"father": {"_id": a1.pk, "tp": u"pj"},
|
||||||
"tp": u"pf",
|
}
|
||||||
"father": {"_id": a1.pk, "tp": u"pj"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(Person.objects(father=a1)._query, {"father._id": a1.pk})
|
assert Person.objects(father=a1)._query == {"father._id": a1.pk}
|
||||||
self.assertEqual(Person.objects(father=a1).count(), 1)
|
assert Person.objects(father=a1).count() == 1
|
||||||
|
|
||||||
Person.objects.update(set__tp="pf")
|
Person.objects.update(set__tp="pf")
|
||||||
Person.father.sync_all()
|
Person.father.sync_all()
|
||||||
|
|
||||||
a2.reload()
|
a2.reload()
|
||||||
self.assertEqual(
|
assert dict(a2.to_mongo()) == {
|
||||||
dict(a2.to_mongo()),
|
"_id": a2.pk,
|
||||||
{
|
"name": u"Wilson Junior",
|
||||||
"_id": a2.pk,
|
"tp": u"pf",
|
||||||
"name": u"Wilson Junior",
|
"father": {"_id": a1.pk, "tp": u"pf"},
|
||||||
"tp": u"pf",
|
}
|
||||||
"father": {"_id": a1.pk, "tp": u"pf"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_cached_reference_fields_on_embedded_documents(self):
|
def test_cached_reference_fields_on_embedded_documents(self):
|
||||||
with self.assertRaises(InvalidDocumentError):
|
with pytest.raises(InvalidDocumentError):
|
||||||
|
|
||||||
class Test(Document):
|
class Test(Document):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
@@ -255,15 +242,12 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
a1.save()
|
a1.save()
|
||||||
|
|
||||||
a2.reload()
|
a2.reload()
|
||||||
self.assertEqual(
|
assert dict(a2.to_mongo()) == {
|
||||||
dict(a2.to_mongo()),
|
"_id": a2.pk,
|
||||||
{
|
"name": "Wilson Junior",
|
||||||
"_id": a2.pk,
|
"tp": "pf",
|
||||||
"name": "Wilson Junior",
|
"father": {"_id": a1.pk, "tp": "pf"},
|
||||||
"tp": "pf",
|
}
|
||||||
"father": {"_id": a1.pk, "tp": "pf"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_cached_reference_auto_sync_disabled(self):
|
def test_cached_reference_auto_sync_disabled(self):
|
||||||
class Persone(Document):
|
class Persone(Document):
|
||||||
@@ -284,15 +268,12 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
a1.tp = "pf"
|
a1.tp = "pf"
|
||||||
a1.save()
|
a1.save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert Persone.objects._collection.find_one({"_id": a2.pk}) == {
|
||||||
Persone.objects._collection.find_one({"_id": a2.pk}),
|
"_id": a2.pk,
|
||||||
{
|
"name": "Wilson Junior",
|
||||||
"_id": a2.pk,
|
"tp": "pf",
|
||||||
"name": "Wilson Junior",
|
"father": {"_id": a1.pk, "tp": "pj"},
|
||||||
"tp": "pf",
|
}
|
||||||
"father": {"_id": a1.pk, "tp": "pj"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_cached_reference_embedded_fields(self):
|
def test_cached_reference_embedded_fields(self):
|
||||||
class Owner(EmbeddedDocument):
|
class Owner(EmbeddedDocument):
|
||||||
@@ -320,28 +301,29 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
o = Ocorrence(person="teste", animal=a)
|
o = Ocorrence(person="teste", animal=a)
|
||||||
o.save()
|
o.save()
|
||||||
self.assertEqual(
|
assert dict(a.to_mongo(fields=["tag", "owner.tp"])) == {
|
||||||
dict(a.to_mongo(fields=["tag", "owner.tp"])),
|
"_id": a.pk,
|
||||||
{"_id": a.pk, "tag": "heavy", "owner": {"t": "u"}},
|
"tag": "heavy",
|
||||||
)
|
"owner": {"t": "u"},
|
||||||
self.assertEqual(o.to_mongo()["animal"]["tag"], "heavy")
|
}
|
||||||
self.assertEqual(o.to_mongo()["animal"]["owner"]["t"], "u")
|
assert o.to_mongo()["animal"]["tag"] == "heavy"
|
||||||
|
assert o.to_mongo()["animal"]["owner"]["t"] == "u"
|
||||||
|
|
||||||
# Check to_mongo with fields
|
# Check to_mongo with fields
|
||||||
self.assertNotIn("animal", o.to_mongo(fields=["person"]))
|
assert "animal" not in o.to_mongo(fields=["person"])
|
||||||
|
|
||||||
# counts
|
# counts
|
||||||
Ocorrence(person="teste 2").save()
|
Ocorrence(person="teste 2").save()
|
||||||
Ocorrence(person="teste 3").save()
|
Ocorrence(person="teste 3").save()
|
||||||
|
|
||||||
count = Ocorrence.objects(animal__tag="heavy", animal__owner__tp="u").count()
|
count = Ocorrence.objects(animal__tag="heavy", animal__owner__tp="u").count()
|
||||||
self.assertEqual(count, 1)
|
assert count == 1
|
||||||
|
|
||||||
ocorrence = Ocorrence.objects(
|
ocorrence = Ocorrence.objects(
|
||||||
animal__tag="heavy", animal__owner__tp="u"
|
animal__tag="heavy", animal__owner__tp="u"
|
||||||
).first()
|
).first()
|
||||||
self.assertEqual(ocorrence.person, "teste")
|
assert ocorrence.person == "teste"
|
||||||
self.assertIsInstance(ocorrence.animal, Animal)
|
assert isinstance(ocorrence.animal, Animal)
|
||||||
|
|
||||||
def test_cached_reference_embedded_list_fields(self):
|
def test_cached_reference_embedded_list_fields(self):
|
||||||
class Owner(EmbeddedDocument):
|
class Owner(EmbeddedDocument):
|
||||||
@@ -370,13 +352,14 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
o = Ocorrence(person="teste 2", animal=a)
|
o = Ocorrence(person="teste 2", animal=a)
|
||||||
o.save()
|
o.save()
|
||||||
self.assertEqual(
|
assert dict(a.to_mongo(fields=["tag", "owner.tags"])) == {
|
||||||
dict(a.to_mongo(fields=["tag", "owner.tags"])),
|
"_id": a.pk,
|
||||||
{"_id": a.pk, "tag": "heavy", "owner": {"tags": ["cool", "funny"]}},
|
"tag": "heavy",
|
||||||
)
|
"owner": {"tags": ["cool", "funny"]},
|
||||||
|
}
|
||||||
|
|
||||||
self.assertEqual(o.to_mongo()["animal"]["tag"], "heavy")
|
assert o.to_mongo()["animal"]["tag"] == "heavy"
|
||||||
self.assertEqual(o.to_mongo()["animal"]["owner"]["tags"], ["cool", "funny"])
|
assert o.to_mongo()["animal"]["owner"]["tags"] == ["cool", "funny"]
|
||||||
|
|
||||||
# counts
|
# counts
|
||||||
Ocorrence(person="teste 2").save()
|
Ocorrence(person="teste 2").save()
|
||||||
@@ -385,10 +368,10 @@ class TestCachedReferenceField(MongoDBTestCase):
|
|||||||
query = Ocorrence.objects(
|
query = Ocorrence.objects(
|
||||||
animal__tag="heavy", animal__owner__tags="cool"
|
animal__tag="heavy", animal__owner__tags="cool"
|
||||||
)._query
|
)._query
|
||||||
self.assertEqual(query, {"animal.owner.tags": "cool", "animal.tag": "heavy"})
|
assert query == {"animal.owner.tags": "cool", "animal.tag": "heavy"}
|
||||||
|
|
||||||
ocorrence = Ocorrence.objects(
|
ocorrence = Ocorrence.objects(
|
||||||
animal__tag="heavy", animal__owner__tags="cool"
|
animal__tag="heavy", animal__owner__tags="cool"
|
||||||
).first()
|
).first()
|
||||||
self.assertEqual(ocorrence.person, "teste 2")
|
assert ocorrence.person == "teste 2"
|
||||||
self.assertIsInstance(ocorrence.animal, Animal)
|
assert isinstance(ocorrence.animal, Animal)
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import datetime
|
import datetime
|
||||||
import math
|
|
||||||
import itertools
|
import itertools
|
||||||
|
import math
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
|
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
@@ -28,7 +30,7 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
log.date = d1
|
log.date = d1
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertEqual(log.date, d1)
|
assert log.date == d1
|
||||||
|
|
||||||
# Post UTC - microseconds are rounded (down) nearest millisecond - with
|
# Post UTC - microseconds are rounded (down) nearest millisecond - with
|
||||||
# default datetimefields
|
# default datetimefields
|
||||||
@@ -36,7 +38,7 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
log.date = d1
|
log.date = d1
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertEqual(log.date, d1)
|
assert log.date == d1
|
||||||
|
|
||||||
# Pre UTC dates microseconds below 1000 are dropped - with default
|
# Pre UTC dates microseconds below 1000 are dropped - with default
|
||||||
# datetimefields
|
# datetimefields
|
||||||
@@ -44,7 +46,7 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
log.date = d1
|
log.date = d1
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertEqual(log.date, d1)
|
assert log.date == d1
|
||||||
|
|
||||||
# Pre UTC microseconds above 1000 is wonky - with default datetimefields
|
# Pre UTC microseconds above 1000 is wonky - with default datetimefields
|
||||||
# log.date has an invalid microsecond value so I can't construct
|
# log.date has an invalid microsecond value so I can't construct
|
||||||
@@ -54,9 +56,9 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
log.date = d1
|
log.date = d1
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertEqual(log.date, d1)
|
assert log.date == d1
|
||||||
log1 = LogEntry.objects.get(date=d1)
|
log1 = LogEntry.objects.get(date=d1)
|
||||||
self.assertEqual(log, log1)
|
assert log == log1
|
||||||
|
|
||||||
# Test string padding
|
# Test string padding
|
||||||
microsecond = map(int, [math.pow(10, x) for x in range(6)])
|
microsecond = map(int, [math.pow(10, x) for x in range(6)])
|
||||||
@@ -64,8 +66,8 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
|
|
||||||
for values in itertools.product([2014], mm, dd, hh, ii, ss, microsecond):
|
for values in itertools.product([2014], mm, dd, hh, ii, ss, microsecond):
|
||||||
stored = LogEntry(date=datetime.datetime(*values)).to_mongo()["date"]
|
stored = LogEntry(date=datetime.datetime(*values)).to_mongo()["date"]
|
||||||
self.assertTrue(
|
assert (
|
||||||
re.match("^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$", stored)
|
re.match(r"^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$", stored)
|
||||||
is not None
|
is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -73,8 +75,8 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
stored = LogEntry(date_with_dots=datetime.datetime(2014, 1, 1)).to_mongo()[
|
stored = LogEntry(date_with_dots=datetime.datetime(2014, 1, 1)).to_mongo()[
|
||||||
"date_with_dots"
|
"date_with_dots"
|
||||||
]
|
]
|
||||||
self.assertTrue(
|
assert (
|
||||||
re.match("^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$", stored) is not None
|
re.match(r"^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$", stored) is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_complexdatetime_usage(self):
|
def test_complexdatetime_usage(self):
|
||||||
@@ -93,40 +95,40 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
log.save()
|
log.save()
|
||||||
|
|
||||||
log1 = LogEntry.objects.get(date=d1)
|
log1 = LogEntry.objects.get(date=d1)
|
||||||
self.assertEqual(log, log1)
|
assert log == log1
|
||||||
|
|
||||||
# create extra 59 log entries for a total of 60
|
# create extra 59 log entries for a total of 60
|
||||||
for i in range(1951, 2010):
|
for i in range(1951, 2010):
|
||||||
d = datetime.datetime(i, 1, 1, 0, 0, 1, 999)
|
d = datetime.datetime(i, 1, 1, 0, 0, 1, 999)
|
||||||
LogEntry(date=d).save()
|
LogEntry(date=d).save()
|
||||||
|
|
||||||
self.assertEqual(LogEntry.objects.count(), 60)
|
assert LogEntry.objects.count() == 60
|
||||||
|
|
||||||
# Test ordering
|
# Test ordering
|
||||||
logs = LogEntry.objects.order_by("date")
|
logs = LogEntry.objects.order_by("date")
|
||||||
i = 0
|
i = 0
|
||||||
while i < 59:
|
while i < 59:
|
||||||
self.assertTrue(logs[i].date <= logs[i + 1].date)
|
assert logs[i].date <= logs[i + 1].date
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
logs = LogEntry.objects.order_by("-date")
|
logs = LogEntry.objects.order_by("-date")
|
||||||
i = 0
|
i = 0
|
||||||
while i < 59:
|
while i < 59:
|
||||||
self.assertTrue(logs[i].date >= logs[i + 1].date)
|
assert logs[i].date >= logs[i + 1].date
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# Test searching
|
# Test searching
|
||||||
logs = LogEntry.objects.filter(date__gte=datetime.datetime(1980, 1, 1))
|
logs = LogEntry.objects.filter(date__gte=datetime.datetime(1980, 1, 1))
|
||||||
self.assertEqual(logs.count(), 30)
|
assert logs.count() == 30
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(date__lte=datetime.datetime(1980, 1, 1))
|
logs = LogEntry.objects.filter(date__lte=datetime.datetime(1980, 1, 1))
|
||||||
self.assertEqual(logs.count(), 30)
|
assert logs.count() == 30
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(
|
logs = LogEntry.objects.filter(
|
||||||
date__lte=datetime.datetime(2011, 1, 1),
|
date__lte=datetime.datetime(2011, 1, 1),
|
||||||
date__gte=datetime.datetime(2000, 1, 1),
|
date__gte=datetime.datetime(2000, 1, 1),
|
||||||
)
|
)
|
||||||
self.assertEqual(logs.count(), 10)
|
assert logs.count() == 10
|
||||||
|
|
||||||
LogEntry.drop_collection()
|
LogEntry.drop_collection()
|
||||||
|
|
||||||
@@ -137,17 +139,17 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
logs = list(LogEntry.objects.order_by("date"))
|
logs = list(LogEntry.objects.order_by("date"))
|
||||||
for next_idx, log in enumerate(logs[:-1], start=1):
|
for next_idx, log in enumerate(logs[:-1], start=1):
|
||||||
next_log = logs[next_idx]
|
next_log = logs[next_idx]
|
||||||
self.assertTrue(log.date < next_log.date)
|
assert log.date < next_log.date
|
||||||
|
|
||||||
logs = list(LogEntry.objects.order_by("-date"))
|
logs = list(LogEntry.objects.order_by("-date"))
|
||||||
for next_idx, log in enumerate(logs[:-1], start=1):
|
for next_idx, log in enumerate(logs[:-1], start=1):
|
||||||
next_log = logs[next_idx]
|
next_log = logs[next_idx]
|
||||||
self.assertTrue(log.date > next_log.date)
|
assert log.date > next_log.date
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(
|
logs = LogEntry.objects.filter(
|
||||||
date__lte=datetime.datetime(2015, 1, 1, 0, 0, 0, 10000)
|
date__lte=datetime.datetime(2015, 1, 1, 0, 0, 0, 10000)
|
||||||
)
|
)
|
||||||
self.assertEqual(logs.count(), 4)
|
assert logs.count() == 4
|
||||||
|
|
||||||
def test_no_default_value(self):
|
def test_no_default_value(self):
|
||||||
class Log(Document):
|
class Log(Document):
|
||||||
@@ -156,11 +158,11 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
Log.drop_collection()
|
Log.drop_collection()
|
||||||
|
|
||||||
log = Log()
|
log = Log()
|
||||||
self.assertIsNone(log.timestamp)
|
assert log.timestamp is None
|
||||||
log.save()
|
log.save()
|
||||||
|
|
||||||
fetched_log = Log.objects.with_id(log.id)
|
fetched_log = Log.objects.with_id(log.id)
|
||||||
self.assertIsNone(fetched_log.timestamp)
|
assert fetched_log.timestamp is None
|
||||||
|
|
||||||
def test_default_static_value(self):
|
def test_default_static_value(self):
|
||||||
NOW = datetime.datetime.utcnow()
|
NOW = datetime.datetime.utcnow()
|
||||||
@@ -171,11 +173,11 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
Log.drop_collection()
|
Log.drop_collection()
|
||||||
|
|
||||||
log = Log()
|
log = Log()
|
||||||
self.assertEqual(log.timestamp, NOW)
|
assert log.timestamp == NOW
|
||||||
log.save()
|
log.save()
|
||||||
|
|
||||||
fetched_log = Log.objects.with_id(log.id)
|
fetched_log = Log.objects.with_id(log.id)
|
||||||
self.assertEqual(fetched_log.timestamp, NOW)
|
assert fetched_log.timestamp == NOW
|
||||||
|
|
||||||
def test_default_callable(self):
|
def test_default_callable(self):
|
||||||
NOW = datetime.datetime.utcnow()
|
NOW = datetime.datetime.utcnow()
|
||||||
@@ -186,8 +188,23 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
Log.drop_collection()
|
Log.drop_collection()
|
||||||
|
|
||||||
log = Log()
|
log = Log()
|
||||||
self.assertGreaterEqual(log.timestamp, NOW)
|
assert log.timestamp >= NOW
|
||||||
log.save()
|
log.save()
|
||||||
|
|
||||||
fetched_log = Log.objects.with_id(log.id)
|
fetched_log = Log.objects.with_id(log.id)
|
||||||
self.assertGreaterEqual(fetched_log.timestamp, NOW)
|
assert fetched_log.timestamp >= NOW
|
||||||
|
|
||||||
|
def test_setting_bad_value_does_not_raise_unless_validate_is_called(self):
|
||||||
|
# test regression of #2253
|
||||||
|
|
||||||
|
class Log(Document):
|
||||||
|
timestamp = ComplexDateTimeField()
|
||||||
|
|
||||||
|
Log.drop_collection()
|
||||||
|
|
||||||
|
log = Log(timestamp="garbage")
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
log.validate()
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
log.save()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import datetime
|
import datetime
|
||||||
import six
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import dateutil
|
import dateutil
|
||||||
@@ -8,7 +9,6 @@ except ImportError:
|
|||||||
dateutil = None
|
dateutil = None
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
|
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +23,8 @@ class TestDateField(MongoDBTestCase):
|
|||||||
dt = DateField()
|
dt = DateField()
|
||||||
|
|
||||||
md = MyDoc(dt="")
|
md = MyDoc(dt="")
|
||||||
self.assertRaises(ValidationError, md.save)
|
with pytest.raises(ValidationError):
|
||||||
|
md.save()
|
||||||
|
|
||||||
def test_date_from_whitespace_string(self):
|
def test_date_from_whitespace_string(self):
|
||||||
"""
|
"""
|
||||||
@@ -35,7 +36,8 @@ class TestDateField(MongoDBTestCase):
|
|||||||
dt = DateField()
|
dt = DateField()
|
||||||
|
|
||||||
md = MyDoc(dt=" ")
|
md = MyDoc(dt=" ")
|
||||||
self.assertRaises(ValidationError, md.save)
|
with pytest.raises(ValidationError):
|
||||||
|
md.save()
|
||||||
|
|
||||||
def test_default_values_today(self):
|
def test_default_values_today(self):
|
||||||
"""Ensure that default field values are used when creating
|
"""Ensure that default field values are used when creating
|
||||||
@@ -47,9 +49,9 @@ class TestDateField(MongoDBTestCase):
|
|||||||
|
|
||||||
person = Person()
|
person = Person()
|
||||||
person.validate()
|
person.validate()
|
||||||
self.assertEqual(person.day, person.day)
|
assert person.day == person.day
|
||||||
self.assertEqual(person.day, datetime.date.today())
|
assert person.day == datetime.date.today()
|
||||||
self.assertEqual(person._data["day"], person.day)
|
assert person._data["day"] == person.day
|
||||||
|
|
||||||
def test_date(self):
|
def test_date(self):
|
||||||
"""Tests showing pymongo date fields
|
"""Tests showing pymongo date fields
|
||||||
@@ -67,7 +69,7 @@ class TestDateField(MongoDBTestCase):
|
|||||||
log.date = datetime.date.today()
|
log.date = datetime.date.today()
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertEqual(log.date, datetime.date.today())
|
assert log.date == datetime.date.today()
|
||||||
|
|
||||||
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 999)
|
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 999)
|
||||||
d2 = datetime.datetime(1970, 1, 1, 0, 0, 1)
|
d2 = datetime.datetime(1970, 1, 1, 0, 0, 1)
|
||||||
@@ -75,27 +77,16 @@ class TestDateField(MongoDBTestCase):
|
|||||||
log.date = d1
|
log.date = d1
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertEqual(log.date, d1.date())
|
assert log.date == d1.date()
|
||||||
self.assertEqual(log.date, d2.date())
|
assert log.date == d2.date()
|
||||||
|
|
||||||
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 9999)
|
d1 = datetime.datetime(1970, 1, 1, 0, 0, 1, 9999)
|
||||||
d2 = datetime.datetime(1970, 1, 1, 0, 0, 1, 9000)
|
d2 = datetime.datetime(1970, 1, 1, 0, 0, 1, 9000)
|
||||||
log.date = d1
|
log.date = d1
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertEqual(log.date, d1.date())
|
assert log.date == d1.date()
|
||||||
self.assertEqual(log.date, d2.date())
|
assert log.date == d2.date()
|
||||||
|
|
||||||
if not six.PY3:
|
|
||||||
# Pre UTC dates microseconds below 1000 are dropped
|
|
||||||
# This does not seem to be true in PY3
|
|
||||||
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, 999)
|
|
||||||
d2 = datetime.datetime(1969, 12, 31, 23, 59, 59)
|
|
||||||
log.date = d1
|
|
||||||
log.save()
|
|
||||||
log.reload()
|
|
||||||
self.assertEqual(log.date, d1.date())
|
|
||||||
self.assertEqual(log.date, d2.date())
|
|
||||||
|
|
||||||
def test_regular_usage(self):
|
def test_regular_usage(self):
|
||||||
"""Tests for regular datetime fields"""
|
"""Tests for regular datetime fields"""
|
||||||
@@ -113,35 +104,35 @@ class TestDateField(MongoDBTestCase):
|
|||||||
|
|
||||||
for query in (d1, d1.isoformat(" ")):
|
for query in (d1, d1.isoformat(" ")):
|
||||||
log1 = LogEntry.objects.get(date=query)
|
log1 = LogEntry.objects.get(date=query)
|
||||||
self.assertEqual(log, log1)
|
assert log == log1
|
||||||
|
|
||||||
if dateutil:
|
if dateutil:
|
||||||
log1 = LogEntry.objects.get(date=d1.isoformat("T"))
|
log1 = LogEntry.objects.get(date=d1.isoformat("T"))
|
||||||
self.assertEqual(log, log1)
|
assert log == log1
|
||||||
|
|
||||||
# create additional 19 log entries for a total of 20
|
# create additional 19 log entries for a total of 20
|
||||||
for i in range(1971, 1990):
|
for i in range(1971, 1990):
|
||||||
d = datetime.datetime(i, 1, 1, 0, 0, 1)
|
d = datetime.datetime(i, 1, 1, 0, 0, 1)
|
||||||
LogEntry(date=d).save()
|
LogEntry(date=d).save()
|
||||||
|
|
||||||
self.assertEqual(LogEntry.objects.count(), 20)
|
assert LogEntry.objects.count() == 20
|
||||||
|
|
||||||
# Test ordering
|
# Test ordering
|
||||||
logs = LogEntry.objects.order_by("date")
|
logs = LogEntry.objects.order_by("date")
|
||||||
i = 0
|
i = 0
|
||||||
while i < 19:
|
while i < 19:
|
||||||
self.assertTrue(logs[i].date <= logs[i + 1].date)
|
assert logs[i].date <= logs[i + 1].date
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
logs = LogEntry.objects.order_by("-date")
|
logs = LogEntry.objects.order_by("-date")
|
||||||
i = 0
|
i = 0
|
||||||
while i < 19:
|
while i < 19:
|
||||||
self.assertTrue(logs[i].date >= logs[i + 1].date)
|
assert logs[i].date >= logs[i + 1].date
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# Test searching
|
# Test searching
|
||||||
logs = LogEntry.objects.filter(date__gte=datetime.datetime(1980, 1, 1))
|
logs = LogEntry.objects.filter(date__gte=datetime.datetime(1980, 1, 1))
|
||||||
self.assertEqual(logs.count(), 10)
|
assert logs.count() == 10
|
||||||
|
|
||||||
def test_validation(self):
|
def test_validation(self):
|
||||||
"""Ensure that invalid values cannot be assigned to datetime
|
"""Ensure that invalid values cannot be assigned to datetime
|
||||||
@@ -166,6 +157,8 @@ class TestDateField(MongoDBTestCase):
|
|||||||
log.validate()
|
log.validate()
|
||||||
|
|
||||||
log.time = -1
|
log.time = -1
|
||||||
self.assertRaises(ValidationError, log.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
log.validate()
|
||||||
log.time = "ABC"
|
log.time = "ABC"
|
||||||
self.assertRaises(ValidationError, log.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
log.validate()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import six
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import dateutil
|
import dateutil
|
||||||
@@ -24,7 +25,8 @@ class TestDateTimeField(MongoDBTestCase):
|
|||||||
dt = DateTimeField()
|
dt = DateTimeField()
|
||||||
|
|
||||||
md = MyDoc(dt="")
|
md = MyDoc(dt="")
|
||||||
self.assertRaises(ValidationError, md.save)
|
with pytest.raises(ValidationError):
|
||||||
|
md.save()
|
||||||
|
|
||||||
def test_datetime_from_whitespace_string(self):
|
def test_datetime_from_whitespace_string(self):
|
||||||
"""
|
"""
|
||||||
@@ -36,7 +38,8 @@ class TestDateTimeField(MongoDBTestCase):
|
|||||||
dt = DateTimeField()
|
dt = DateTimeField()
|
||||||
|
|
||||||
md = MyDoc(dt=" ")
|
md = MyDoc(dt=" ")
|
||||||
self.assertRaises(ValidationError, md.save)
|
with pytest.raises(ValidationError):
|
||||||
|
md.save()
|
||||||
|
|
||||||
def test_default_value_utcnow(self):
|
def test_default_value_utcnow(self):
|
||||||
"""Ensure that default field values are used when creating
|
"""Ensure that default field values are used when creating
|
||||||
@@ -50,11 +53,9 @@ class TestDateTimeField(MongoDBTestCase):
|
|||||||
person = Person()
|
person = Person()
|
||||||
person.validate()
|
person.validate()
|
||||||
person_created_t0 = person.created
|
person_created_t0 = person.created
|
||||||
self.assertLess(person.created - utcnow, dt.timedelta(seconds=1))
|
assert person.created - utcnow < dt.timedelta(seconds=1)
|
||||||
self.assertEqual(
|
assert person_created_t0 == person.created # make sure it does not change
|
||||||
person_created_t0, person.created
|
assert person._data["created"] == person.created
|
||||||
) # make sure it does not change
|
|
||||||
self.assertEqual(person._data["created"], person.created)
|
|
||||||
|
|
||||||
def test_handling_microseconds(self):
|
def test_handling_microseconds(self):
|
||||||
"""Tests showing pymongo datetime fields handling of microseconds.
|
"""Tests showing pymongo datetime fields handling of microseconds.
|
||||||
@@ -74,7 +75,7 @@ class TestDateTimeField(MongoDBTestCase):
|
|||||||
log.date = dt.date.today()
|
log.date = dt.date.today()
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertEqual(log.date.date(), dt.date.today())
|
assert log.date.date() == dt.date.today()
|
||||||
|
|
||||||
# Post UTC - microseconds are rounded (down) nearest millisecond and
|
# Post UTC - microseconds are rounded (down) nearest millisecond and
|
||||||
# dropped
|
# dropped
|
||||||
@@ -84,8 +85,8 @@ class TestDateTimeField(MongoDBTestCase):
|
|||||||
log.date = d1
|
log.date = d1
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertNotEqual(log.date, d1)
|
assert log.date != d1
|
||||||
self.assertEqual(log.date, d2)
|
assert log.date == d2
|
||||||
|
|
||||||
# Post UTC - microseconds are rounded (down) nearest millisecond
|
# Post UTC - microseconds are rounded (down) nearest millisecond
|
||||||
d1 = dt.datetime(1970, 1, 1, 0, 0, 1, 9999)
|
d1 = dt.datetime(1970, 1, 1, 0, 0, 1, 9999)
|
||||||
@@ -93,19 +94,8 @@ class TestDateTimeField(MongoDBTestCase):
|
|||||||
log.date = d1
|
log.date = d1
|
||||||
log.save()
|
log.save()
|
||||||
log.reload()
|
log.reload()
|
||||||
self.assertNotEqual(log.date, d1)
|
assert log.date != d1
|
||||||
self.assertEqual(log.date, d2)
|
assert log.date == d2
|
||||||
|
|
||||||
if not six.PY3:
|
|
||||||
# Pre UTC dates microseconds below 1000 are dropped
|
|
||||||
# This does not seem to be true in PY3
|
|
||||||
d1 = dt.datetime(1969, 12, 31, 23, 59, 59, 999)
|
|
||||||
d2 = dt.datetime(1969, 12, 31, 23, 59, 59)
|
|
||||||
log.date = d1
|
|
||||||
log.save()
|
|
||||||
log.reload()
|
|
||||||
self.assertNotEqual(log.date, d1)
|
|
||||||
self.assertEqual(log.date, d2)
|
|
||||||
|
|
||||||
def test_regular_usage(self):
|
def test_regular_usage(self):
|
||||||
"""Tests for regular datetime fields"""
|
"""Tests for regular datetime fields"""
|
||||||
@@ -123,43 +113,43 @@ class TestDateTimeField(MongoDBTestCase):
|
|||||||
|
|
||||||
for query in (d1, d1.isoformat(" ")):
|
for query in (d1, d1.isoformat(" ")):
|
||||||
log1 = LogEntry.objects.get(date=query)
|
log1 = LogEntry.objects.get(date=query)
|
||||||
self.assertEqual(log, log1)
|
assert log == log1
|
||||||
|
|
||||||
if dateutil:
|
if dateutil:
|
||||||
log1 = LogEntry.objects.get(date=d1.isoformat("T"))
|
log1 = LogEntry.objects.get(date=d1.isoformat("T"))
|
||||||
self.assertEqual(log, log1)
|
assert log == log1
|
||||||
|
|
||||||
# create additional 19 log entries for a total of 20
|
# create additional 19 log entries for a total of 20
|
||||||
for i in range(1971, 1990):
|
for i in range(1971, 1990):
|
||||||
d = dt.datetime(i, 1, 1, 0, 0, 1)
|
d = dt.datetime(i, 1, 1, 0, 0, 1)
|
||||||
LogEntry(date=d).save()
|
LogEntry(date=d).save()
|
||||||
|
|
||||||
self.assertEqual(LogEntry.objects.count(), 20)
|
assert LogEntry.objects.count() == 20
|
||||||
|
|
||||||
# Test ordering
|
# Test ordering
|
||||||
logs = LogEntry.objects.order_by("date")
|
logs = LogEntry.objects.order_by("date")
|
||||||
i = 0
|
i = 0
|
||||||
while i < 19:
|
while i < 19:
|
||||||
self.assertTrue(logs[i].date <= logs[i + 1].date)
|
assert logs[i].date <= logs[i + 1].date
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
logs = LogEntry.objects.order_by("-date")
|
logs = LogEntry.objects.order_by("-date")
|
||||||
i = 0
|
i = 0
|
||||||
while i < 19:
|
while i < 19:
|
||||||
self.assertTrue(logs[i].date >= logs[i + 1].date)
|
assert logs[i].date >= logs[i + 1].date
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# Test searching
|
# Test searching
|
||||||
logs = LogEntry.objects.filter(date__gte=dt.datetime(1980, 1, 1))
|
logs = LogEntry.objects.filter(date__gte=dt.datetime(1980, 1, 1))
|
||||||
self.assertEqual(logs.count(), 10)
|
assert logs.count() == 10
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(date__lte=dt.datetime(1980, 1, 1))
|
logs = LogEntry.objects.filter(date__lte=dt.datetime(1980, 1, 1))
|
||||||
self.assertEqual(logs.count(), 10)
|
assert logs.count() == 10
|
||||||
|
|
||||||
logs = LogEntry.objects.filter(
|
logs = LogEntry.objects.filter(
|
||||||
date__lte=dt.datetime(1980, 1, 1), date__gte=dt.datetime(1975, 1, 1)
|
date__lte=dt.datetime(1980, 1, 1), date__gte=dt.datetime(1975, 1, 1)
|
||||||
)
|
)
|
||||||
self.assertEqual(logs.count(), 5)
|
assert logs.count() == 5
|
||||||
|
|
||||||
def test_datetime_validation(self):
|
def test_datetime_validation(self):
|
||||||
"""Ensure that invalid values cannot be assigned to datetime
|
"""Ensure that invalid values cannot be assigned to datetime
|
||||||
@@ -187,15 +177,20 @@ class TestDateTimeField(MongoDBTestCase):
|
|||||||
log.validate()
|
log.validate()
|
||||||
|
|
||||||
log.time = -1
|
log.time = -1
|
||||||
self.assertRaises(ValidationError, log.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
log.validate()
|
||||||
log.time = "ABC"
|
log.time = "ABC"
|
||||||
self.assertRaises(ValidationError, log.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
log.validate()
|
||||||
log.time = "2019-05-16 21:GARBAGE:12"
|
log.time = "2019-05-16 21:GARBAGE:12"
|
||||||
self.assertRaises(ValidationError, log.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
log.validate()
|
||||||
log.time = "2019-05-16 21:42:57.GARBAGE"
|
log.time = "2019-05-16 21:42:57.GARBAGE"
|
||||||
self.assertRaises(ValidationError, log.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
log.validate()
|
||||||
log.time = "2019-05-16 21:42:57.123.456"
|
log.time = "2019-05-16 21:42:57.123.456"
|
||||||
self.assertRaises(ValidationError, log.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
log.validate()
|
||||||
|
|
||||||
def test_parse_datetime_as_str(self):
|
def test_parse_datetime_as_str(self):
|
||||||
class DTDoc(Document):
|
class DTDoc(Document):
|
||||||
@@ -206,15 +201,16 @@ class TestDateTimeField(MongoDBTestCase):
|
|||||||
# make sure that passing a parsable datetime works
|
# make sure that passing a parsable datetime works
|
||||||
dtd = DTDoc()
|
dtd = DTDoc()
|
||||||
dtd.date = date_str
|
dtd.date = date_str
|
||||||
self.assertIsInstance(dtd.date, six.string_types)
|
assert isinstance(dtd.date, str)
|
||||||
dtd.save()
|
dtd.save()
|
||||||
dtd.reload()
|
dtd.reload()
|
||||||
|
|
||||||
self.assertIsInstance(dtd.date, dt.datetime)
|
assert isinstance(dtd.date, dt.datetime)
|
||||||
self.assertEqual(str(dtd.date), date_str)
|
assert str(dtd.date) == date_str
|
||||||
|
|
||||||
dtd.date = "January 1st, 9999999999"
|
dtd.date = "January 1st, 9999999999"
|
||||||
self.assertRaises(ValidationError, dtd.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
dtd.validate()
|
||||||
|
|
||||||
|
|
||||||
class TestDateTimeTzAware(MongoDBTestCase):
|
class TestDateTimeTzAware(MongoDBTestCase):
|
||||||
@@ -235,4 +231,4 @@ class TestDateTimeTzAware(MongoDBTestCase):
|
|||||||
|
|
||||||
log = LogEntry.objects.first()
|
log = LogEntry.objects.first()
|
||||||
log.time = dt.datetime(2013, 1, 1, 0, 0, 0)
|
log.time = dt.datetime(2013, 1, 1, 0, 0, 0)
|
||||||
self.assertEqual(["time"], log._changed_fields)
|
assert ["time"] == log._changed_fields
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from mongoengine import *
|
import pytest
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -18,21 +19,26 @@ class TestDecimalField(MongoDBTestCase):
|
|||||||
|
|
||||||
Person(height=Decimal("1.89")).save()
|
Person(height=Decimal("1.89")).save()
|
||||||
person = Person.objects.first()
|
person = Person.objects.first()
|
||||||
self.assertEqual(person.height, Decimal("1.89"))
|
assert person.height == Decimal("1.89")
|
||||||
|
|
||||||
person.height = "2.0"
|
person.height = "2.0"
|
||||||
person.save()
|
person.save()
|
||||||
person.height = 0.01
|
person.height = 0.01
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
person.height = Decimal("0.01")
|
person.height = Decimal("0.01")
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
person.height = Decimal("4.0")
|
person.height = Decimal("4.0")
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
person.height = "something invalid"
|
person.height = "something invalid"
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
person_2 = Person(height="something invalid")
|
person_2 = Person(height="something invalid")
|
||||||
self.assertRaises(ValidationError, person_2.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person_2.validate()
|
||||||
|
|
||||||
def test_comparison(self):
|
def test_comparison(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -45,11 +51,11 @@ class TestDecimalField(MongoDBTestCase):
|
|||||||
Person(money=8).save()
|
Person(money=8).save()
|
||||||
Person(money=10).save()
|
Person(money=10).save()
|
||||||
|
|
||||||
self.assertEqual(2, Person.objects(money__gt=Decimal("7")).count())
|
assert 2 == Person.objects(money__gt=Decimal("7")).count()
|
||||||
self.assertEqual(2, Person.objects(money__gt=7).count())
|
assert 2 == Person.objects(money__gt=7).count()
|
||||||
self.assertEqual(2, Person.objects(money__gt="7").count())
|
assert 2 == Person.objects(money__gt="7").count()
|
||||||
|
|
||||||
self.assertEqual(3, Person.objects(money__gte="7").count())
|
assert 3 == Person.objects(money__gte="7").count()
|
||||||
|
|
||||||
def test_storage(self):
|
def test_storage(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -87,7 +93,7 @@ class TestDecimalField(MongoDBTestCase):
|
|||||||
]
|
]
|
||||||
expected.extend(expected)
|
expected.extend(expected)
|
||||||
actual = list(Person.objects.exclude("id").as_pymongo())
|
actual = list(Person.objects.exclude("id").as_pymongo())
|
||||||
self.assertEqual(expected, actual)
|
assert expected == actual
|
||||||
|
|
||||||
# How it comes out locally
|
# How it comes out locally
|
||||||
expected = [
|
expected = [
|
||||||
@@ -101,4 +107,4 @@ class TestDecimalField(MongoDBTestCase):
|
|||||||
expected.extend(expected)
|
expected.extend(expected)
|
||||||
for field_name in ["float_value", "string_value"]:
|
for field_name in ["float_value", "string_value"]:
|
||||||
actual = list(Person.objects().scalar(field_name))
|
actual = list(Person.objects().scalar(field_name))
|
||||||
self.assertEqual(expected, actual)
|
assert expected == actual
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from bson import InvalidDocument
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.base import BaseDict
|
from mongoengine.base import BaseDict
|
||||||
|
from mongoengine.mongodb_support import MONGODB_36, get_mongodb_version
|
||||||
|
|
||||||
from tests.utils import MongoDBTestCase, get_as_pymongo
|
from tests.utils import MongoDBTestCase, get_as_pymongo
|
||||||
|
|
||||||
@@ -14,7 +18,57 @@ class TestDictField(MongoDBTestCase):
|
|||||||
|
|
||||||
info = {"testkey": "testvalue"}
|
info = {"testkey": "testvalue"}
|
||||||
post = BlogPost(info=info).save()
|
post = BlogPost(info=info).save()
|
||||||
self.assertEqual(get_as_pymongo(post), {"_id": post.id, "info": info})
|
assert get_as_pymongo(post) == {"_id": post.id, "info": info}
|
||||||
|
|
||||||
|
def test_validate_invalid_type(self):
|
||||||
|
class BlogPost(Document):
|
||||||
|
info = DictField()
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
invalid_infos = ["my post", ["test", "test"], {1: "test"}]
|
||||||
|
for invalid_info in invalid_infos:
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
BlogPost(info=invalid_info).validate()
|
||||||
|
|
||||||
|
def test_keys_with_dots_or_dollars(self):
|
||||||
|
class BlogPost(Document):
|
||||||
|
info = DictField()
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
post = BlogPost()
|
||||||
|
|
||||||
|
post.info = {"$title": "test"}
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
post.validate()
|
||||||
|
|
||||||
|
post.info = {"nested": {"$title": "test"}}
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
post.validate()
|
||||||
|
|
||||||
|
post.info = {"$title.test": "test"}
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
post.validate()
|
||||||
|
|
||||||
|
post.info = {"nested": {"the.title": "test"}}
|
||||||
|
if get_mongodb_version() < MONGODB_36:
|
||||||
|
# MongoDB < 3.6 rejects dots
|
||||||
|
# To avoid checking the mongodb version from the DictField class
|
||||||
|
# we rely on MongoDB to reject the data during the save
|
||||||
|
post.validate()
|
||||||
|
with pytest.raises(InvalidDocument):
|
||||||
|
post.save()
|
||||||
|
else:
|
||||||
|
post.validate()
|
||||||
|
|
||||||
|
post.info = {"dollar_and_dot": {"te$st.test": "test"}}
|
||||||
|
if get_mongodb_version() < MONGODB_36:
|
||||||
|
post.validate()
|
||||||
|
with pytest.raises(InvalidDocument):
|
||||||
|
post.save()
|
||||||
|
else:
|
||||||
|
post.validate()
|
||||||
|
|
||||||
def test_general_things(self):
|
def test_general_things(self):
|
||||||
"""Ensure that dict types work as expected."""
|
"""Ensure that dict types work as expected."""
|
||||||
@@ -24,29 +78,7 @@ class TestDictField(MongoDBTestCase):
|
|||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
post = BlogPost()
|
post = BlogPost(info={"title": "test"})
|
||||||
post.info = "my post"
|
|
||||||
self.assertRaises(ValidationError, post.validate)
|
|
||||||
|
|
||||||
post.info = ["test", "test"]
|
|
||||||
self.assertRaises(ValidationError, post.validate)
|
|
||||||
|
|
||||||
post.info = {"$title": "test"}
|
|
||||||
self.assertRaises(ValidationError, post.validate)
|
|
||||||
|
|
||||||
post.info = {"nested": {"$title": "test"}}
|
|
||||||
self.assertRaises(ValidationError, post.validate)
|
|
||||||
|
|
||||||
post.info = {"the.title": "test"}
|
|
||||||
self.assertRaises(ValidationError, post.validate)
|
|
||||||
|
|
||||||
post.info = {"nested": {"the.title": "test"}}
|
|
||||||
self.assertRaises(ValidationError, post.validate)
|
|
||||||
|
|
||||||
post.info = {1: "test"}
|
|
||||||
self.assertRaises(ValidationError, post.validate)
|
|
||||||
|
|
||||||
post.info = {"title": "test"}
|
|
||||||
post.save()
|
post.save()
|
||||||
|
|
||||||
post = BlogPost()
|
post = BlogPost()
|
||||||
@@ -61,33 +93,27 @@ class TestDictField(MongoDBTestCase):
|
|||||||
post.info = {"details": {"test": 3}}
|
post.info = {"details": {"test": 3}}
|
||||||
post.save()
|
post.save()
|
||||||
|
|
||||||
self.assertEqual(BlogPost.objects.count(), 4)
|
assert BlogPost.objects.count() == 4
|
||||||
self.assertEqual(BlogPost.objects.filter(info__title__exact="test").count(), 1)
|
assert BlogPost.objects.filter(info__title__exact="test").count() == 1
|
||||||
self.assertEqual(
|
assert BlogPost.objects.filter(info__details__test__exact="test").count() == 1
|
||||||
BlogPost.objects.filter(info__details__test__exact="test").count(), 1
|
|
||||||
)
|
|
||||||
|
|
||||||
post = BlogPost.objects.filter(info__title__exact="dollar_sign").first()
|
post = BlogPost.objects.filter(info__title__exact="dollar_sign").first()
|
||||||
self.assertIn("te$t", post["info"]["details"])
|
assert "te$t" in post["info"]["details"]
|
||||||
|
|
||||||
# Confirm handles non strings or non existing keys
|
# Confirm handles non strings or non existing keys
|
||||||
self.assertEqual(
|
assert BlogPost.objects.filter(info__details__test__exact=5).count() == 0
|
||||||
BlogPost.objects.filter(info__details__test__exact=5).count(), 0
|
assert BlogPost.objects.filter(info__made_up__test__exact="test").count() == 0
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
BlogPost.objects.filter(info__made_up__test__exact="test").count(), 0
|
|
||||||
)
|
|
||||||
|
|
||||||
post = BlogPost.objects.create(info={"title": "original"})
|
post = BlogPost.objects.create(info={"title": "original"})
|
||||||
post.info.update({"title": "updated"})
|
post.info.update({"title": "updated"})
|
||||||
post.save()
|
post.save()
|
||||||
post.reload()
|
post.reload()
|
||||||
self.assertEqual("updated", post.info["title"])
|
assert "updated" == post.info["title"]
|
||||||
|
|
||||||
post.info.setdefault("authors", [])
|
post.info.setdefault("authors", [])
|
||||||
post.save()
|
post.save()
|
||||||
post.reload()
|
post.reload()
|
||||||
self.assertEqual([], post.info["authors"])
|
assert [] == post.info["authors"]
|
||||||
|
|
||||||
def test_dictfield_dump_document(self):
|
def test_dictfield_dump_document(self):
|
||||||
"""Ensure a DictField can handle another document's dump."""
|
"""Ensure a DictField can handle another document's dump."""
|
||||||
@@ -114,10 +140,8 @@ class TestDictField(MongoDBTestCase):
|
|||||||
).save()
|
).save()
|
||||||
doc = Doc(field=to_embed.to_mongo().to_dict())
|
doc = Doc(field=to_embed.to_mongo().to_dict())
|
||||||
doc.save()
|
doc.save()
|
||||||
self.assertIsInstance(doc.field, dict)
|
assert isinstance(doc.field, dict)
|
||||||
self.assertEqual(
|
assert doc.field == {"_id": 2, "recursive": {"_id": 1, "recursive": {}}}
|
||||||
doc.field, {"_id": 2, "recursive": {"_id": 1, "recursive": {}}}
|
|
||||||
)
|
|
||||||
# Same thing with a Document with a _cls field
|
# Same thing with a Document with a _cls field
|
||||||
to_embed_recursive = ToEmbedChild(id=1).save()
|
to_embed_recursive = ToEmbedChild(id=1).save()
|
||||||
to_embed_child = ToEmbedChild(
|
to_embed_child = ToEmbedChild(
|
||||||
@@ -125,7 +149,7 @@ class TestDictField(MongoDBTestCase):
|
|||||||
).save()
|
).save()
|
||||||
doc = Doc(field=to_embed_child.to_mongo().to_dict())
|
doc = Doc(field=to_embed_child.to_mongo().to_dict())
|
||||||
doc.save()
|
doc.save()
|
||||||
self.assertIsInstance(doc.field, dict)
|
assert isinstance(doc.field, dict)
|
||||||
expected = {
|
expected = {
|
||||||
"_id": 2,
|
"_id": 2,
|
||||||
"_cls": "ToEmbedParent.ToEmbedChild",
|
"_cls": "ToEmbedParent.ToEmbedChild",
|
||||||
@@ -135,7 +159,7 @@ class TestDictField(MongoDBTestCase):
|
|||||||
"recursive": {},
|
"recursive": {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
self.assertEqual(doc.field, expected)
|
assert doc.field == expected
|
||||||
|
|
||||||
def test_dictfield_strict(self):
|
def test_dictfield_strict(self):
|
||||||
"""Ensure that dict field handles validation if provided a strict field type."""
|
"""Ensure that dict field handles validation if provided a strict field type."""
|
||||||
@@ -150,7 +174,7 @@ class TestDictField(MongoDBTestCase):
|
|||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
# try creating an invalid mapping
|
# try creating an invalid mapping
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
e.mapping["somestring"] = "abc"
|
e.mapping["somestring"] = "abc"
|
||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
@@ -184,22 +208,21 @@ class TestDictField(MongoDBTestCase):
|
|||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
e2 = Simple.objects.get(id=e.id)
|
e2 = Simple.objects.get(id=e.id)
|
||||||
self.assertIsInstance(e2.mapping["somestring"], StringSetting)
|
assert isinstance(e2.mapping["somestring"], StringSetting)
|
||||||
self.assertIsInstance(e2.mapping["someint"], IntegerSetting)
|
assert isinstance(e2.mapping["someint"], IntegerSetting)
|
||||||
|
|
||||||
# Test querying
|
# Test querying
|
||||||
self.assertEqual(Simple.objects.filter(mapping__someint__value=42).count(), 1)
|
assert Simple.objects.filter(mapping__someint__value=42).count() == 1
|
||||||
self.assertEqual(
|
assert Simple.objects.filter(mapping__nested_dict__number=1).count() == 1
|
||||||
Simple.objects.filter(mapping__nested_dict__number=1).count(), 1
|
assert (
|
||||||
|
Simple.objects.filter(mapping__nested_dict__complex__value=42).count() == 1
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert (
|
||||||
Simple.objects.filter(mapping__nested_dict__complex__value=42).count(), 1
|
Simple.objects.filter(mapping__nested_dict__list__0__value=42).count() == 1
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert (
|
||||||
Simple.objects.filter(mapping__nested_dict__list__0__value=42).count(), 1
|
Simple.objects.filter(mapping__nested_dict__list__1__value="foo").count()
|
||||||
)
|
== 1
|
||||||
self.assertEqual(
|
|
||||||
Simple.objects.filter(mapping__nested_dict__list__1__value="foo").count(), 1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Confirm can update
|
# Confirm can update
|
||||||
@@ -207,11 +230,13 @@ class TestDictField(MongoDBTestCase):
|
|||||||
Simple.objects().update(
|
Simple.objects().update(
|
||||||
set__mapping__nested_dict__list__1=StringSetting(value="Boo")
|
set__mapping__nested_dict__list__1=StringSetting(value="Boo")
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert (
|
||||||
Simple.objects.filter(mapping__nested_dict__list__1__value="foo").count(), 0
|
Simple.objects.filter(mapping__nested_dict__list__1__value="foo").count()
|
||||||
|
== 0
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert (
|
||||||
Simple.objects.filter(mapping__nested_dict__list__1__value="Boo").count(), 1
|
Simple.objects.filter(mapping__nested_dict__list__1__value="Boo").count()
|
||||||
|
== 1
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_push_dict(self):
|
def test_push_dict(self):
|
||||||
@@ -221,12 +246,12 @@ class TestDictField(MongoDBTestCase):
|
|||||||
doc = MyModel(events=[{"a": 1}]).save()
|
doc = MyModel(events=[{"a": 1}]).save()
|
||||||
raw_doc = get_as_pymongo(doc)
|
raw_doc = get_as_pymongo(doc)
|
||||||
expected_raw_doc = {"_id": doc.id, "events": [{"a": 1}]}
|
expected_raw_doc = {"_id": doc.id, "events": [{"a": 1}]}
|
||||||
self.assertEqual(raw_doc, expected_raw_doc)
|
assert raw_doc == expected_raw_doc
|
||||||
|
|
||||||
MyModel.objects(id=doc.id).update(push__events={})
|
MyModel.objects(id=doc.id).update(push__events={})
|
||||||
raw_doc = get_as_pymongo(doc)
|
raw_doc = get_as_pymongo(doc)
|
||||||
expected_raw_doc = {"_id": doc.id, "events": [{"a": 1}, {}]}
|
expected_raw_doc = {"_id": doc.id, "events": [{"a": 1}, {}]}
|
||||||
self.assertEqual(raw_doc, expected_raw_doc)
|
assert raw_doc == expected_raw_doc
|
||||||
|
|
||||||
def test_ensure_unique_default_instances(self):
|
def test_ensure_unique_default_instances(self):
|
||||||
"""Ensure that every field has it's own unique default instance."""
|
"""Ensure that every field has it's own unique default instance."""
|
||||||
@@ -239,8 +264,8 @@ class TestDictField(MongoDBTestCase):
|
|||||||
d1.data["foo"] = "bar"
|
d1.data["foo"] = "bar"
|
||||||
d1.data2["foo"] = "bar"
|
d1.data2["foo"] = "bar"
|
||||||
d2 = D()
|
d2 = D()
|
||||||
self.assertEqual(d2.data, {})
|
assert d2.data == {}
|
||||||
self.assertEqual(d2.data2, {})
|
assert d2.data2 == {}
|
||||||
|
|
||||||
def test_dict_field_invalid_dict_value(self):
|
def test_dict_field_invalid_dict_value(self):
|
||||||
class DictFieldTest(Document):
|
class DictFieldTest(Document):
|
||||||
@@ -250,11 +275,13 @@ class TestDictField(MongoDBTestCase):
|
|||||||
|
|
||||||
test = DictFieldTest(dictionary=None)
|
test = DictFieldTest(dictionary=None)
|
||||||
test.dictionary # Just access to test getter
|
test.dictionary # Just access to test getter
|
||||||
self.assertRaises(ValidationError, test.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
test.validate()
|
||||||
|
|
||||||
test = DictFieldTest(dictionary=False)
|
test = DictFieldTest(dictionary=False)
|
||||||
test.dictionary # Just access to test getter
|
test.dictionary # Just access to test getter
|
||||||
self.assertRaises(ValidationError, test.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
test.validate()
|
||||||
|
|
||||||
def test_dict_field_raises_validation_error_if_wrongly_assign_embedded_doc(self):
|
def test_dict_field_raises_validation_error_if_wrongly_assign_embedded_doc(self):
|
||||||
class DictFieldTest(Document):
|
class DictFieldTest(Document):
|
||||||
@@ -267,12 +294,12 @@ class TestDictField(MongoDBTestCase):
|
|||||||
|
|
||||||
embed = Embedded(name="garbage")
|
embed = Embedded(name="garbage")
|
||||||
doc = DictFieldTest(dictionary=embed)
|
doc = DictFieldTest(dictionary=embed)
|
||||||
with self.assertRaises(ValidationError) as ctx_err:
|
with pytest.raises(ValidationError) as exc_info:
|
||||||
doc.validate()
|
doc.validate()
|
||||||
self.assertIn("'dictionary'", str(ctx_err.exception))
|
|
||||||
self.assertIn(
|
error_msg = str(exc_info.value)
|
||||||
"Only dictionaries may be used in a DictField", str(ctx_err.exception)
|
assert "'dictionary'" in error_msg
|
||||||
)
|
assert "Only dictionaries may be used in a DictField" in error_msg
|
||||||
|
|
||||||
def test_atomic_update_dict_field(self):
|
def test_atomic_update_dict_field(self):
|
||||||
"""Ensure that the entire DictField can be atomically updated."""
|
"""Ensure that the entire DictField can be atomically updated."""
|
||||||
@@ -287,11 +314,11 @@ class TestDictField(MongoDBTestCase):
|
|||||||
e.save()
|
e.save()
|
||||||
e.update(set__mapping={"ints": [3, 4]})
|
e.update(set__mapping={"ints": [3, 4]})
|
||||||
e.reload()
|
e.reload()
|
||||||
self.assertEqual(BaseDict, type(e.mapping))
|
assert isinstance(e.mapping, BaseDict)
|
||||||
self.assertEqual({"ints": [3, 4]}, e.mapping)
|
assert {"ints": [3, 4]} == e.mapping
|
||||||
|
|
||||||
# try creating an invalid mapping
|
# try creating an invalid mapping
|
||||||
with self.assertRaises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
e.update(set__mapping={"somestrings": ["foo", "bar"]})
|
e.update(set__mapping={"somestrings": ["foo", "bar"]})
|
||||||
|
|
||||||
def test_dictfield_with_referencefield_complex_nesting_cases(self):
|
def test_dictfield_with_referencefield_complex_nesting_cases(self):
|
||||||
@@ -329,13 +356,13 @@ class TestDictField(MongoDBTestCase):
|
|||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
s = Simple.objects.first()
|
s = Simple.objects.first()
|
||||||
self.assertIsInstance(s.mapping0["someint"], Doc)
|
assert isinstance(s.mapping0["someint"], Doc)
|
||||||
self.assertIsInstance(s.mapping1["someint"], Doc)
|
assert isinstance(s.mapping1["someint"], Doc)
|
||||||
self.assertIsInstance(s.mapping2["someint"][0], Doc)
|
assert isinstance(s.mapping2["someint"][0], Doc)
|
||||||
self.assertIsInstance(s.mapping3["someint"][0], Doc)
|
assert isinstance(s.mapping3["someint"][0], Doc)
|
||||||
self.assertIsInstance(s.mapping4["someint"]["d"], Doc)
|
assert isinstance(s.mapping4["someint"]["d"], Doc)
|
||||||
self.assertIsInstance(s.mapping5["someint"]["d"], Doc)
|
assert isinstance(s.mapping5["someint"]["d"], Doc)
|
||||||
self.assertIsInstance(s.mapping6["someint"][0]["d"], Doc)
|
assert isinstance(s.mapping6["someint"][0]["d"], Doc)
|
||||||
self.assertIsInstance(s.mapping7["someint"][0]["d"], Doc)
|
assert isinstance(s.mapping7["someint"][0]["d"], Doc)
|
||||||
self.assertIsInstance(s.mapping8["someint"][0]["d"][0], Doc)
|
assert isinstance(s.mapping8["someint"][0]["d"][0], Doc)
|
||||||
self.assertIsInstance(s.mapping9["someint"][0]["d"][0], Doc)
|
assert isinstance(s.mapping9["someint"][0]["d"][0], Doc)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import sys
|
import sys
|
||||||
from unittest import SkipTest
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
|
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -27,7 +27,8 @@ class TestEmailField(MongoDBTestCase):
|
|||||||
user.validate()
|
user.validate()
|
||||||
|
|
||||||
user = User(email="ross@example.com.")
|
user = User(email="ross@example.com.")
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
# unicode domain
|
# unicode domain
|
||||||
user = User(email=u"user@пример.рф")
|
user = User(email=u"user@пример.рф")
|
||||||
@@ -35,24 +36,22 @@ class TestEmailField(MongoDBTestCase):
|
|||||||
|
|
||||||
# invalid unicode domain
|
# invalid unicode domain
|
||||||
user = User(email=u"user@пример")
|
user = User(email=u"user@пример")
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
# invalid data type
|
# invalid data type
|
||||||
user = User(email=123)
|
user = User(email=123)
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
def test_email_field_unicode_user(self):
|
def test_email_field_unicode_user(self):
|
||||||
# Don't run this test on pypy3, which doesn't support unicode regex:
|
|
||||||
# https://bitbucket.org/pypy/pypy/issues/1821/regular-expression-doesnt-find-unicode
|
|
||||||
if sys.version_info[:2] == (3, 2):
|
|
||||||
raise SkipTest("unicode email addresses are not supported on PyPy 3")
|
|
||||||
|
|
||||||
class User(Document):
|
class User(Document):
|
||||||
email = EmailField()
|
email = EmailField()
|
||||||
|
|
||||||
# unicode user shouldn't validate by default...
|
# unicode user shouldn't validate by default...
|
||||||
user = User(email=u"Dörte@Sörensen.example.com")
|
user = User(email=u"Dörte@Sörensen.example.com")
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
# ...but it should be fine with allow_utf8_user set to True
|
# ...but it should be fine with allow_utf8_user set to True
|
||||||
class User(Document):
|
class User(Document):
|
||||||
@@ -67,7 +66,8 @@ class TestEmailField(MongoDBTestCase):
|
|||||||
|
|
||||||
# localhost domain shouldn't validate by default...
|
# localhost domain shouldn't validate by default...
|
||||||
user = User(email="me@localhost")
|
user = User(email="me@localhost")
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
# ...but it should be fine if it's whitelisted
|
# ...but it should be fine if it's whitelisted
|
||||||
class User(Document):
|
class User(Document):
|
||||||
@@ -82,9 +82,10 @@ class TestEmailField(MongoDBTestCase):
|
|||||||
|
|
||||||
invalid_idn = ".google.com"
|
invalid_idn = ".google.com"
|
||||||
user = User(email="me@%s" % invalid_idn)
|
user = User(email="me@%s" % invalid_idn)
|
||||||
with self.assertRaises(ValidationError) as ctx_err:
|
|
||||||
|
with pytest.raises(ValidationError) as exc_info:
|
||||||
user.validate()
|
user.validate()
|
||||||
self.assertIn("domain failed IDN encoding", str(ctx_err.exception))
|
assert "domain failed IDN encoding" in str(exc_info.value)
|
||||||
|
|
||||||
def test_email_field_ip_domain(self):
|
def test_email_field_ip_domain(self):
|
||||||
class User(Document):
|
class User(Document):
|
||||||
@@ -96,13 +97,16 @@ class TestEmailField(MongoDBTestCase):
|
|||||||
|
|
||||||
# IP address as a domain shouldn't validate by default...
|
# IP address as a domain shouldn't validate by default...
|
||||||
user = User(email=valid_ipv4)
|
user = User(email=valid_ipv4)
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
user = User(email=valid_ipv6)
|
user = User(email=valid_ipv6)
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
user = User(email=invalid_ip)
|
user = User(email=invalid_ip)
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
# ...but it should be fine with allow_ip_domain set to True
|
# ...but it should be fine with allow_ip_domain set to True
|
||||||
class User(Document):
|
class User(Document):
|
||||||
@@ -116,7 +120,8 @@ class TestEmailField(MongoDBTestCase):
|
|||||||
|
|
||||||
# invalid IP should still fail validation
|
# invalid IP should still fail validation
|
||||||
user = User(email=invalid_ip)
|
user = User(email=invalid_ip)
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
def test_email_field_honors_regex(self):
|
def test_email_field_honors_regex(self):
|
||||||
class User(Document):
|
class User(Document):
|
||||||
@@ -124,8 +129,9 @@ class TestEmailField(MongoDBTestCase):
|
|||||||
|
|
||||||
# Fails regex validation
|
# Fails regex validation
|
||||||
user = User(email="me@foo.com")
|
user = User(email="me@foo.com")
|
||||||
self.assertRaises(ValidationError, user.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
user.validate()
|
||||||
|
|
||||||
# Passes regex validation
|
# Passes regex validation
|
||||||
user = User(email="me@example.com")
|
user = User(email="me@example.com")
|
||||||
self.assertIsNone(user.validate())
|
assert user.validate() is None
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import (
|
from mongoengine import (
|
||||||
Document,
|
Document,
|
||||||
StringField,
|
|
||||||
ValidationError,
|
|
||||||
EmbeddedDocument,
|
EmbeddedDocument,
|
||||||
EmbeddedDocumentField,
|
EmbeddedDocumentField,
|
||||||
InvalidQueryError,
|
|
||||||
LookUpError,
|
|
||||||
IntField,
|
|
||||||
GenericEmbeddedDocumentField,
|
GenericEmbeddedDocumentField,
|
||||||
|
IntField,
|
||||||
|
InvalidQueryError,
|
||||||
ListField,
|
ListField,
|
||||||
EmbeddedDocumentListField,
|
LookUpError,
|
||||||
ReferenceField,
|
StringField,
|
||||||
|
ValidationError,
|
||||||
)
|
)
|
||||||
|
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
@@ -23,13 +23,13 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
field = EmbeddedDocumentField(MyDoc)
|
field = EmbeddedDocumentField(MyDoc)
|
||||||
self.assertEqual(field.document_type_obj, MyDoc)
|
assert field.document_type_obj == MyDoc
|
||||||
|
|
||||||
field2 = EmbeddedDocumentField("MyDoc")
|
field2 = EmbeddedDocumentField("MyDoc")
|
||||||
self.assertEqual(field2.document_type_obj, "MyDoc")
|
assert field2.document_type_obj == "MyDoc"
|
||||||
|
|
||||||
def test___init___throw_error_if_document_type_is_not_EmbeddedDocument(self):
|
def test___init___throw_error_if_document_type_is_not_EmbeddedDocument(self):
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
EmbeddedDocumentField(dict)
|
EmbeddedDocumentField(dict)
|
||||||
|
|
||||||
def test_document_type_throw_error_if_not_EmbeddedDocument_subclass(self):
|
def test_document_type_throw_error_if_not_EmbeddedDocument_subclass(self):
|
||||||
@@ -37,11 +37,11 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
emb = EmbeddedDocumentField("MyDoc")
|
emb = EmbeddedDocumentField("MyDoc")
|
||||||
with self.assertRaises(ValidationError) as ctx:
|
with pytest.raises(ValidationError) as exc_info:
|
||||||
emb.document_type
|
emb.document_type
|
||||||
self.assertIn(
|
assert (
|
||||||
"Invalid embedded document class provided to an EmbeddedDocumentField",
|
"Invalid embedded document class provided to an EmbeddedDocumentField"
|
||||||
str(ctx.exception),
|
in str(exc_info.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_embedded_document_field_only_allow_subclasses_of_embedded_document(self):
|
def test_embedded_document_field_only_allow_subclasses_of_embedded_document(self):
|
||||||
@@ -49,12 +49,12 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
class MyDoc(Document):
|
class MyDoc(Document):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
|
|
||||||
class MyFailingDoc(Document):
|
class MyFailingDoc(Document):
|
||||||
emb = EmbeddedDocumentField(MyDoc)
|
emb = EmbeddedDocumentField(MyDoc)
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
|
|
||||||
class MyFailingdoc2(Document):
|
class MyFailingdoc2(Document):
|
||||||
emb = EmbeddedDocumentField("MyDoc")
|
emb = EmbeddedDocumentField("MyDoc")
|
||||||
@@ -73,24 +73,24 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
p = Person(settings=AdminSettings(foo1="bar1", foo2="bar2"), name="John").save()
|
p = Person(settings=AdminSettings(foo1="bar1", foo2="bar2"), name="John").save()
|
||||||
|
|
||||||
# Test non exiting attribute
|
# Test non exiting attribute
|
||||||
with self.assertRaises(InvalidQueryError) as ctx_err:
|
with pytest.raises(InvalidQueryError) as exc_info:
|
||||||
Person.objects(settings__notexist="bar").first()
|
Person.objects(settings__notexist="bar").first()
|
||||||
self.assertEqual(unicode(ctx_err.exception), u'Cannot resolve field "notexist"')
|
assert str(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||||
|
|
||||||
with self.assertRaises(LookUpError):
|
with pytest.raises(LookUpError):
|
||||||
Person.objects.only("settings.notexist")
|
Person.objects.only("settings.notexist")
|
||||||
|
|
||||||
# Test existing attribute
|
# Test existing attribute
|
||||||
self.assertEqual(Person.objects(settings__foo1="bar1").first().id, p.id)
|
assert Person.objects(settings__foo1="bar1").first().id == p.id
|
||||||
only_p = Person.objects.only("settings.foo1").first()
|
only_p = Person.objects.only("settings.foo1").first()
|
||||||
self.assertEqual(only_p.settings.foo1, p.settings.foo1)
|
assert only_p.settings.foo1 == p.settings.foo1
|
||||||
self.assertIsNone(only_p.settings.foo2)
|
assert only_p.settings.foo2 is None
|
||||||
self.assertIsNone(only_p.name)
|
assert only_p.name is None
|
||||||
|
|
||||||
exclude_p = Person.objects.exclude("settings.foo1").first()
|
exclude_p = Person.objects.exclude("settings.foo1").first()
|
||||||
self.assertIsNone(exclude_p.settings.foo1)
|
assert exclude_p.settings.foo1 is None
|
||||||
self.assertEqual(exclude_p.settings.foo2, p.settings.foo2)
|
assert exclude_p.settings.foo2 == p.settings.foo2
|
||||||
self.assertEqual(exclude_p.name, p.name)
|
assert exclude_p.name == p.name
|
||||||
|
|
||||||
def test_query_embedded_document_attribute_with_inheritance(self):
|
def test_query_embedded_document_attribute_with_inheritance(self):
|
||||||
class BaseSettings(EmbeddedDocument):
|
class BaseSettings(EmbeddedDocument):
|
||||||
@@ -109,17 +109,17 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
# Test non exiting attribute
|
# Test non exiting attribute
|
||||||
with self.assertRaises(InvalidQueryError) as ctx_err:
|
with pytest.raises(InvalidQueryError) as exc_info:
|
||||||
self.assertEqual(Person.objects(settings__notexist="bar").first().id, p.id)
|
assert Person.objects(settings__notexist="bar").first().id == p.id
|
||||||
self.assertEqual(unicode(ctx_err.exception), u'Cannot resolve field "notexist"')
|
assert str(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||||
|
|
||||||
# Test existing attribute
|
# Test existing attribute
|
||||||
self.assertEqual(Person.objects(settings__base_foo="basefoo").first().id, p.id)
|
assert Person.objects(settings__base_foo="basefoo").first().id == p.id
|
||||||
self.assertEqual(Person.objects(settings__sub_foo="subfoo").first().id, p.id)
|
assert Person.objects(settings__sub_foo="subfoo").first().id == p.id
|
||||||
|
|
||||||
only_p = Person.objects.only("settings.base_foo", "settings._cls").first()
|
only_p = Person.objects.only("settings.base_foo", "settings._cls").first()
|
||||||
self.assertEqual(only_p.settings.base_foo, "basefoo")
|
assert only_p.settings.base_foo == "basefoo"
|
||||||
self.assertIsNone(only_p.settings.sub_foo)
|
assert only_p.settings.sub_foo is None
|
||||||
|
|
||||||
def test_query_list_embedded_document_with_inheritance(self):
|
def test_query_list_embedded_document_with_inheritance(self):
|
||||||
class Post(EmbeddedDocument):
|
class Post(EmbeddedDocument):
|
||||||
@@ -139,14 +139,14 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
record_text = Record(posts=[TextPost(content="a", title="foo")]).save()
|
record_text = Record(posts=[TextPost(content="a", title="foo")]).save()
|
||||||
|
|
||||||
records = list(Record.objects(posts__author=record_movie.posts[0].author))
|
records = list(Record.objects(posts__author=record_movie.posts[0].author))
|
||||||
self.assertEqual(len(records), 1)
|
assert len(records) == 1
|
||||||
self.assertEqual(records[0].id, record_movie.id)
|
assert records[0].id == record_movie.id
|
||||||
|
|
||||||
records = list(Record.objects(posts__content=record_text.posts[0].content))
|
records = list(Record.objects(posts__content=record_text.posts[0].content))
|
||||||
self.assertEqual(len(records), 1)
|
assert len(records) == 1
|
||||||
self.assertEqual(records[0].id, record_text.id)
|
assert records[0].id == record_text.id
|
||||||
|
|
||||||
self.assertEqual(Record.objects(posts__title="foo").count(), 2)
|
assert Record.objects(posts__title="foo").count() == 2
|
||||||
|
|
||||||
|
|
||||||
class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
||||||
@@ -169,13 +169,13 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
person = Person.objects.first()
|
person = Person.objects.first()
|
||||||
self.assertIsInstance(person.like, Car)
|
assert isinstance(person.like, Car)
|
||||||
|
|
||||||
person.like = Dish(food="arroz", number=15)
|
person.like = Dish(food="arroz", number=15)
|
||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
person = Person.objects.first()
|
person = Person.objects.first()
|
||||||
self.assertIsInstance(person.like, Dish)
|
assert isinstance(person.like, Dish)
|
||||||
|
|
||||||
def test_generic_embedded_document_choices(self):
|
def test_generic_embedded_document_choices(self):
|
||||||
"""Ensure you can limit GenericEmbeddedDocument choices."""
|
"""Ensure you can limit GenericEmbeddedDocument choices."""
|
||||||
@@ -195,13 +195,14 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
|
|
||||||
person = Person(name="Test User")
|
person = Person(name="Test User")
|
||||||
person.like = Car(name="Fiat")
|
person.like = Car(name="Fiat")
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
person.like = Dish(food="arroz", number=15)
|
person.like = Dish(food="arroz", number=15)
|
||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
person = Person.objects.first()
|
person = Person.objects.first()
|
||||||
self.assertIsInstance(person.like, Dish)
|
assert isinstance(person.like, Dish)
|
||||||
|
|
||||||
def test_generic_list_embedded_document_choices(self):
|
def test_generic_list_embedded_document_choices(self):
|
||||||
"""Ensure you can limit GenericEmbeddedDocument choices inside
|
"""Ensure you can limit GenericEmbeddedDocument choices inside
|
||||||
@@ -223,13 +224,14 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
|
|
||||||
person = Person(name="Test User")
|
person = Person(name="Test User")
|
||||||
person.likes = [Car(name="Fiat")]
|
person.likes = [Car(name="Fiat")]
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
person.likes = [Dish(food="arroz", number=15)]
|
person.likes = [Dish(food="arroz", number=15)]
|
||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
person = Person.objects.first()
|
person = Person.objects.first()
|
||||||
self.assertIsInstance(person.likes[0], Dish)
|
assert isinstance(person.likes[0], Dish)
|
||||||
|
|
||||||
def test_choices_validation_documents(self):
|
def test_choices_validation_documents(self):
|
||||||
"""
|
"""
|
||||||
@@ -265,7 +267,8 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
|
|
||||||
# Single Entry Failure
|
# Single Entry Failure
|
||||||
post = BlogPost(comments=[ModeratorComments(author="mod1", message="message1")])
|
post = BlogPost(comments=[ModeratorComments(author="mod1", message="message1")])
|
||||||
self.assertRaises(ValidationError, post.save)
|
with pytest.raises(ValidationError):
|
||||||
|
post.save()
|
||||||
|
|
||||||
# Mixed Entry Failure
|
# Mixed Entry Failure
|
||||||
post = BlogPost(
|
post = BlogPost(
|
||||||
@@ -274,7 +277,8 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
UserComments(author="user2", message="message2"),
|
UserComments(author="user2", message="message2"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
self.assertRaises(ValidationError, post.save)
|
with pytest.raises(ValidationError):
|
||||||
|
post.save()
|
||||||
|
|
||||||
def test_choices_validation_documents_inheritance(self):
|
def test_choices_validation_documents_inheritance(self):
|
||||||
"""
|
"""
|
||||||
@@ -313,16 +317,16 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
p2 = Person(settings=NonAdminSettings(foo2="bar2")).save()
|
p2 = Person(settings=NonAdminSettings(foo2="bar2")).save()
|
||||||
|
|
||||||
# Test non exiting attribute
|
# Test non exiting attribute
|
||||||
with self.assertRaises(InvalidQueryError) as ctx_err:
|
with pytest.raises(InvalidQueryError) as exc_info:
|
||||||
Person.objects(settings__notexist="bar").first()
|
Person.objects(settings__notexist="bar").first()
|
||||||
self.assertEqual(unicode(ctx_err.exception), u'Cannot resolve field "notexist"')
|
assert str(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||||
|
|
||||||
with self.assertRaises(LookUpError):
|
with pytest.raises(LookUpError):
|
||||||
Person.objects.only("settings.notexist")
|
Person.objects.only("settings.notexist")
|
||||||
|
|
||||||
# Test existing attribute
|
# Test existing attribute
|
||||||
self.assertEqual(Person.objects(settings__foo1="bar1").first().id, p1.id)
|
assert Person.objects(settings__foo1="bar1").first().id == p1.id
|
||||||
self.assertEqual(Person.objects(settings__foo2="bar2").first().id, p2.id)
|
assert Person.objects(settings__foo2="bar2").first().id == p2.id
|
||||||
|
|
||||||
def test_query_generic_embedded_document_attribute_with_inheritance(self):
|
def test_query_generic_embedded_document_attribute_with_inheritance(self):
|
||||||
class BaseSettings(EmbeddedDocument):
|
class BaseSettings(EmbeddedDocument):
|
||||||
@@ -341,10 +345,10 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
|
|||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
# Test non exiting attribute
|
# Test non exiting attribute
|
||||||
with self.assertRaises(InvalidQueryError) as ctx_err:
|
with pytest.raises(InvalidQueryError) as exc_info:
|
||||||
self.assertEqual(Person.objects(settings__notexist="bar").first().id, p.id)
|
assert Person.objects(settings__notexist="bar").first().id == p.id
|
||||||
self.assertEqual(unicode(ctx_err.exception), u'Cannot resolve field "notexist"')
|
assert str(exc_info.value) == u'Cannot resolve field "notexist"'
|
||||||
|
|
||||||
# Test existing attribute
|
# Test existing attribute
|
||||||
self.assertEqual(Person.objects(settings__base_foo="basefoo").first().id, p.id)
|
assert Person.objects(settings__base_foo="basefoo").first().id == p.id
|
||||||
self.assertEqual(Person.objects(settings__sub_foo="subfoo").first().id, p.id)
|
assert Person.objects(settings__sub_foo="subfoo").first().id == p.id
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,15 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import copy
|
import copy
|
||||||
import os
|
import os
|
||||||
import unittest
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
import gridfs
|
import gridfs
|
||||||
import six
|
import pytest
|
||||||
|
|
||||||
from nose.plugins.skip import SkipTest
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.connection import get_db
|
from mongoengine.connection import get_db
|
||||||
from mongoengine.python_support import StringIO
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -21,6 +20,8 @@ except ImportError:
|
|||||||
|
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
require_pil = pytest.mark.skipif(not HAS_PIL, reason="PIL not installed")
|
||||||
|
|
||||||
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), "mongoengine.png")
|
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), "mongoengine.png")
|
||||||
TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), "mongodb_leaf.png")
|
TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), "mongodb_leaf.png")
|
||||||
|
|
||||||
@@ -28,14 +29,14 @@ TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), "mongodb_leaf.png")
|
|||||||
def get_file(path):
|
def get_file(path):
|
||||||
"""Use a BytesIO instead of a file to allow
|
"""Use a BytesIO instead of a file to allow
|
||||||
to have a one-liner and avoid that the file remains opened"""
|
to have a one-liner and avoid that the file remains opened"""
|
||||||
bytes_io = StringIO()
|
bytes_io = BytesIO()
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
bytes_io.write(f.read())
|
bytes_io.write(f.read())
|
||||||
bytes_io.seek(0)
|
bytes_io.seek(0)
|
||||||
return bytes_io
|
return bytes_io
|
||||||
|
|
||||||
|
|
||||||
class FileTest(MongoDBTestCase):
|
class TestFileField(MongoDBTestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.db.drop_collection("fs.files")
|
self.db.drop_collection("fs.files")
|
||||||
self.db.drop_collection("fs.chunks")
|
self.db.drop_collection("fs.chunks")
|
||||||
@@ -56,7 +57,7 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
PutFile.drop_collection()
|
PutFile.drop_collection()
|
||||||
|
|
||||||
text = six.b("Hello, World!")
|
text = "Hello, World!".encode("latin-1")
|
||||||
content_type = "text/plain"
|
content_type = "text/plain"
|
||||||
|
|
||||||
putfile = PutFile()
|
putfile = PutFile()
|
||||||
@@ -64,13 +65,13 @@ class FileTest(MongoDBTestCase):
|
|||||||
putfile.save()
|
putfile.save()
|
||||||
|
|
||||||
result = PutFile.objects.first()
|
result = PutFile.objects.first()
|
||||||
self.assertEqual(putfile, result)
|
assert putfile == result
|
||||||
self.assertEqual(
|
assert (
|
||||||
"%s" % result.the_file,
|
"%s" % result.the_file
|
||||||
"<GridFSProxy: hello (%s)>" % result.the_file.grid_id,
|
== "<GridFSProxy: hello (%s)>" % result.the_file.grid_id
|
||||||
)
|
)
|
||||||
self.assertEqual(result.the_file.read(), text)
|
assert result.the_file.read() == text
|
||||||
self.assertEqual(result.the_file.content_type, content_type)
|
assert result.the_file.content_type == content_type
|
||||||
result.the_file.delete() # Remove file from GridFS
|
result.the_file.delete() # Remove file from GridFS
|
||||||
PutFile.objects.delete()
|
PutFile.objects.delete()
|
||||||
|
|
||||||
@@ -78,16 +79,16 @@ class FileTest(MongoDBTestCase):
|
|||||||
PutFile.drop_collection()
|
PutFile.drop_collection()
|
||||||
|
|
||||||
putfile = PutFile()
|
putfile = PutFile()
|
||||||
putstring = StringIO()
|
putstring = BytesIO()
|
||||||
putstring.write(text)
|
putstring.write(text)
|
||||||
putstring.seek(0)
|
putstring.seek(0)
|
||||||
putfile.the_file.put(putstring, content_type=content_type)
|
putfile.the_file.put(putstring, content_type=content_type)
|
||||||
putfile.save()
|
putfile.save()
|
||||||
|
|
||||||
result = PutFile.objects.first()
|
result = PutFile.objects.first()
|
||||||
self.assertEqual(putfile, result)
|
assert putfile == result
|
||||||
self.assertEqual(result.the_file.read(), text)
|
assert result.the_file.read() == text
|
||||||
self.assertEqual(result.the_file.content_type, content_type)
|
assert result.the_file.content_type == content_type
|
||||||
result.the_file.delete()
|
result.the_file.delete()
|
||||||
|
|
||||||
def test_file_fields_stream(self):
|
def test_file_fields_stream(self):
|
||||||
@@ -99,8 +100,8 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
StreamFile.drop_collection()
|
StreamFile.drop_collection()
|
||||||
|
|
||||||
text = six.b("Hello, World!")
|
text = "Hello, World!".encode("latin-1")
|
||||||
more_text = six.b("Foo Bar")
|
more_text = "Foo Bar".encode("latin-1")
|
||||||
content_type = "text/plain"
|
content_type = "text/plain"
|
||||||
|
|
||||||
streamfile = StreamFile()
|
streamfile = StreamFile()
|
||||||
@@ -111,19 +112,19 @@ class FileTest(MongoDBTestCase):
|
|||||||
streamfile.save()
|
streamfile.save()
|
||||||
|
|
||||||
result = StreamFile.objects.first()
|
result = StreamFile.objects.first()
|
||||||
self.assertEqual(streamfile, result)
|
assert streamfile == result
|
||||||
self.assertEqual(result.the_file.read(), text + more_text)
|
assert result.the_file.read() == text + more_text
|
||||||
self.assertEqual(result.the_file.content_type, content_type)
|
assert result.the_file.content_type == content_type
|
||||||
result.the_file.seek(0)
|
result.the_file.seek(0)
|
||||||
self.assertEqual(result.the_file.tell(), 0)
|
assert result.the_file.tell() == 0
|
||||||
self.assertEqual(result.the_file.read(len(text)), text)
|
assert result.the_file.read(len(text)) == text
|
||||||
self.assertEqual(result.the_file.tell(), len(text))
|
assert result.the_file.tell() == len(text)
|
||||||
self.assertEqual(result.the_file.read(len(more_text)), more_text)
|
assert result.the_file.read(len(more_text)) == more_text
|
||||||
self.assertEqual(result.the_file.tell(), len(text + more_text))
|
assert result.the_file.tell() == len(text + more_text)
|
||||||
result.the_file.delete()
|
result.the_file.delete()
|
||||||
|
|
||||||
# Ensure deleted file returns None
|
# Ensure deleted file returns None
|
||||||
self.assertTrue(result.the_file.read() is None)
|
assert result.the_file.read() is None
|
||||||
|
|
||||||
def test_file_fields_stream_after_none(self):
|
def test_file_fields_stream_after_none(self):
|
||||||
"""Ensure that a file field can be written to after it has been saved as
|
"""Ensure that a file field can be written to after it has been saved as
|
||||||
@@ -135,9 +136,8 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
StreamFile.drop_collection()
|
StreamFile.drop_collection()
|
||||||
|
|
||||||
text = six.b("Hello, World!")
|
text = "Hello, World!".encode("latin-1")
|
||||||
more_text = six.b("Foo Bar")
|
more_text = "Foo Bar".encode("latin-1")
|
||||||
content_type = "text/plain"
|
|
||||||
|
|
||||||
streamfile = StreamFile()
|
streamfile = StreamFile()
|
||||||
streamfile.save()
|
streamfile.save()
|
||||||
@@ -148,26 +148,26 @@ class FileTest(MongoDBTestCase):
|
|||||||
streamfile.save()
|
streamfile.save()
|
||||||
|
|
||||||
result = StreamFile.objects.first()
|
result = StreamFile.objects.first()
|
||||||
self.assertEqual(streamfile, result)
|
assert streamfile == result
|
||||||
self.assertEqual(result.the_file.read(), text + more_text)
|
assert result.the_file.read() == text + more_text
|
||||||
# self.assertEqual(result.the_file.content_type, content_type)
|
# assert result.the_file.content_type == content_type
|
||||||
result.the_file.seek(0)
|
result.the_file.seek(0)
|
||||||
self.assertEqual(result.the_file.tell(), 0)
|
assert result.the_file.tell() == 0
|
||||||
self.assertEqual(result.the_file.read(len(text)), text)
|
assert result.the_file.read(len(text)) == text
|
||||||
self.assertEqual(result.the_file.tell(), len(text))
|
assert result.the_file.tell() == len(text)
|
||||||
self.assertEqual(result.the_file.read(len(more_text)), more_text)
|
assert result.the_file.read(len(more_text)) == more_text
|
||||||
self.assertEqual(result.the_file.tell(), len(text + more_text))
|
assert result.the_file.tell() == len(text + more_text)
|
||||||
result.the_file.delete()
|
result.the_file.delete()
|
||||||
|
|
||||||
# Ensure deleted file returns None
|
# Ensure deleted file returns None
|
||||||
self.assertTrue(result.the_file.read() is None)
|
assert result.the_file.read() is None
|
||||||
|
|
||||||
def test_file_fields_set(self):
|
def test_file_fields_set(self):
|
||||||
class SetFile(Document):
|
class SetFile(Document):
|
||||||
the_file = FileField()
|
the_file = FileField()
|
||||||
|
|
||||||
text = six.b("Hello, World!")
|
text = "Hello, World!".encode("latin-1")
|
||||||
more_text = six.b("Foo Bar")
|
more_text = "Foo Bar".encode("latin-1")
|
||||||
|
|
||||||
SetFile.drop_collection()
|
SetFile.drop_collection()
|
||||||
|
|
||||||
@@ -176,16 +176,16 @@ class FileTest(MongoDBTestCase):
|
|||||||
setfile.save()
|
setfile.save()
|
||||||
|
|
||||||
result = SetFile.objects.first()
|
result = SetFile.objects.first()
|
||||||
self.assertEqual(setfile, result)
|
assert setfile == result
|
||||||
self.assertEqual(result.the_file.read(), text)
|
assert result.the_file.read() == text
|
||||||
|
|
||||||
# Try replacing file with new one
|
# Try replacing file with new one
|
||||||
result.the_file.replace(more_text)
|
result.the_file.replace(more_text)
|
||||||
result.save()
|
result.save()
|
||||||
|
|
||||||
result = SetFile.objects.first()
|
result = SetFile.objects.first()
|
||||||
self.assertEqual(setfile, result)
|
assert setfile == result
|
||||||
self.assertEqual(result.the_file.read(), more_text)
|
assert result.the_file.read() == more_text
|
||||||
result.the_file.delete()
|
result.the_file.delete()
|
||||||
|
|
||||||
def test_file_field_no_default(self):
|
def test_file_field_no_default(self):
|
||||||
@@ -195,7 +195,7 @@ class FileTest(MongoDBTestCase):
|
|||||||
GridDocument.drop_collection()
|
GridDocument.drop_collection()
|
||||||
|
|
||||||
with tempfile.TemporaryFile() as f:
|
with tempfile.TemporaryFile() as f:
|
||||||
f.write(six.b("Hello World!"))
|
f.write("Hello World!".encode("latin-1"))
|
||||||
f.flush()
|
f.flush()
|
||||||
|
|
||||||
# Test without default
|
# Test without default
|
||||||
@@ -205,28 +205,28 @@ class FileTest(MongoDBTestCase):
|
|||||||
doc_b = GridDocument.objects.with_id(doc_a.id)
|
doc_b = GridDocument.objects.with_id(doc_a.id)
|
||||||
doc_b.the_file.replace(f, filename="doc_b")
|
doc_b.the_file.replace(f, filename="doc_b")
|
||||||
doc_b.save()
|
doc_b.save()
|
||||||
self.assertNotEqual(doc_b.the_file.grid_id, None)
|
assert doc_b.the_file.grid_id is not None
|
||||||
|
|
||||||
# Test it matches
|
# Test it matches
|
||||||
doc_c = GridDocument.objects.with_id(doc_b.id)
|
doc_c = GridDocument.objects.with_id(doc_b.id)
|
||||||
self.assertEqual(doc_b.the_file.grid_id, doc_c.the_file.grid_id)
|
assert doc_b.the_file.grid_id == doc_c.the_file.grid_id
|
||||||
|
|
||||||
# Test with default
|
# Test with default
|
||||||
doc_d = GridDocument(the_file=six.b(""))
|
doc_d = GridDocument(the_file="".encode("latin-1"))
|
||||||
doc_d.save()
|
doc_d.save()
|
||||||
|
|
||||||
doc_e = GridDocument.objects.with_id(doc_d.id)
|
doc_e = GridDocument.objects.with_id(doc_d.id)
|
||||||
self.assertEqual(doc_d.the_file.grid_id, doc_e.the_file.grid_id)
|
assert doc_d.the_file.grid_id == doc_e.the_file.grid_id
|
||||||
|
|
||||||
doc_e.the_file.replace(f, filename="doc_e")
|
doc_e.the_file.replace(f, filename="doc_e")
|
||||||
doc_e.save()
|
doc_e.save()
|
||||||
|
|
||||||
doc_f = GridDocument.objects.with_id(doc_e.id)
|
doc_f = GridDocument.objects.with_id(doc_e.id)
|
||||||
self.assertEqual(doc_e.the_file.grid_id, doc_f.the_file.grid_id)
|
assert doc_e.the_file.grid_id == doc_f.the_file.grid_id
|
||||||
|
|
||||||
db = GridDocument._get_db()
|
db = GridDocument._get_db()
|
||||||
grid_fs = gridfs.GridFS(db)
|
grid_fs = gridfs.GridFS(db)
|
||||||
self.assertEqual(["doc_b", "doc_e"], grid_fs.list())
|
assert ["doc_b", "doc_e"] == grid_fs.list()
|
||||||
|
|
||||||
def test_file_uniqueness(self):
|
def test_file_uniqueness(self):
|
||||||
"""Ensure that each instance of a FileField is unique
|
"""Ensure that each instance of a FileField is unique
|
||||||
@@ -239,15 +239,15 @@ class FileTest(MongoDBTestCase):
|
|||||||
# First instance
|
# First instance
|
||||||
test_file = TestFile()
|
test_file = TestFile()
|
||||||
test_file.name = "Hello, World!"
|
test_file.name = "Hello, World!"
|
||||||
test_file.the_file.put(six.b("Hello, World!"))
|
test_file.the_file.put("Hello, World!".encode("latin-1"))
|
||||||
test_file.save()
|
test_file.save()
|
||||||
|
|
||||||
# Second instance
|
# Second instance
|
||||||
test_file_dupe = TestFile()
|
test_file_dupe = TestFile()
|
||||||
data = test_file_dupe.the_file.read() # Should be None
|
data = test_file_dupe.the_file.read() # Should be None
|
||||||
|
|
||||||
self.assertNotEqual(test_file.name, test_file_dupe.name)
|
assert test_file.name != test_file_dupe.name
|
||||||
self.assertNotEqual(test_file.the_file.read(), data)
|
assert test_file.the_file.read() != data
|
||||||
|
|
||||||
TestFile.drop_collection()
|
TestFile.drop_collection()
|
||||||
|
|
||||||
@@ -268,8 +268,8 @@ class FileTest(MongoDBTestCase):
|
|||||||
marmot.save()
|
marmot.save()
|
||||||
|
|
||||||
marmot = Animal.objects.get()
|
marmot = Animal.objects.get()
|
||||||
self.assertEqual(marmot.photo.content_type, "image/jpeg")
|
assert marmot.photo.content_type == "image/jpeg"
|
||||||
self.assertEqual(marmot.photo.foo, "bar")
|
assert marmot.photo.foo == "bar"
|
||||||
|
|
||||||
def test_file_reassigning(self):
|
def test_file_reassigning(self):
|
||||||
class TestFile(Document):
|
class TestFile(Document):
|
||||||
@@ -278,12 +278,12 @@ class FileTest(MongoDBTestCase):
|
|||||||
TestFile.drop_collection()
|
TestFile.drop_collection()
|
||||||
|
|
||||||
test_file = TestFile(the_file=get_file(TEST_IMAGE_PATH)).save()
|
test_file = TestFile(the_file=get_file(TEST_IMAGE_PATH)).save()
|
||||||
self.assertEqual(test_file.the_file.get().length, 8313)
|
assert test_file.the_file.get().length == 8313
|
||||||
|
|
||||||
test_file = TestFile.objects.first()
|
test_file = TestFile.objects.first()
|
||||||
test_file.the_file = get_file(TEST_IMAGE2_PATH)
|
test_file.the_file = get_file(TEST_IMAGE2_PATH)
|
||||||
test_file.save()
|
test_file.save()
|
||||||
self.assertEqual(test_file.the_file.get().length, 4971)
|
assert test_file.the_file.get().length == 4971
|
||||||
|
|
||||||
def test_file_boolean(self):
|
def test_file_boolean(self):
|
||||||
"""Ensure that a boolean test of a FileField indicates its presence
|
"""Ensure that a boolean test of a FileField indicates its presence
|
||||||
@@ -295,13 +295,15 @@ class FileTest(MongoDBTestCase):
|
|||||||
TestFile.drop_collection()
|
TestFile.drop_collection()
|
||||||
|
|
||||||
test_file = TestFile()
|
test_file = TestFile()
|
||||||
self.assertFalse(bool(test_file.the_file))
|
assert not bool(test_file.the_file)
|
||||||
test_file.the_file.put(six.b("Hello, World!"), content_type="text/plain")
|
test_file.the_file.put(
|
||||||
|
"Hello, World!".encode("latin-1"), content_type="text/plain"
|
||||||
|
)
|
||||||
test_file.save()
|
test_file.save()
|
||||||
self.assertTrue(bool(test_file.the_file))
|
assert bool(test_file.the_file)
|
||||||
|
|
||||||
test_file = TestFile.objects.first()
|
test_file = TestFile.objects.first()
|
||||||
self.assertEqual(test_file.the_file.content_type, "text/plain")
|
assert test_file.the_file.content_type == "text/plain"
|
||||||
|
|
||||||
def test_file_cmp(self):
|
def test_file_cmp(self):
|
||||||
"""Test comparing against other types"""
|
"""Test comparing against other types"""
|
||||||
@@ -310,7 +312,7 @@ class FileTest(MongoDBTestCase):
|
|||||||
the_file = FileField()
|
the_file = FileField()
|
||||||
|
|
||||||
test_file = TestFile()
|
test_file = TestFile()
|
||||||
self.assertNotIn(test_file.the_file, [{"test": 1}])
|
assert test_file.the_file not in [{"test": 1}]
|
||||||
|
|
||||||
def test_file_disk_space(self):
|
def test_file_disk_space(self):
|
||||||
""" Test disk space usage when we delete/replace a file """
|
""" Test disk space usage when we delete/replace a file """
|
||||||
@@ -318,7 +320,7 @@ class FileTest(MongoDBTestCase):
|
|||||||
class TestFile(Document):
|
class TestFile(Document):
|
||||||
the_file = FileField()
|
the_file = FileField()
|
||||||
|
|
||||||
text = six.b("Hello, World!")
|
text = "Hello, World!".encode("latin-1")
|
||||||
content_type = "text/plain"
|
content_type = "text/plain"
|
||||||
|
|
||||||
testfile = TestFile()
|
testfile = TestFile()
|
||||||
@@ -330,16 +332,16 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
files = db.fs.files.find()
|
files = db.fs.files.find()
|
||||||
chunks = db.fs.chunks.find()
|
chunks = db.fs.chunks.find()
|
||||||
self.assertEqual(len(list(files)), 1)
|
assert len(list(files)) == 1
|
||||||
self.assertEqual(len(list(chunks)), 1)
|
assert len(list(chunks)) == 1
|
||||||
|
|
||||||
# Deleting the docoument should delete the files
|
# Deleting the docoument should delete the files
|
||||||
testfile.delete()
|
testfile.delete()
|
||||||
|
|
||||||
files = db.fs.files.find()
|
files = db.fs.files.find()
|
||||||
chunks = db.fs.chunks.find()
|
chunks = db.fs.chunks.find()
|
||||||
self.assertEqual(len(list(files)), 0)
|
assert len(list(files)) == 0
|
||||||
self.assertEqual(len(list(chunks)), 0)
|
assert len(list(chunks)) == 0
|
||||||
|
|
||||||
# Test case where we don't store a file in the first place
|
# Test case where we don't store a file in the first place
|
||||||
testfile = TestFile()
|
testfile = TestFile()
|
||||||
@@ -347,48 +349,46 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
files = db.fs.files.find()
|
files = db.fs.files.find()
|
||||||
chunks = db.fs.chunks.find()
|
chunks = db.fs.chunks.find()
|
||||||
self.assertEqual(len(list(files)), 0)
|
assert len(list(files)) == 0
|
||||||
self.assertEqual(len(list(chunks)), 0)
|
assert len(list(chunks)) == 0
|
||||||
|
|
||||||
testfile.delete()
|
testfile.delete()
|
||||||
|
|
||||||
files = db.fs.files.find()
|
files = db.fs.files.find()
|
||||||
chunks = db.fs.chunks.find()
|
chunks = db.fs.chunks.find()
|
||||||
self.assertEqual(len(list(files)), 0)
|
assert len(list(files)) == 0
|
||||||
self.assertEqual(len(list(chunks)), 0)
|
assert len(list(chunks)) == 0
|
||||||
|
|
||||||
# Test case where we overwrite the file
|
# Test case where we overwrite the file
|
||||||
testfile = TestFile()
|
testfile = TestFile()
|
||||||
testfile.the_file.put(text, content_type=content_type, filename="hello")
|
testfile.the_file.put(text, content_type=content_type, filename="hello")
|
||||||
testfile.save()
|
testfile.save()
|
||||||
|
|
||||||
text = six.b("Bonjour, World!")
|
text = "Bonjour, World!".encode("latin-1")
|
||||||
testfile.the_file.replace(text, content_type=content_type, filename="hello")
|
testfile.the_file.replace(text, content_type=content_type, filename="hello")
|
||||||
testfile.save()
|
testfile.save()
|
||||||
|
|
||||||
files = db.fs.files.find()
|
files = db.fs.files.find()
|
||||||
chunks = db.fs.chunks.find()
|
chunks = db.fs.chunks.find()
|
||||||
self.assertEqual(len(list(files)), 1)
|
assert len(list(files)) == 1
|
||||||
self.assertEqual(len(list(chunks)), 1)
|
assert len(list(chunks)) == 1
|
||||||
|
|
||||||
testfile.delete()
|
testfile.delete()
|
||||||
|
|
||||||
files = db.fs.files.find()
|
files = db.fs.files.find()
|
||||||
chunks = db.fs.chunks.find()
|
chunks = db.fs.chunks.find()
|
||||||
self.assertEqual(len(list(files)), 0)
|
assert len(list(files)) == 0
|
||||||
self.assertEqual(len(list(chunks)), 0)
|
assert len(list(chunks)) == 0
|
||||||
|
|
||||||
|
@require_pil
|
||||||
def test_image_field(self):
|
def test_image_field(self):
|
||||||
if not HAS_PIL:
|
|
||||||
raise SkipTest("PIL not installed")
|
|
||||||
|
|
||||||
class TestImage(Document):
|
class TestImage(Document):
|
||||||
image = ImageField()
|
image = ImageField()
|
||||||
|
|
||||||
TestImage.drop_collection()
|
TestImage.drop_collection()
|
||||||
|
|
||||||
with tempfile.TemporaryFile() as f:
|
with tempfile.TemporaryFile() as f:
|
||||||
f.write(six.b("Hello World!"))
|
f.write("Hello World!".encode("latin-1"))
|
||||||
f.flush()
|
f.flush()
|
||||||
|
|
||||||
t = TestImage()
|
t = TestImage()
|
||||||
@@ -396,9 +396,7 @@ class FileTest(MongoDBTestCase):
|
|||||||
t.image.put(f)
|
t.image.put(f)
|
||||||
self.fail("Should have raised an invalidation error")
|
self.fail("Should have raised an invalidation error")
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertEqual(
|
assert "%s" % e == "Invalid image: cannot identify image file %s" % f
|
||||||
"%s" % e, "Invalid image: cannot identify image file %s" % f
|
|
||||||
)
|
|
||||||
|
|
||||||
t = TestImage()
|
t = TestImage()
|
||||||
t.image.put(get_file(TEST_IMAGE_PATH))
|
t.image.put(get_file(TEST_IMAGE_PATH))
|
||||||
@@ -406,35 +404,31 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
t = TestImage.objects.first()
|
t = TestImage.objects.first()
|
||||||
|
|
||||||
self.assertEqual(t.image.format, "PNG")
|
assert t.image.format == "PNG"
|
||||||
|
|
||||||
w, h = t.image.size
|
w, h = t.image.size
|
||||||
self.assertEqual(w, 371)
|
assert w == 371
|
||||||
self.assertEqual(h, 76)
|
assert h == 76
|
||||||
|
|
||||||
t.image.delete()
|
t.image.delete()
|
||||||
|
|
||||||
|
@require_pil
|
||||||
def test_image_field_reassigning(self):
|
def test_image_field_reassigning(self):
|
||||||
if not HAS_PIL:
|
|
||||||
raise SkipTest("PIL not installed")
|
|
||||||
|
|
||||||
class TestFile(Document):
|
class TestFile(Document):
|
||||||
the_file = ImageField()
|
the_file = ImageField()
|
||||||
|
|
||||||
TestFile.drop_collection()
|
TestFile.drop_collection()
|
||||||
|
|
||||||
test_file = TestFile(the_file=get_file(TEST_IMAGE_PATH)).save()
|
test_file = TestFile(the_file=get_file(TEST_IMAGE_PATH)).save()
|
||||||
self.assertEqual(test_file.the_file.size, (371, 76))
|
assert test_file.the_file.size == (371, 76)
|
||||||
|
|
||||||
test_file = TestFile.objects.first()
|
test_file = TestFile.objects.first()
|
||||||
test_file.the_file = get_file(TEST_IMAGE2_PATH)
|
test_file.the_file = get_file(TEST_IMAGE2_PATH)
|
||||||
test_file.save()
|
test_file.save()
|
||||||
self.assertEqual(test_file.the_file.size, (45, 101))
|
assert test_file.the_file.size == (45, 101)
|
||||||
|
|
||||||
|
@require_pil
|
||||||
def test_image_field_resize(self):
|
def test_image_field_resize(self):
|
||||||
if not HAS_PIL:
|
|
||||||
raise SkipTest("PIL not installed")
|
|
||||||
|
|
||||||
class TestImage(Document):
|
class TestImage(Document):
|
||||||
image = ImageField(size=(185, 37))
|
image = ImageField(size=(185, 37))
|
||||||
|
|
||||||
@@ -446,18 +440,16 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
t = TestImage.objects.first()
|
t = TestImage.objects.first()
|
||||||
|
|
||||||
self.assertEqual(t.image.format, "PNG")
|
assert t.image.format == "PNG"
|
||||||
w, h = t.image.size
|
w, h = t.image.size
|
||||||
|
|
||||||
self.assertEqual(w, 185)
|
assert w == 185
|
||||||
self.assertEqual(h, 37)
|
assert h == 37
|
||||||
|
|
||||||
t.image.delete()
|
t.image.delete()
|
||||||
|
|
||||||
|
@require_pil
|
||||||
def test_image_field_resize_force(self):
|
def test_image_field_resize_force(self):
|
||||||
if not HAS_PIL:
|
|
||||||
raise SkipTest("PIL not installed")
|
|
||||||
|
|
||||||
class TestImage(Document):
|
class TestImage(Document):
|
||||||
image = ImageField(size=(185, 37, True))
|
image = ImageField(size=(185, 37, True))
|
||||||
|
|
||||||
@@ -469,18 +461,16 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
t = TestImage.objects.first()
|
t = TestImage.objects.first()
|
||||||
|
|
||||||
self.assertEqual(t.image.format, "PNG")
|
assert t.image.format == "PNG"
|
||||||
w, h = t.image.size
|
w, h = t.image.size
|
||||||
|
|
||||||
self.assertEqual(w, 185)
|
assert w == 185
|
||||||
self.assertEqual(h, 37)
|
assert h == 37
|
||||||
|
|
||||||
t.image.delete()
|
t.image.delete()
|
||||||
|
|
||||||
|
@require_pil
|
||||||
def test_image_field_thumbnail(self):
|
def test_image_field_thumbnail(self):
|
||||||
if not HAS_PIL:
|
|
||||||
raise SkipTest("PIL not installed")
|
|
||||||
|
|
||||||
class TestImage(Document):
|
class TestImage(Document):
|
||||||
image = ImageField(thumbnail_size=(92, 18))
|
image = ImageField(thumbnail_size=(92, 18))
|
||||||
|
|
||||||
@@ -492,9 +482,9 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
t = TestImage.objects.first()
|
t = TestImage.objects.first()
|
||||||
|
|
||||||
self.assertEqual(t.image.thumbnail.format, "PNG")
|
assert t.image.thumbnail.format == "PNG"
|
||||||
self.assertEqual(t.image.thumbnail.width, 92)
|
assert t.image.thumbnail.width == 92
|
||||||
self.assertEqual(t.image.thumbnail.height, 18)
|
assert t.image.thumbnail.height == 18
|
||||||
|
|
||||||
t.image.delete()
|
t.image.delete()
|
||||||
|
|
||||||
@@ -514,21 +504,21 @@ class FileTest(MongoDBTestCase):
|
|||||||
# First instance
|
# First instance
|
||||||
test_file = TestFile()
|
test_file = TestFile()
|
||||||
test_file.name = "Hello, World!"
|
test_file.name = "Hello, World!"
|
||||||
test_file.the_file.put(six.b("Hello, World!"), name="hello.txt")
|
test_file.the_file.put("Hello, World!".encode("latin-1"), name="hello.txt")
|
||||||
test_file.save()
|
test_file.save()
|
||||||
|
|
||||||
data = get_db("test_files").macumba.files.find_one()
|
data = get_db("test_files").macumba.files.find_one()
|
||||||
self.assertEqual(data.get("name"), "hello.txt")
|
assert data.get("name") == "hello.txt"
|
||||||
|
|
||||||
test_file = TestFile.objects.first()
|
test_file = TestFile.objects.first()
|
||||||
self.assertEqual(test_file.the_file.read(), six.b("Hello, World!"))
|
assert test_file.the_file.read() == "Hello, World!".encode("latin-1")
|
||||||
|
|
||||||
test_file = TestFile.objects.first()
|
test_file = TestFile.objects.first()
|
||||||
test_file.the_file = six.b("HELLO, WORLD!")
|
test_file.the_file = "Hello, World!".encode("latin-1")
|
||||||
test_file.save()
|
test_file.save()
|
||||||
|
|
||||||
test_file = TestFile.objects.first()
|
test_file = TestFile.objects.first()
|
||||||
self.assertEqual(test_file.the_file.read(), six.b("HELLO, WORLD!"))
|
assert test_file.the_file.read() == "Hello, World!".encode("latin-1")
|
||||||
|
|
||||||
def test_copyable(self):
|
def test_copyable(self):
|
||||||
class PutFile(Document):
|
class PutFile(Document):
|
||||||
@@ -536,7 +526,7 @@ class FileTest(MongoDBTestCase):
|
|||||||
|
|
||||||
PutFile.drop_collection()
|
PutFile.drop_collection()
|
||||||
|
|
||||||
text = six.b("Hello, World!")
|
text = "Hello, World!".encode("latin-1")
|
||||||
content_type = "text/plain"
|
content_type = "text/plain"
|
||||||
|
|
||||||
putfile = PutFile()
|
putfile = PutFile()
|
||||||
@@ -546,14 +536,11 @@ class FileTest(MongoDBTestCase):
|
|||||||
class TestFile(Document):
|
class TestFile(Document):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
self.assertEqual(putfile, copy.copy(putfile))
|
assert putfile == copy.copy(putfile)
|
||||||
self.assertEqual(putfile, copy.deepcopy(putfile))
|
assert putfile == copy.deepcopy(putfile)
|
||||||
|
|
||||||
|
@require_pil
|
||||||
def test_get_image_by_grid_id(self):
|
def test_get_image_by_grid_id(self):
|
||||||
|
|
||||||
if not HAS_PIL:
|
|
||||||
raise SkipTest("PIL not installed")
|
|
||||||
|
|
||||||
class TestImage(Document):
|
class TestImage(Document):
|
||||||
|
|
||||||
image1 = ImageField()
|
image1 = ImageField()
|
||||||
@@ -569,9 +556,7 @@ class FileTest(MongoDBTestCase):
|
|||||||
test = TestImage.objects.first()
|
test = TestImage.objects.first()
|
||||||
grid_id = test.image1.grid_id
|
grid_id = test.image1.grid_id
|
||||||
|
|
||||||
self.assertEqual(
|
assert 1 == TestImage.objects(Q(image1=grid_id) or Q(image2=grid_id)).count()
|
||||||
1, TestImage.objects(Q(image1=grid_id) or Q(image2=grid_id)).count()
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_complex_field_filefield(self):
|
def test_complex_field_filefield(self):
|
||||||
"""Ensure you can add meta data to file"""
|
"""Ensure you can add meta data to file"""
|
||||||
@@ -593,9 +578,9 @@ class FileTest(MongoDBTestCase):
|
|||||||
marmot.save()
|
marmot.save()
|
||||||
|
|
||||||
marmot = Animal.objects.get()
|
marmot = Animal.objects.get()
|
||||||
self.assertEqual(marmot.photos[0].content_type, "image/jpeg")
|
assert marmot.photos[0].content_type == "image/jpeg"
|
||||||
self.assertEqual(marmot.photos[0].foo, "bar")
|
assert marmot.photos[0].foo == "bar"
|
||||||
self.assertEqual(marmot.photos[0].get().length, 8313)
|
assert marmot.photos[0].get().length == 8313
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import six
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ class TestFloatField(MongoDBTestCase):
|
|||||||
TestDocument(float_fld=None).save()
|
TestDocument(float_fld=None).save()
|
||||||
TestDocument(float_fld=1).save()
|
TestDocument(float_fld=1).save()
|
||||||
|
|
||||||
self.assertEqual(1, TestDocument.objects(float_fld__ne=None).count())
|
assert 1 == TestDocument.objects(float_fld__ne=None).count()
|
||||||
self.assertEqual(1, TestDocument.objects(float_fld__ne=1).count())
|
assert 1 == TestDocument.objects(float_fld__ne=1).count()
|
||||||
|
|
||||||
def test_validation(self):
|
def test_validation(self):
|
||||||
"""Ensure that invalid values cannot be assigned to float fields.
|
"""Ensure that invalid values cannot be assigned to float fields.
|
||||||
@@ -34,25 +34,29 @@ class TestFloatField(MongoDBTestCase):
|
|||||||
person.validate()
|
person.validate()
|
||||||
|
|
||||||
person.height = "2.0"
|
person.height = "2.0"
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
person.height = 0.01
|
person.height = 0.01
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
person.height = 4.0
|
person.height = 4.0
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
person_2 = Person(height="something invalid")
|
person_2 = Person(height="something invalid")
|
||||||
self.assertRaises(ValidationError, person_2.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person_2.validate()
|
||||||
|
|
||||||
big_person = BigPerson()
|
big_person = BigPerson()
|
||||||
|
|
||||||
for value, value_type in enumerate(six.integer_types):
|
big_person.height = int(0)
|
||||||
big_person.height = value_type(value)
|
big_person.validate()
|
||||||
big_person.validate()
|
|
||||||
|
|
||||||
big_person.height = 2 ** 500
|
big_person.height = 2 ** 500
|
||||||
big_person.validate()
|
big_person.validate()
|
||||||
|
|
||||||
big_person.height = 2 ** 100000 # Too big for a float value
|
big_person.height = 2 ** 100000 # Too big for a float value
|
||||||
self.assertRaises(ValidationError, big_person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
big_person.validate()
|
||||||
|
|||||||
@@ -2,22 +2,16 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.connection import get_db
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
__all__ = ("GeoFieldTest",)
|
|
||||||
|
|
||||||
|
|
||||||
class GeoFieldTest(unittest.TestCase):
|
class TestGeoField(MongoDBTestCase):
|
||||||
def setUp(self):
|
|
||||||
connect(db="mongoenginetest")
|
|
||||||
self.db = get_db()
|
|
||||||
|
|
||||||
def _test_for_expected_error(self, Cls, loc, expected):
|
def _test_for_expected_error(self, Cls, loc, expected):
|
||||||
try:
|
try:
|
||||||
Cls(loc=loc).validate()
|
Cls(loc=loc).validate()
|
||||||
self.fail("Should not validate the location {0}".format(loc))
|
self.fail("Should not validate the location {0}".format(loc))
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertEqual(expected, e.to_dict()["loc"])
|
assert expected == e.to_dict()["loc"]
|
||||||
|
|
||||||
def test_geopoint_validation(self):
|
def test_geopoint_validation(self):
|
||||||
class Location(Document):
|
class Location(Document):
|
||||||
@@ -305,7 +299,7 @@ class GeoFieldTest(unittest.TestCase):
|
|||||||
location = GeoPointField()
|
location = GeoPointField()
|
||||||
|
|
||||||
geo_indicies = Event._geo_indices()
|
geo_indicies = Event._geo_indices()
|
||||||
self.assertEqual(geo_indicies, [{"fields": [("location", "2d")]}])
|
assert geo_indicies == [{"fields": [("location", "2d")]}]
|
||||||
|
|
||||||
def test_geopoint_embedded_indexes(self):
|
def test_geopoint_embedded_indexes(self):
|
||||||
"""Ensure that indexes are created automatically for GeoPointFields on
|
"""Ensure that indexes are created automatically for GeoPointFields on
|
||||||
@@ -321,7 +315,7 @@ class GeoFieldTest(unittest.TestCase):
|
|||||||
venue = EmbeddedDocumentField(Venue)
|
venue = EmbeddedDocumentField(Venue)
|
||||||
|
|
||||||
geo_indicies = Event._geo_indices()
|
geo_indicies = Event._geo_indices()
|
||||||
self.assertEqual(geo_indicies, [{"fields": [("venue.location", "2d")]}])
|
assert geo_indicies == [{"fields": [("venue.location", "2d")]}]
|
||||||
|
|
||||||
def test_indexes_2dsphere(self):
|
def test_indexes_2dsphere(self):
|
||||||
"""Ensure that indexes are created automatically for GeoPointFields.
|
"""Ensure that indexes are created automatically for GeoPointFields.
|
||||||
@@ -334,9 +328,9 @@ class GeoFieldTest(unittest.TestCase):
|
|||||||
polygon = PolygonField()
|
polygon = PolygonField()
|
||||||
|
|
||||||
geo_indicies = Event._geo_indices()
|
geo_indicies = Event._geo_indices()
|
||||||
self.assertIn({"fields": [("line", "2dsphere")]}, geo_indicies)
|
assert {"fields": [("line", "2dsphere")]} in geo_indicies
|
||||||
self.assertIn({"fields": [("polygon", "2dsphere")]}, geo_indicies)
|
assert {"fields": [("polygon", "2dsphere")]} in geo_indicies
|
||||||
self.assertIn({"fields": [("point", "2dsphere")]}, geo_indicies)
|
assert {"fields": [("point", "2dsphere")]} in geo_indicies
|
||||||
|
|
||||||
def test_indexes_2dsphere_embedded(self):
|
def test_indexes_2dsphere_embedded(self):
|
||||||
"""Ensure that indexes are created automatically for GeoPointFields.
|
"""Ensure that indexes are created automatically for GeoPointFields.
|
||||||
@@ -353,9 +347,9 @@ class GeoFieldTest(unittest.TestCase):
|
|||||||
venue = EmbeddedDocumentField(Venue)
|
venue = EmbeddedDocumentField(Venue)
|
||||||
|
|
||||||
geo_indicies = Event._geo_indices()
|
geo_indicies = Event._geo_indices()
|
||||||
self.assertIn({"fields": [("venue.line", "2dsphere")]}, geo_indicies)
|
assert {"fields": [("venue.line", "2dsphere")]} in geo_indicies
|
||||||
self.assertIn({"fields": [("venue.polygon", "2dsphere")]}, geo_indicies)
|
assert {"fields": [("venue.polygon", "2dsphere")]} in geo_indicies
|
||||||
self.assertIn({"fields": [("venue.point", "2dsphere")]}, geo_indicies)
|
assert {"fields": [("venue.point", "2dsphere")]} in geo_indicies
|
||||||
|
|
||||||
def test_geo_indexes_recursion(self):
|
def test_geo_indexes_recursion(self):
|
||||||
class Location(Document):
|
class Location(Document):
|
||||||
@@ -371,12 +365,12 @@ class GeoFieldTest(unittest.TestCase):
|
|||||||
|
|
||||||
Parent(name="Berlin").save()
|
Parent(name="Berlin").save()
|
||||||
info = Parent._get_collection().index_information()
|
info = Parent._get_collection().index_information()
|
||||||
self.assertNotIn("location_2d", info)
|
assert "location_2d" not in info
|
||||||
info = Location._get_collection().index_information()
|
info = Location._get_collection().index_information()
|
||||||
self.assertIn("location_2d", info)
|
assert "location_2d" in info
|
||||||
|
|
||||||
self.assertEqual(len(Parent._geo_indices()), 0)
|
assert len(Parent._geo_indices()) == 0
|
||||||
self.assertEqual(len(Location._geo_indices()), 1)
|
assert len(Location._geo_indices()) == 1
|
||||||
|
|
||||||
def test_geo_indexes_auto_index(self):
|
def test_geo_indexes_auto_index(self):
|
||||||
|
|
||||||
@@ -387,16 +381,16 @@ class GeoFieldTest(unittest.TestCase):
|
|||||||
|
|
||||||
meta = {"indexes": [[("location", "2dsphere"), ("datetime", 1)]]}
|
meta = {"indexes": [[("location", "2dsphere"), ("datetime", 1)]]}
|
||||||
|
|
||||||
self.assertEqual([], Log._geo_indices())
|
assert [] == Log._geo_indices()
|
||||||
|
|
||||||
Log.drop_collection()
|
Log.drop_collection()
|
||||||
Log.ensure_indexes()
|
Log.ensure_indexes()
|
||||||
|
|
||||||
info = Log._get_collection().index_information()
|
info = Log._get_collection().index_information()
|
||||||
self.assertEqual(
|
assert info["location_2dsphere_datetime_1"]["key"] == [
|
||||||
info["location_2dsphere_datetime_1"]["key"],
|
("location", "2dsphere"),
|
||||||
[("location", "2dsphere"), ("datetime", 1)],
|
("datetime", 1),
|
||||||
)
|
]
|
||||||
|
|
||||||
# Test listing explicitly
|
# Test listing explicitly
|
||||||
class Log(Document):
|
class Log(Document):
|
||||||
@@ -407,16 +401,16 @@ class GeoFieldTest(unittest.TestCase):
|
|||||||
"indexes": [{"fields": [("location", "2dsphere"), ("datetime", 1)]}]
|
"indexes": [{"fields": [("location", "2dsphere"), ("datetime", 1)]}]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assertEqual([], Log._geo_indices())
|
assert [] == Log._geo_indices()
|
||||||
|
|
||||||
Log.drop_collection()
|
Log.drop_collection()
|
||||||
Log.ensure_indexes()
|
Log.ensure_indexes()
|
||||||
|
|
||||||
info = Log._get_collection().index_information()
|
info = Log._get_collection().index_information()
|
||||||
self.assertEqual(
|
assert info["location_2dsphere_datetime_1"]["key"] == [
|
||||||
info["location_2dsphere_datetime_1"]["key"],
|
("location", "2dsphere"),
|
||||||
[("location", "2dsphere"), ("datetime", 1)],
|
("datetime", 1),
|
||||||
)
|
]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
|
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
@@ -23,11 +25,14 @@ class TestIntField(MongoDBTestCase):
|
|||||||
person.validate()
|
person.validate()
|
||||||
|
|
||||||
person.age = -1
|
person.age = -1
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
person.age = 120
|
person.age = 120
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
person.age = "ten"
|
person.age = "ten"
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
def test_ne_operator(self):
|
def test_ne_operator(self):
|
||||||
class TestDocument(Document):
|
class TestDocument(Document):
|
||||||
@@ -38,5 +43,5 @@ class TestIntField(MongoDBTestCase):
|
|||||||
TestDocument(int_fld=None).save()
|
TestDocument(int_fld=None).save()
|
||||||
TestDocument(int_fld=1).save()
|
TestDocument(int_fld=1).save()
|
||||||
|
|
||||||
self.assertEqual(1, TestDocument.objects(int_fld__ne=None).count())
|
assert 1 == TestDocument.objects(int_fld__ne=None).count()
|
||||||
self.assertEqual(1, TestDocument.objects(int_fld__ne=1).count())
|
assert 1 == TestDocument.objects(int_fld__ne=1).count()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from bson import DBRef, ObjectId
|
from bson import DBRef, ObjectId
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.base import LazyReference
|
from mongoengine.base import LazyReference
|
||||||
@@ -11,7 +12,8 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
def test_lazy_reference_config(self):
|
def test_lazy_reference_config(self):
|
||||||
# Make sure ReferenceField only accepts a document class or a string
|
# Make sure ReferenceField only accepts a document class or a string
|
||||||
# with a document class name.
|
# with a document class name.
|
||||||
self.assertRaises(ValidationError, LazyReferenceField, EmbeddedDocument)
|
with pytest.raises(ValidationError):
|
||||||
|
LazyReferenceField(EmbeddedDocument)
|
||||||
|
|
||||||
def test___repr__(self):
|
def test___repr__(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -25,7 +27,7 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
animal = Animal()
|
animal = Animal()
|
||||||
oc = Ocurrence(animal=animal)
|
oc = Ocurrence(animal=animal)
|
||||||
self.assertIn("LazyReference", repr(oc.animal))
|
assert "LazyReference" in repr(oc.animal)
|
||||||
|
|
||||||
def test___getattr___unknown_attr_raises_attribute_error(self):
|
def test___getattr___unknown_attr_raises_attribute_error(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -39,7 +41,7 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
animal = Animal().save()
|
animal = Animal().save()
|
||||||
oc = Ocurrence(animal=animal)
|
oc = Ocurrence(animal=animal)
|
||||||
with self.assertRaises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
oc.animal.not_exist
|
oc.animal.not_exist
|
||||||
|
|
||||||
def test_lazy_reference_simple(self):
|
def test_lazy_reference_simple(self):
|
||||||
@@ -57,19 +59,19 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
animal = Animal(name="Leopard", tag="heavy").save()
|
animal = Animal(name="Leopard", tag="heavy").save()
|
||||||
Ocurrence(person="test", animal=animal).save()
|
Ocurrence(person="test", animal=animal).save()
|
||||||
p = Ocurrence.objects.get()
|
p = Ocurrence.objects.get()
|
||||||
self.assertIsInstance(p.animal, LazyReference)
|
assert isinstance(p.animal, LazyReference)
|
||||||
fetched_animal = p.animal.fetch()
|
fetched_animal = p.animal.fetch()
|
||||||
self.assertEqual(fetched_animal, animal)
|
assert fetched_animal == animal
|
||||||
# `fetch` keep cache on referenced document by default...
|
# `fetch` keep cache on referenced document by default...
|
||||||
animal.tag = "not so heavy"
|
animal.tag = "not so heavy"
|
||||||
animal.save()
|
animal.save()
|
||||||
double_fetch = p.animal.fetch()
|
double_fetch = p.animal.fetch()
|
||||||
self.assertIs(fetched_animal, double_fetch)
|
assert fetched_animal is double_fetch
|
||||||
self.assertEqual(double_fetch.tag, "heavy")
|
assert double_fetch.tag == "heavy"
|
||||||
# ...unless specified otherwise
|
# ...unless specified otherwise
|
||||||
fetch_force = p.animal.fetch(force=True)
|
fetch_force = p.animal.fetch(force=True)
|
||||||
self.assertIsNot(fetch_force, fetched_animal)
|
assert fetch_force is not fetched_animal
|
||||||
self.assertEqual(fetch_force.tag, "not so heavy")
|
assert fetch_force.tag == "not so heavy"
|
||||||
|
|
||||||
def test_lazy_reference_fetch_invalid_ref(self):
|
def test_lazy_reference_fetch_invalid_ref(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -87,8 +89,8 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
Ocurrence(person="test", animal=animal).save()
|
Ocurrence(person="test", animal=animal).save()
|
||||||
animal.delete()
|
animal.delete()
|
||||||
p = Ocurrence.objects.get()
|
p = Ocurrence.objects.get()
|
||||||
self.assertIsInstance(p.animal, LazyReference)
|
assert isinstance(p.animal, LazyReference)
|
||||||
with self.assertRaises(DoesNotExist):
|
with pytest.raises(DoesNotExist):
|
||||||
p.animal.fetch()
|
p.animal.fetch()
|
||||||
|
|
||||||
def test_lazy_reference_set(self):
|
def test_lazy_reference_set(self):
|
||||||
@@ -122,7 +124,7 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
):
|
):
|
||||||
p = Ocurrence(person="test", animal=ref).save()
|
p = Ocurrence(person="test", animal=ref).save()
|
||||||
p.reload()
|
p.reload()
|
||||||
self.assertIsInstance(p.animal, LazyReference)
|
assert isinstance(p.animal, LazyReference)
|
||||||
p.animal.fetch()
|
p.animal.fetch()
|
||||||
|
|
||||||
def test_lazy_reference_bad_set(self):
|
def test_lazy_reference_bad_set(self):
|
||||||
@@ -149,8 +151,8 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
DBRef(baddoc._get_collection_name(), animal.pk),
|
DBRef(baddoc._get_collection_name(), animal.pk),
|
||||||
LazyReference(BadDoc, animal.pk),
|
LazyReference(BadDoc, animal.pk),
|
||||||
):
|
):
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
p = Ocurrence(person="test", animal=bad).save()
|
Ocurrence(person="test", animal=bad).save()
|
||||||
|
|
||||||
def test_lazy_reference_query_conversion(self):
|
def test_lazy_reference_query_conversion(self):
|
||||||
"""Ensure that LazyReferenceFields can be queried using objects and values
|
"""Ensure that LazyReferenceFields can be queried using objects and values
|
||||||
@@ -179,14 +181,14 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
post2.save()
|
post2.save()
|
||||||
|
|
||||||
post = BlogPost.objects(author=m1).first()
|
post = BlogPost.objects(author=m1).first()
|
||||||
self.assertEqual(post.id, post1.id)
|
assert post.id == post1.id
|
||||||
|
|
||||||
post = BlogPost.objects(author=m2).first()
|
post = BlogPost.objects(author=m2).first()
|
||||||
self.assertEqual(post.id, post2.id)
|
assert post.id == post2.id
|
||||||
|
|
||||||
# Same thing by passing a LazyReference instance
|
# Same thing by passing a LazyReference instance
|
||||||
post = BlogPost.objects(author=LazyReference(Member, m2.pk)).first()
|
post = BlogPost.objects(author=LazyReference(Member, m2.pk)).first()
|
||||||
self.assertEqual(post.id, post2.id)
|
assert post.id == post2.id
|
||||||
|
|
||||||
def test_lazy_reference_query_conversion_dbref(self):
|
def test_lazy_reference_query_conversion_dbref(self):
|
||||||
"""Ensure that LazyReferenceFields can be queried using objects and values
|
"""Ensure that LazyReferenceFields can be queried using objects and values
|
||||||
@@ -215,14 +217,14 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
post2.save()
|
post2.save()
|
||||||
|
|
||||||
post = BlogPost.objects(author=m1).first()
|
post = BlogPost.objects(author=m1).first()
|
||||||
self.assertEqual(post.id, post1.id)
|
assert post.id == post1.id
|
||||||
|
|
||||||
post = BlogPost.objects(author=m2).first()
|
post = BlogPost.objects(author=m2).first()
|
||||||
self.assertEqual(post.id, post2.id)
|
assert post.id == post2.id
|
||||||
|
|
||||||
# Same thing by passing a LazyReference instance
|
# Same thing by passing a LazyReference instance
|
||||||
post = BlogPost.objects(author=LazyReference(Member, m2.pk)).first()
|
post = BlogPost.objects(author=LazyReference(Member, m2.pk)).first()
|
||||||
self.assertEqual(post.id, post2.id)
|
assert post.id == post2.id
|
||||||
|
|
||||||
def test_lazy_reference_passthrough(self):
|
def test_lazy_reference_passthrough(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -239,20 +241,20 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
animal = Animal(name="Leopard", tag="heavy").save()
|
animal = Animal(name="Leopard", tag="heavy").save()
|
||||||
Ocurrence(animal=animal, animal_passthrough=animal).save()
|
Ocurrence(animal=animal, animal_passthrough=animal).save()
|
||||||
p = Ocurrence.objects.get()
|
p = Ocurrence.objects.get()
|
||||||
self.assertIsInstance(p.animal, LazyReference)
|
assert isinstance(p.animal, LazyReference)
|
||||||
with self.assertRaises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
p.animal["name"]
|
p.animal["name"]
|
||||||
with self.assertRaises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
p.animal.name
|
p.animal.name
|
||||||
self.assertEqual(p.animal.pk, animal.pk)
|
assert p.animal.pk == animal.pk
|
||||||
|
|
||||||
self.assertEqual(p.animal_passthrough.name, "Leopard")
|
assert p.animal_passthrough.name == "Leopard"
|
||||||
self.assertEqual(p.animal_passthrough["name"], "Leopard")
|
assert p.animal_passthrough["name"] == "Leopard"
|
||||||
|
|
||||||
# Should not be able to access referenced document's methods
|
# Should not be able to access referenced document's methods
|
||||||
with self.assertRaises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
p.animal.save
|
p.animal.save
|
||||||
with self.assertRaises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
p.animal["save"]
|
p.animal["save"]
|
||||||
|
|
||||||
def test_lazy_reference_not_set(self):
|
def test_lazy_reference_not_set(self):
|
||||||
@@ -269,7 +271,7 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
Ocurrence(person="foo").save()
|
Ocurrence(person="foo").save()
|
||||||
p = Ocurrence.objects.get()
|
p = Ocurrence.objects.get()
|
||||||
self.assertIs(p.animal, None)
|
assert p.animal is None
|
||||||
|
|
||||||
def test_lazy_reference_equality(self):
|
def test_lazy_reference_equality(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -280,12 +282,12 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
animal = Animal(name="Leopard", tag="heavy").save()
|
animal = Animal(name="Leopard", tag="heavy").save()
|
||||||
animalref = LazyReference(Animal, animal.pk)
|
animalref = LazyReference(Animal, animal.pk)
|
||||||
self.assertEqual(animal, animalref)
|
assert animal == animalref
|
||||||
self.assertEqual(animalref, animal)
|
assert animalref == animal
|
||||||
|
|
||||||
other_animalref = LazyReference(Animal, ObjectId("54495ad94c934721ede76f90"))
|
other_animalref = LazyReference(Animal, ObjectId("54495ad94c934721ede76f90"))
|
||||||
self.assertNotEqual(animal, other_animalref)
|
assert animal != other_animalref
|
||||||
self.assertNotEqual(other_animalref, animal)
|
assert other_animalref != animal
|
||||||
|
|
||||||
def test_lazy_reference_embedded(self):
|
def test_lazy_reference_embedded(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -308,12 +310,12 @@ class TestLazyReferenceField(MongoDBTestCase):
|
|||||||
animal2 = Animal(name="cheeta").save()
|
animal2 = Animal(name="cheeta").save()
|
||||||
|
|
||||||
def check_fields_type(occ):
|
def check_fields_type(occ):
|
||||||
self.assertIsInstance(occ.direct, LazyReference)
|
assert isinstance(occ.direct, LazyReference)
|
||||||
for elem in occ.in_list:
|
for elem in occ.in_list:
|
||||||
self.assertIsInstance(elem, LazyReference)
|
assert isinstance(elem, LazyReference)
|
||||||
self.assertIsInstance(occ.in_embedded.direct, LazyReference)
|
assert isinstance(occ.in_embedded.direct, LazyReference)
|
||||||
for elem in occ.in_embedded.in_list:
|
for elem in occ.in_embedded.in_list:
|
||||||
self.assertIsInstance(elem, LazyReference)
|
assert isinstance(elem, LazyReference)
|
||||||
|
|
||||||
occ = Ocurrence(
|
occ = Ocurrence(
|
||||||
in_list=[animal1, animal2],
|
in_list=[animal1, animal2],
|
||||||
@@ -346,19 +348,19 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
|
|||||||
animal = Animal(name="Leopard", tag="heavy").save()
|
animal = Animal(name="Leopard", tag="heavy").save()
|
||||||
Ocurrence(person="test", animal=animal).save()
|
Ocurrence(person="test", animal=animal).save()
|
||||||
p = Ocurrence.objects.get()
|
p = Ocurrence.objects.get()
|
||||||
self.assertIsInstance(p.animal, LazyReference)
|
assert isinstance(p.animal, LazyReference)
|
||||||
fetched_animal = p.animal.fetch()
|
fetched_animal = p.animal.fetch()
|
||||||
self.assertEqual(fetched_animal, animal)
|
assert fetched_animal == animal
|
||||||
# `fetch` keep cache on referenced document by default...
|
# `fetch` keep cache on referenced document by default...
|
||||||
animal.tag = "not so heavy"
|
animal.tag = "not so heavy"
|
||||||
animal.save()
|
animal.save()
|
||||||
double_fetch = p.animal.fetch()
|
double_fetch = p.animal.fetch()
|
||||||
self.assertIs(fetched_animal, double_fetch)
|
assert fetched_animal is double_fetch
|
||||||
self.assertEqual(double_fetch.tag, "heavy")
|
assert double_fetch.tag == "heavy"
|
||||||
# ...unless specified otherwise
|
# ...unless specified otherwise
|
||||||
fetch_force = p.animal.fetch(force=True)
|
fetch_force = p.animal.fetch(force=True)
|
||||||
self.assertIsNot(fetch_force, fetched_animal)
|
assert fetch_force is not fetched_animal
|
||||||
self.assertEqual(fetch_force.tag, "not so heavy")
|
assert fetch_force.tag == "not so heavy"
|
||||||
|
|
||||||
def test_generic_lazy_reference_choices(self):
|
def test_generic_lazy_reference_choices(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -384,14 +386,14 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
|
|||||||
mineral = Mineral(name="Granite").save()
|
mineral = Mineral(name="Granite").save()
|
||||||
|
|
||||||
occ_animal = Ocurrence(living_thing=animal, thing=animal).save()
|
occ_animal = Ocurrence(living_thing=animal, thing=animal).save()
|
||||||
occ_vegetal = Ocurrence(living_thing=vegetal, thing=vegetal).save()
|
_ = Ocurrence(living_thing=vegetal, thing=vegetal).save()
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
Ocurrence(living_thing=mineral).save()
|
Ocurrence(living_thing=mineral).save()
|
||||||
|
|
||||||
occ = Ocurrence.objects.get(living_thing=animal)
|
occ = Ocurrence.objects.get(living_thing=animal)
|
||||||
self.assertEqual(occ, occ_animal)
|
assert occ == occ_animal
|
||||||
self.assertIsInstance(occ.thing, LazyReference)
|
assert isinstance(occ.thing, LazyReference)
|
||||||
self.assertIsInstance(occ.living_thing, LazyReference)
|
assert isinstance(occ.living_thing, LazyReference)
|
||||||
|
|
||||||
occ.thing = vegetal
|
occ.thing = vegetal
|
||||||
occ.living_thing = vegetal
|
occ.living_thing = vegetal
|
||||||
@@ -399,7 +401,7 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
occ.thing = mineral
|
occ.thing = mineral
|
||||||
occ.living_thing = mineral
|
occ.living_thing = mineral
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
occ.save()
|
occ.save()
|
||||||
|
|
||||||
def test_generic_lazy_reference_set(self):
|
def test_generic_lazy_reference_set(self):
|
||||||
@@ -434,7 +436,7 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
|
|||||||
):
|
):
|
||||||
p = Ocurrence(person="test", animal=ref).save()
|
p = Ocurrence(person="test", animal=ref).save()
|
||||||
p.reload()
|
p.reload()
|
||||||
self.assertIsInstance(p.animal, (LazyReference, Document))
|
assert isinstance(p.animal, (LazyReference, Document))
|
||||||
p.animal.fetch()
|
p.animal.fetch()
|
||||||
|
|
||||||
def test_generic_lazy_reference_bad_set(self):
|
def test_generic_lazy_reference_bad_set(self):
|
||||||
@@ -455,8 +457,8 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
|
|||||||
animal = Animal(name="Leopard", tag="heavy").save()
|
animal = Animal(name="Leopard", tag="heavy").save()
|
||||||
baddoc = BadDoc().save()
|
baddoc = BadDoc().save()
|
||||||
for bad in (42, "foo", baddoc, LazyReference(BadDoc, animal.pk)):
|
for bad in (42, "foo", baddoc, LazyReference(BadDoc, animal.pk)):
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
p = Ocurrence(person="test", animal=bad).save()
|
Ocurrence(person="test", animal=bad).save()
|
||||||
|
|
||||||
def test_generic_lazy_reference_query_conversion(self):
|
def test_generic_lazy_reference_query_conversion(self):
|
||||||
class Member(Document):
|
class Member(Document):
|
||||||
@@ -481,14 +483,14 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
|
|||||||
post2.save()
|
post2.save()
|
||||||
|
|
||||||
post = BlogPost.objects(author=m1).first()
|
post = BlogPost.objects(author=m1).first()
|
||||||
self.assertEqual(post.id, post1.id)
|
assert post.id == post1.id
|
||||||
|
|
||||||
post = BlogPost.objects(author=m2).first()
|
post = BlogPost.objects(author=m2).first()
|
||||||
self.assertEqual(post.id, post2.id)
|
assert post.id == post2.id
|
||||||
|
|
||||||
# Same thing by passing a LazyReference instance
|
# Same thing by passing a LazyReference instance
|
||||||
post = BlogPost.objects(author=LazyReference(Member, m2.pk)).first()
|
post = BlogPost.objects(author=LazyReference(Member, m2.pk)).first()
|
||||||
self.assertEqual(post.id, post2.id)
|
assert post.id == post2.id
|
||||||
|
|
||||||
def test_generic_lazy_reference_not_set(self):
|
def test_generic_lazy_reference_not_set(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -504,7 +506,7 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
Ocurrence(person="foo").save()
|
Ocurrence(person="foo").save()
|
||||||
p = Ocurrence.objects.get()
|
p = Ocurrence.objects.get()
|
||||||
self.assertIs(p.animal, None)
|
assert p.animal is None
|
||||||
|
|
||||||
def test_generic_lazy_reference_accepts_string_instead_of_class(self):
|
def test_generic_lazy_reference_accepts_string_instead_of_class(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -521,7 +523,7 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
|
|||||||
animal = Animal().save()
|
animal = Animal().save()
|
||||||
Ocurrence(animal=animal).save()
|
Ocurrence(animal=animal).save()
|
||||||
p = Ocurrence.objects.get()
|
p = Ocurrence.objects.get()
|
||||||
self.assertEqual(p.animal, animal)
|
assert p.animal == animal
|
||||||
|
|
||||||
def test_generic_lazy_reference_embedded(self):
|
def test_generic_lazy_reference_embedded(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -544,12 +546,12 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
|
|||||||
animal2 = Animal(name="cheeta").save()
|
animal2 = Animal(name="cheeta").save()
|
||||||
|
|
||||||
def check_fields_type(occ):
|
def check_fields_type(occ):
|
||||||
self.assertIsInstance(occ.direct, LazyReference)
|
assert isinstance(occ.direct, LazyReference)
|
||||||
for elem in occ.in_list:
|
for elem in occ.in_list:
|
||||||
self.assertIsInstance(elem, LazyReference)
|
assert isinstance(elem, LazyReference)
|
||||||
self.assertIsInstance(occ.in_embedded.direct, LazyReference)
|
assert isinstance(occ.in_embedded.direct, LazyReference)
|
||||||
for elem in occ.in_embedded.in_list:
|
for elem in occ.in_embedded.in_list:
|
||||||
self.assertIsInstance(elem, LazyReference)
|
assert isinstance(elem, LazyReference)
|
||||||
|
|
||||||
occ = Ocurrence(
|
occ = Ocurrence(
|
||||||
in_list=[animal1, animal2],
|
in_list=[animal1, animal2],
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
from bson.int64 import Int64
|
||||||
import six
|
import pytest
|
||||||
|
|
||||||
try:
|
|
||||||
from bson.int64 import Int64
|
|
||||||
except ImportError:
|
|
||||||
Int64 = long
|
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.connection import get_db
|
from mongoengine.connection import get_db
|
||||||
@@ -24,10 +19,10 @@ class TestLongField(MongoDBTestCase):
|
|||||||
|
|
||||||
doc = TestLongFieldConsideredAsInt64(some_long=42).save()
|
doc = TestLongFieldConsideredAsInt64(some_long=42).save()
|
||||||
db = get_db()
|
db = get_db()
|
||||||
self.assertIsInstance(
|
assert isinstance(
|
||||||
db.test_long_field_considered_as_int64.find()[0]["some_long"], Int64
|
db.test_long_field_considered_as_int64.find()[0]["some_long"], Int64
|
||||||
)
|
)
|
||||||
self.assertIsInstance(doc.some_long, six.integer_types)
|
assert isinstance(doc.some_long, int)
|
||||||
|
|
||||||
def test_long_validation(self):
|
def test_long_validation(self):
|
||||||
"""Ensure that invalid values cannot be assigned to long fields.
|
"""Ensure that invalid values cannot be assigned to long fields.
|
||||||
@@ -41,11 +36,14 @@ class TestLongField(MongoDBTestCase):
|
|||||||
doc.validate()
|
doc.validate()
|
||||||
|
|
||||||
doc.value = -1
|
doc.value = -1
|
||||||
self.assertRaises(ValidationError, doc.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
doc.validate()
|
||||||
doc.value = 120
|
doc.value = 120
|
||||||
self.assertRaises(ValidationError, doc.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
doc.validate()
|
||||||
doc.value = "ten"
|
doc.value = "ten"
|
||||||
self.assertRaises(ValidationError, doc.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
doc.validate()
|
||||||
|
|
||||||
def test_long_ne_operator(self):
|
def test_long_ne_operator(self):
|
||||||
class TestDocument(Document):
|
class TestDocument(Document):
|
||||||
@@ -56,4 +54,4 @@ class TestLongField(MongoDBTestCase):
|
|||||||
TestDocument(long_fld=None).save()
|
TestDocument(long_fld=None).save()
|
||||||
TestDocument(long_fld=1).save()
|
TestDocument(long_fld=1).save()
|
||||||
|
|
||||||
self.assertEqual(1, TestDocument.objects(long_fld__ne=None).count())
|
assert 1 == TestDocument.objects(long_fld__ne=None).count()
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from mongoengine import *
|
import pytest
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -19,11 +20,11 @@ class TestMapField(MongoDBTestCase):
|
|||||||
e.mapping["someint"] = 1
|
e.mapping["someint"] = 1
|
||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
e.mapping["somestring"] = "abc"
|
e.mapping["somestring"] = "abc"
|
||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
|
|
||||||
class NoDeclaredType(Document):
|
class NoDeclaredType(Document):
|
||||||
mapping = MapField()
|
mapping = MapField()
|
||||||
@@ -51,10 +52,10 @@ class TestMapField(MongoDBTestCase):
|
|||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
e2 = Extensible.objects.get(id=e.id)
|
e2 = Extensible.objects.get(id=e.id)
|
||||||
self.assertIsInstance(e2.mapping["somestring"], StringSetting)
|
assert isinstance(e2.mapping["somestring"], StringSetting)
|
||||||
self.assertIsInstance(e2.mapping["someint"], IntegerSetting)
|
assert isinstance(e2.mapping["someint"], IntegerSetting)
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
e.mapping["someint"] = 123
|
e.mapping["someint"] = 123
|
||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
@@ -74,9 +75,9 @@ class TestMapField(MongoDBTestCase):
|
|||||||
Test.objects.update_one(inc__my_map__DICTIONARY_KEY__number=1)
|
Test.objects.update_one(inc__my_map__DICTIONARY_KEY__number=1)
|
||||||
|
|
||||||
test = Test.objects.get()
|
test = Test.objects.get()
|
||||||
self.assertEqual(test.my_map["DICTIONARY_KEY"].number, 2)
|
assert test.my_map["DICTIONARY_KEY"].number == 2
|
||||||
doc = self.db.test.find_one()
|
doc = self.db.test.find_one()
|
||||||
self.assertEqual(doc["x"]["DICTIONARY_KEY"]["i"], 2)
|
assert doc["x"]["DICTIONARY_KEY"]["i"] == 2
|
||||||
|
|
||||||
def test_mapfield_numerical_index(self):
|
def test_mapfield_numerical_index(self):
|
||||||
"""Ensure that MapField accept numeric strings as indexes."""
|
"""Ensure that MapField accept numeric strings as indexes."""
|
||||||
@@ -116,13 +117,13 @@ class TestMapField(MongoDBTestCase):
|
|||||||
actions={"friends": Action(operation="drink", object="beer")},
|
actions={"friends": Action(operation="drink", object="beer")},
|
||||||
).save()
|
).save()
|
||||||
|
|
||||||
self.assertEqual(1, Log.objects(visited__friends__exists=True).count())
|
assert 1 == Log.objects(visited__friends__exists=True).count()
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
1,
|
1
|
||||||
Log.objects(
|
== Log.objects(
|
||||||
actions__friends__operation="drink", actions__friends__object="beer"
|
actions__friends__operation="drink", actions__friends__object="beer"
|
||||||
).count(),
|
).count()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_map_field_unicode(self):
|
def test_map_field_unicode(self):
|
||||||
@@ -139,7 +140,7 @@ class TestMapField(MongoDBTestCase):
|
|||||||
|
|
||||||
tree.save()
|
tree.save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
BlogPost.objects.get(id=tree.id).info_dict[u"éééé"].description,
|
BlogPost.objects.get(id=tree.id).info_dict[u"éééé"].description
|
||||||
u"VALUE: éééé",
|
== u"VALUE: éééé"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from bson import SON, DBRef
|
from bson import DBRef, SON
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
|
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -24,19 +24,22 @@ class TestReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
# Make sure ReferenceField only accepts a document class or a string
|
# Make sure ReferenceField only accepts a document class or a string
|
||||||
# with a document class name.
|
# with a document class name.
|
||||||
self.assertRaises(ValidationError, ReferenceField, EmbeddedDocument)
|
with pytest.raises(ValidationError):
|
||||||
|
ReferenceField(EmbeddedDocument)
|
||||||
|
|
||||||
user = User(name="Test User")
|
user = User(name="Test User")
|
||||||
|
|
||||||
# Ensure that the referenced object must have been saved
|
# Ensure that the referenced object must have been saved
|
||||||
post1 = BlogPost(content="Chips and gravy taste good.")
|
post1 = BlogPost(content="Chips and gravy taste good.")
|
||||||
post1.author = user
|
post1.author = user
|
||||||
self.assertRaises(ValidationError, post1.save)
|
with pytest.raises(ValidationError):
|
||||||
|
post1.save()
|
||||||
|
|
||||||
# Check that an invalid object type cannot be used
|
# Check that an invalid object type cannot be used
|
||||||
post2 = BlogPost(content="Chips and chilli taste good.")
|
post2 = BlogPost(content="Chips and chilli taste good.")
|
||||||
post1.author = post2
|
post1.author = post2
|
||||||
self.assertRaises(ValidationError, post1.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
post1.validate()
|
||||||
|
|
||||||
# Ensure ObjectID's are accepted as references
|
# Ensure ObjectID's are accepted as references
|
||||||
user_object_id = user.pk
|
user_object_id = user.pk
|
||||||
@@ -52,22 +55,8 @@ class TestReferenceField(MongoDBTestCase):
|
|||||||
# Make sure referencing a saved document of the *wrong* type fails
|
# Make sure referencing a saved document of the *wrong* type fails
|
||||||
post2.save()
|
post2.save()
|
||||||
post1.author = post2
|
post1.author = post2
|
||||||
self.assertRaises(ValidationError, post1.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
post1.validate()
|
||||||
def test_objectid_reference_fields(self):
|
|
||||||
"""Make sure storing Object ID references works."""
|
|
||||||
|
|
||||||
class Person(Document):
|
|
||||||
name = StringField()
|
|
||||||
parent = ReferenceField("self")
|
|
||||||
|
|
||||||
Person.drop_collection()
|
|
||||||
|
|
||||||
p1 = Person(name="John").save()
|
|
||||||
Person(name="Ross", parent=p1.pk).save()
|
|
||||||
|
|
||||||
p = Person.objects.get(name="Ross")
|
|
||||||
self.assertEqual(p.parent, p1)
|
|
||||||
|
|
||||||
def test_dbref_reference_fields(self):
|
def test_dbref_reference_fields(self):
|
||||||
"""Make sure storing references as bson.dbref.DBRef works."""
|
"""Make sure storing references as bson.dbref.DBRef works."""
|
||||||
@@ -81,13 +70,12 @@ class TestReferenceField(MongoDBTestCase):
|
|||||||
p1 = Person(name="John").save()
|
p1 = Person(name="John").save()
|
||||||
Person(name="Ross", parent=p1).save()
|
Person(name="Ross", parent=p1).save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert Person._get_collection().find_one({"name": "Ross"})["parent"] == DBRef(
|
||||||
Person._get_collection().find_one({"name": "Ross"})["parent"],
|
"person", p1.pk
|
||||||
DBRef("person", p1.pk),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
p = Person.objects.get(name="Ross")
|
p = Person.objects.get(name="Ross")
|
||||||
self.assertEqual(p.parent, p1)
|
assert p.parent == p1
|
||||||
|
|
||||||
def test_dbref_to_mongo(self):
|
def test_dbref_to_mongo(self):
|
||||||
"""Make sure that calling to_mongo on a ReferenceField which
|
"""Make sure that calling to_mongo on a ReferenceField which
|
||||||
@@ -100,9 +88,7 @@ class TestReferenceField(MongoDBTestCase):
|
|||||||
parent = ReferenceField("self", dbref=False)
|
parent = ReferenceField("self", dbref=False)
|
||||||
|
|
||||||
p = Person(name="Steve", parent=DBRef("person", "abcdefghijklmnop"))
|
p = Person(name="Steve", parent=DBRef("person", "abcdefghijklmnop"))
|
||||||
self.assertEqual(
|
assert p.to_mongo() == SON([("name", u"Steve"), ("parent", "abcdefghijklmnop")])
|
||||||
p.to_mongo(), SON([("name", u"Steve"), ("parent", "abcdefghijklmnop")])
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_objectid_reference_fields(self):
|
def test_objectid_reference_fields(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -116,10 +102,10 @@ class TestReferenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
col = Person._get_collection()
|
col = Person._get_collection()
|
||||||
data = col.find_one({"name": "Ross"})
|
data = col.find_one({"name": "Ross"})
|
||||||
self.assertEqual(data["parent"], p1.pk)
|
assert data["parent"] == p1.pk
|
||||||
|
|
||||||
p = Person.objects.get(name="Ross")
|
p = Person.objects.get(name="Ross")
|
||||||
self.assertEqual(p.parent, p1)
|
assert p.parent == p1
|
||||||
|
|
||||||
def test_undefined_reference(self):
|
def test_undefined_reference(self):
|
||||||
"""Ensure that ReferenceFields may reference undefined Documents.
|
"""Ensure that ReferenceFields may reference undefined Documents.
|
||||||
@@ -144,14 +130,14 @@ class TestReferenceField(MongoDBTestCase):
|
|||||||
me.save()
|
me.save()
|
||||||
|
|
||||||
obj = Product.objects(company=ten_gen).first()
|
obj = Product.objects(company=ten_gen).first()
|
||||||
self.assertEqual(obj, mongodb)
|
assert obj == mongodb
|
||||||
self.assertEqual(obj.company, ten_gen)
|
assert obj.company == ten_gen
|
||||||
|
|
||||||
obj = Product.objects(company=None).first()
|
obj = Product.objects(company=None).first()
|
||||||
self.assertEqual(obj, me)
|
assert obj == me
|
||||||
|
|
||||||
obj = Product.objects.get(company=None)
|
obj = Product.objects.get(company=None)
|
||||||
self.assertEqual(obj, me)
|
assert obj == me
|
||||||
|
|
||||||
def test_reference_query_conversion(self):
|
def test_reference_query_conversion(self):
|
||||||
"""Ensure that ReferenceFields can be queried using objects and values
|
"""Ensure that ReferenceFields can be queried using objects and values
|
||||||
@@ -180,10 +166,10 @@ class TestReferenceField(MongoDBTestCase):
|
|||||||
post2.save()
|
post2.save()
|
||||||
|
|
||||||
post = BlogPost.objects(author=m1).first()
|
post = BlogPost.objects(author=m1).first()
|
||||||
self.assertEqual(post.id, post1.id)
|
assert post.id == post1.id
|
||||||
|
|
||||||
post = BlogPost.objects(author=m2).first()
|
post = BlogPost.objects(author=m2).first()
|
||||||
self.assertEqual(post.id, post2.id)
|
assert post.id == post2.id
|
||||||
|
|
||||||
def test_reference_query_conversion_dbref(self):
|
def test_reference_query_conversion_dbref(self):
|
||||||
"""Ensure that ReferenceFields can be queried using objects and values
|
"""Ensure that ReferenceFields can be queried using objects and values
|
||||||
@@ -212,7 +198,7 @@ class TestReferenceField(MongoDBTestCase):
|
|||||||
post2.save()
|
post2.save()
|
||||||
|
|
||||||
post = BlogPost.objects(author=m1).first()
|
post = BlogPost.objects(author=m1).first()
|
||||||
self.assertEqual(post.id, post1.id)
|
assert post.id == post1.id
|
||||||
|
|
||||||
post = BlogPost.objects(author=m2).first()
|
post = BlogPost.objects(author=m2).first()
|
||||||
self.assertEqual(post.id, post2.id)
|
assert post.id == post2.id
|
||||||
|
|||||||
@@ -18,17 +18,17 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
Person(name="Person %s" % x).save()
|
Person(name="Person %s" % x).save()
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
ids = [i.id for i in Person.objects]
|
ids = [i.id for i in Person.objects]
|
||||||
self.assertEqual(ids, range(1, 11))
|
assert ids == list(range(1, 11))
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
Person.id.set_next_value(1000)
|
Person.id.set_next_value(1000)
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 1000)
|
assert c["next"] == 1000
|
||||||
|
|
||||||
def test_sequence_field_get_next_value(self):
|
def test_sequence_field_get_next_value(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -41,10 +41,10 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
for x in range(10):
|
for x in range(10):
|
||||||
Person(name="Person %s" % x).save()
|
Person(name="Person %s" % x).save()
|
||||||
|
|
||||||
self.assertEqual(Person.id.get_next_value(), 11)
|
assert Person.id.get_next_value() == 11
|
||||||
self.db["mongoengine.counters"].drop()
|
self.db["mongoengine.counters"].drop()
|
||||||
|
|
||||||
self.assertEqual(Person.id.get_next_value(), 1)
|
assert Person.id.get_next_value() == 1
|
||||||
|
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
id = SequenceField(primary_key=True, value_decorator=str)
|
id = SequenceField(primary_key=True, value_decorator=str)
|
||||||
@@ -56,10 +56,10 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
for x in range(10):
|
for x in range(10):
|
||||||
Person(name="Person %s" % x).save()
|
Person(name="Person %s" % x).save()
|
||||||
|
|
||||||
self.assertEqual(Person.id.get_next_value(), "11")
|
assert Person.id.get_next_value() == "11"
|
||||||
self.db["mongoengine.counters"].drop()
|
self.db["mongoengine.counters"].drop()
|
||||||
|
|
||||||
self.assertEqual(Person.id.get_next_value(), "1")
|
assert Person.id.get_next_value() == "1"
|
||||||
|
|
||||||
def test_sequence_field_sequence_name(self):
|
def test_sequence_field_sequence_name(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -73,17 +73,17 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
Person(name="Person %s" % x).save()
|
Person(name="Person %s" % x).save()
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
ids = [i.id for i in Person.objects]
|
ids = [i.id for i in Person.objects]
|
||||||
self.assertEqual(ids, range(1, 11))
|
assert ids == list(range(1, 11))
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
Person.id.set_next_value(1000)
|
Person.id.set_next_value(1000)
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
|
||||||
self.assertEqual(c["next"], 1000)
|
assert c["next"] == 1000
|
||||||
|
|
||||||
def test_multiple_sequence_fields(self):
|
def test_multiple_sequence_fields(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -98,24 +98,24 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
Person(name="Person %s" % x).save()
|
Person(name="Person %s" % x).save()
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
ids = [i.id for i in Person.objects]
|
ids = [i.id for i in Person.objects]
|
||||||
self.assertEqual(ids, range(1, 11))
|
assert ids == list(range(1, 11))
|
||||||
|
|
||||||
counters = [i.counter for i in Person.objects]
|
counters = [i.counter for i in Person.objects]
|
||||||
self.assertEqual(counters, range(1, 11))
|
assert counters == list(range(1, 11))
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
Person.id.set_next_value(1000)
|
Person.id.set_next_value(1000)
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 1000)
|
assert c["next"] == 1000
|
||||||
|
|
||||||
Person.counter.set_next_value(999)
|
Person.counter.set_next_value(999)
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.counter"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.counter"})
|
||||||
self.assertEqual(c["next"], 999)
|
assert c["next"] == 999
|
||||||
|
|
||||||
def test_sequence_fields_reload(self):
|
def test_sequence_fields_reload(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -127,20 +127,20 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
|
|
||||||
a = Animal(name="Boi").save()
|
a = Animal(name="Boi").save()
|
||||||
|
|
||||||
self.assertEqual(a.counter, 1)
|
assert a.counter == 1
|
||||||
a.reload()
|
a.reload()
|
||||||
self.assertEqual(a.counter, 1)
|
assert a.counter == 1
|
||||||
|
|
||||||
a.counter = None
|
a.counter = None
|
||||||
self.assertEqual(a.counter, 2)
|
assert a.counter == 2
|
||||||
a.save()
|
a.save()
|
||||||
|
|
||||||
self.assertEqual(a.counter, 2)
|
assert a.counter == 2
|
||||||
|
|
||||||
a = Animal.objects.first()
|
a = Animal.objects.first()
|
||||||
self.assertEqual(a.counter, 2)
|
assert a.counter == 2
|
||||||
a.reload()
|
a.reload()
|
||||||
self.assertEqual(a.counter, 2)
|
assert a.counter == 2
|
||||||
|
|
||||||
def test_multiple_sequence_fields_on_docs(self):
|
def test_multiple_sequence_fields_on_docs(self):
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
@@ -160,22 +160,22 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
Person(name="Person %s" % x).save()
|
Person(name="Person %s" % x).save()
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "animal.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "animal.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
ids = [i.id for i in Person.objects]
|
ids = [i.id for i in Person.objects]
|
||||||
self.assertEqual(ids, range(1, 11))
|
assert ids == list(range(1, 11))
|
||||||
|
|
||||||
id = [i.id for i in Animal.objects]
|
id = [i.id for i in Animal.objects]
|
||||||
self.assertEqual(id, range(1, 11))
|
assert id == list(range(1, 11))
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "animal.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "animal.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
def test_sequence_field_value_decorator(self):
|
def test_sequence_field_value_decorator(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -190,13 +190,13 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
ids = [i.id for i in Person.objects]
|
ids = [i.id for i in Person.objects]
|
||||||
self.assertEqual(ids, map(str, range(1, 11)))
|
assert ids == [str(i) for i in range(1, 11)]
|
||||||
|
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
self.assertEqual(c["next"], 10)
|
assert c["next"] == 10
|
||||||
|
|
||||||
def test_embedded_sequence_field(self):
|
def test_embedded_sequence_field(self):
|
||||||
class Comment(EmbeddedDocument):
|
class Comment(EmbeddedDocument):
|
||||||
@@ -218,10 +218,10 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
],
|
],
|
||||||
).save()
|
).save()
|
||||||
c = self.db["mongoengine.counters"].find_one({"_id": "comment.id"})
|
c = self.db["mongoengine.counters"].find_one({"_id": "comment.id"})
|
||||||
self.assertEqual(c["next"], 2)
|
assert c["next"] == 2
|
||||||
post = Post.objects.first()
|
post = Post.objects.first()
|
||||||
self.assertEqual(1, post.comments[0].id)
|
assert 1 == post.comments[0].id
|
||||||
self.assertEqual(2, post.comments[1].id)
|
assert 2 == post.comments[1].id
|
||||||
|
|
||||||
def test_inherited_sequencefield(self):
|
def test_inherited_sequencefield(self):
|
||||||
class Base(Document):
|
class Base(Document):
|
||||||
@@ -241,16 +241,14 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
foo = Foo(name="Foo")
|
foo = Foo(name="Foo")
|
||||||
foo.save()
|
foo.save()
|
||||||
|
|
||||||
self.assertTrue(
|
assert "base.counter" in self.db["mongoengine.counters"].find().distinct("_id")
|
||||||
"base.counter" in self.db["mongoengine.counters"].find().distinct("_id")
|
assert not (
|
||||||
)
|
|
||||||
self.assertFalse(
|
|
||||||
("foo.counter" or "bar.counter")
|
("foo.counter" or "bar.counter")
|
||||||
in self.db["mongoengine.counters"].find().distinct("_id")
|
in self.db["mongoengine.counters"].find().distinct("_id")
|
||||||
)
|
)
|
||||||
self.assertNotEqual(foo.counter, bar.counter)
|
assert foo.counter != bar.counter
|
||||||
self.assertEqual(foo._fields["counter"].owner_document, Base)
|
assert foo._fields["counter"].owner_document == Base
|
||||||
self.assertEqual(bar._fields["counter"].owner_document, Base)
|
assert bar._fields["counter"].owner_document == Base
|
||||||
|
|
||||||
def test_no_inherited_sequencefield(self):
|
def test_no_inherited_sequencefield(self):
|
||||||
class Base(Document):
|
class Base(Document):
|
||||||
@@ -269,13 +267,12 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
foo = Foo(name="Foo")
|
foo = Foo(name="Foo")
|
||||||
foo.save()
|
foo.save()
|
||||||
|
|
||||||
self.assertFalse(
|
assert "base.counter" not in self.db["mongoengine.counters"].find().distinct(
|
||||||
"base.counter" in self.db["mongoengine.counters"].find().distinct("_id")
|
"_id"
|
||||||
)
|
)
|
||||||
self.assertTrue(
|
existing_counters = self.db["mongoengine.counters"].find().distinct("_id")
|
||||||
("foo.counter" and "bar.counter")
|
assert "foo.counter" in existing_counters
|
||||||
in self.db["mongoengine.counters"].find().distinct("_id")
|
assert "bar.counter" in existing_counters
|
||||||
)
|
assert foo.counter == bar.counter
|
||||||
self.assertEqual(foo.counter, bar.counter)
|
assert foo._fields["counter"].owner_document == Foo
|
||||||
self.assertEqual(foo._fields["counter"].owner_document, Foo)
|
assert bar._fields["counter"].owner_document == Bar
|
||||||
self.assertEqual(bar._fields["counter"].owner_document, Bar)
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from mongoengine import *
|
import pytest
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -13,7 +14,8 @@ class TestURLField(MongoDBTestCase):
|
|||||||
|
|
||||||
link = Link()
|
link = Link()
|
||||||
link.url = "google"
|
link.url = "google"
|
||||||
self.assertRaises(ValidationError, link.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
link.validate()
|
||||||
|
|
||||||
link.url = "http://www.google.com:8080"
|
link.url = "http://www.google.com:8080"
|
||||||
link.validate()
|
link.validate()
|
||||||
@@ -29,11 +31,11 @@ class TestURLField(MongoDBTestCase):
|
|||||||
|
|
||||||
# TODO fix URL validation - this *IS* a valid URL
|
# TODO fix URL validation - this *IS* a valid URL
|
||||||
# For now we just want to make sure that the error message is correct
|
# For now we just want to make sure that the error message is correct
|
||||||
with self.assertRaises(ValidationError) as ctx_err:
|
with pytest.raises(ValidationError) as exc_info:
|
||||||
link.validate()
|
link.validate()
|
||||||
self.assertEqual(
|
assert (
|
||||||
unicode(ctx_err.exception),
|
str(exc_info.value)
|
||||||
u"ValidationError (Link:None) (Invalid URL: http://\u043f\u0440\u0438\u0432\u0435\u0442.com: ['url'])",
|
== u"ValidationError (Link:None) (Invalid URL: http://\u043f\u0440\u0438\u0432\u0435\u0442.com: ['url'])"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_url_scheme_validation(self):
|
def test_url_scheme_validation(self):
|
||||||
@@ -48,7 +50,8 @@ class TestURLField(MongoDBTestCase):
|
|||||||
|
|
||||||
link = Link()
|
link = Link()
|
||||||
link.url = "ws://google.com"
|
link.url = "ws://google.com"
|
||||||
self.assertRaises(ValidationError, link.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
link.validate()
|
||||||
|
|
||||||
scheme_link = SchemeLink()
|
scheme_link = SchemeLink()
|
||||||
scheme_link.url = "ws://google.com"
|
scheme_link.url = "ws://google.com"
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from mongoengine import *
|
import pytest
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
from tests.utils import MongoDBTestCase, get_as_pymongo
|
from tests.utils import MongoDBTestCase, get_as_pymongo
|
||||||
|
|
||||||
|
|
||||||
@@ -14,9 +15,7 @@ class TestUUIDField(MongoDBTestCase):
|
|||||||
def test_storage(self):
|
def test_storage(self):
|
||||||
uid = uuid.uuid4()
|
uid = uuid.uuid4()
|
||||||
person = Person(api_key=uid).save()
|
person = Person(api_key=uid).save()
|
||||||
self.assertEqual(
|
assert get_as_pymongo(person) == {"_id": person.id, "api_key": str(uid)}
|
||||||
get_as_pymongo(person), {"_id": person.id, "api_key": str(uid)}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_field_string(self):
|
def test_field_string(self):
|
||||||
"""Test UUID fields storing as String
|
"""Test UUID fields storing as String
|
||||||
@@ -25,8 +24,8 @@ class TestUUIDField(MongoDBTestCase):
|
|||||||
|
|
||||||
uu = uuid.uuid4()
|
uu = uuid.uuid4()
|
||||||
Person(api_key=uu).save()
|
Person(api_key=uu).save()
|
||||||
self.assertEqual(1, Person.objects(api_key=uu).count())
|
assert 1 == Person.objects(api_key=uu).count()
|
||||||
self.assertEqual(uu, Person.objects.first().api_key)
|
assert uu == Person.objects.first().api_key
|
||||||
|
|
||||||
person = Person()
|
person = Person()
|
||||||
valid = (uuid.uuid4(), uuid.uuid1())
|
valid = (uuid.uuid4(), uuid.uuid1())
|
||||||
@@ -40,7 +39,8 @@ class TestUUIDField(MongoDBTestCase):
|
|||||||
)
|
)
|
||||||
for api_key in invalid:
|
for api_key in invalid:
|
||||||
person.api_key = api_key
|
person.api_key = api_key
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
def test_field_binary(self):
|
def test_field_binary(self):
|
||||||
"""Test UUID fields storing as Binary object."""
|
"""Test UUID fields storing as Binary object."""
|
||||||
@@ -48,8 +48,8 @@ class TestUUIDField(MongoDBTestCase):
|
|||||||
|
|
||||||
uu = uuid.uuid4()
|
uu = uuid.uuid4()
|
||||||
Person(api_key=uu).save()
|
Person(api_key=uu).save()
|
||||||
self.assertEqual(1, Person.objects(api_key=uu).count())
|
assert 1 == Person.objects(api_key=uu).count()
|
||||||
self.assertEqual(uu, Person.objects.first().api_key)
|
assert uu == Person.objects.first().api_key
|
||||||
|
|
||||||
person = Person()
|
person = Person()
|
||||||
valid = (uuid.uuid4(), uuid.uuid1())
|
valid = (uuid.uuid4(), uuid.uuid1())
|
||||||
@@ -63,4 +63,5 @@ class TestUUIDField(MongoDBTestCase):
|
|||||||
)
|
)
|
||||||
for api_key in invalid:
|
for api_key in invalid:
|
||||||
person.api_key = api_key
|
person.api_key = api_key
|
||||||
self.assertRaises(ValidationError, person.validate)
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ class PickleSignalsTest(Document):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def post_save(self, sender, document, created, **kwargs):
|
def post_save(self, sender, document, created, **kwargs):
|
||||||
pickled = pickle.dumps(document)
|
pickle.dumps(document)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def post_delete(self, sender, document, **kwargs):
|
def post_delete(self, sender, document, **kwargs):
|
||||||
pickled = pickle.dumps(document)
|
pickle.dumps(document)
|
||||||
|
|
||||||
|
|
||||||
signals.post_save.connect(PickleSignalsTest.post_save, sender=PickleSignalsTest)
|
signals.post_save.connect(PickleSignalsTest.post_save, sender=PickleSignalsTest)
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
from .transform import *
|
|
||||||
from .field_list import *
|
|
||||||
from .queryset import *
|
|
||||||
from .visitor import *
|
|
||||||
from .geo import *
|
|
||||||
from .modify import *
|
|
||||||
|
|||||||
@@ -1,72 +1,72 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.queryset import QueryFieldList
|
from mongoengine.queryset import QueryFieldList
|
||||||
|
|
||||||
__all__ = ("QueryFieldListTest", "OnlyExcludeAllTest")
|
|
||||||
|
|
||||||
|
class TestQueryFieldList:
|
||||||
class QueryFieldListTest(unittest.TestCase):
|
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
q = QueryFieldList()
|
q = QueryFieldList()
|
||||||
self.assertFalse(q)
|
assert not q
|
||||||
|
|
||||||
q = QueryFieldList(always_include=["_cls"])
|
q = QueryFieldList(always_include=["_cls"])
|
||||||
self.assertFalse(q)
|
assert not q
|
||||||
|
|
||||||
def test_include_include(self):
|
def test_include_include(self):
|
||||||
q = QueryFieldList()
|
q = QueryFieldList()
|
||||||
q += QueryFieldList(
|
q += QueryFieldList(
|
||||||
fields=["a", "b"], value=QueryFieldList.ONLY, _only_called=True
|
fields=["a", "b"], value=QueryFieldList.ONLY, _only_called=True
|
||||||
)
|
)
|
||||||
self.assertEqual(q.as_dict(), {"a": 1, "b": 1})
|
assert q.as_dict() == {"a": 1, "b": 1}
|
||||||
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
||||||
self.assertEqual(q.as_dict(), {"a": 1, "b": 1, "c": 1})
|
assert q.as_dict() == {"a": 1, "b": 1, "c": 1}
|
||||||
|
|
||||||
def test_include_exclude(self):
|
def test_include_exclude(self):
|
||||||
q = QueryFieldList()
|
q = QueryFieldList()
|
||||||
q += QueryFieldList(fields=["a", "b"], value=QueryFieldList.ONLY)
|
q += QueryFieldList(fields=["a", "b"], value=QueryFieldList.ONLY)
|
||||||
self.assertEqual(q.as_dict(), {"a": 1, "b": 1})
|
assert q.as_dict() == {"a": 1, "b": 1}
|
||||||
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.EXCLUDE)
|
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.EXCLUDE)
|
||||||
self.assertEqual(q.as_dict(), {"a": 1})
|
assert q.as_dict() == {"a": 1}
|
||||||
|
|
||||||
def test_exclude_exclude(self):
|
def test_exclude_exclude(self):
|
||||||
q = QueryFieldList()
|
q = QueryFieldList()
|
||||||
q += QueryFieldList(fields=["a", "b"], value=QueryFieldList.EXCLUDE)
|
q += QueryFieldList(fields=["a", "b"], value=QueryFieldList.EXCLUDE)
|
||||||
self.assertEqual(q.as_dict(), {"a": 0, "b": 0})
|
assert q.as_dict() == {"a": 0, "b": 0}
|
||||||
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.EXCLUDE)
|
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.EXCLUDE)
|
||||||
self.assertEqual(q.as_dict(), {"a": 0, "b": 0, "c": 0})
|
assert q.as_dict() == {"a": 0, "b": 0, "c": 0}
|
||||||
|
|
||||||
def test_exclude_include(self):
|
def test_exclude_include(self):
|
||||||
q = QueryFieldList()
|
q = QueryFieldList()
|
||||||
q += QueryFieldList(fields=["a", "b"], value=QueryFieldList.EXCLUDE)
|
q += QueryFieldList(fields=["a", "b"], value=QueryFieldList.EXCLUDE)
|
||||||
self.assertEqual(q.as_dict(), {"a": 0, "b": 0})
|
assert q.as_dict() == {"a": 0, "b": 0}
|
||||||
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
||||||
self.assertEqual(q.as_dict(), {"c": 1})
|
assert q.as_dict() == {"c": 1}
|
||||||
|
|
||||||
def test_always_include(self):
|
def test_always_include(self):
|
||||||
q = QueryFieldList(always_include=["x", "y"])
|
q = QueryFieldList(always_include=["x", "y"])
|
||||||
q += QueryFieldList(fields=["a", "b", "x"], value=QueryFieldList.EXCLUDE)
|
q += QueryFieldList(fields=["a", "b", "x"], value=QueryFieldList.EXCLUDE)
|
||||||
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
||||||
self.assertEqual(q.as_dict(), {"x": 1, "y": 1, "c": 1})
|
assert q.as_dict() == {"x": 1, "y": 1, "c": 1}
|
||||||
|
|
||||||
def test_reset(self):
|
def test_reset(self):
|
||||||
q = QueryFieldList(always_include=["x", "y"])
|
q = QueryFieldList(always_include=["x", "y"])
|
||||||
q += QueryFieldList(fields=["a", "b", "x"], value=QueryFieldList.EXCLUDE)
|
q += QueryFieldList(fields=["a", "b", "x"], value=QueryFieldList.EXCLUDE)
|
||||||
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
||||||
self.assertEqual(q.as_dict(), {"x": 1, "y": 1, "c": 1})
|
assert q.as_dict() == {"x": 1, "y": 1, "c": 1}
|
||||||
q.reset()
|
q.reset()
|
||||||
self.assertFalse(q)
|
assert not q
|
||||||
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
|
||||||
self.assertEqual(q.as_dict(), {"x": 1, "y": 1, "b": 1, "c": 1})
|
assert q.as_dict() == {"x": 1, "y": 1, "b": 1, "c": 1}
|
||||||
|
|
||||||
def test_using_a_slice(self):
|
def test_using_a_slice(self):
|
||||||
q = QueryFieldList()
|
q = QueryFieldList()
|
||||||
q += QueryFieldList(fields=["a"], value={"$slice": 5})
|
q += QueryFieldList(fields=["a"], value={"$slice": 5})
|
||||||
self.assertEqual(q.as_dict(), {"a": {"$slice": 5}})
|
assert q.as_dict() == {"a": {"$slice": 5}}
|
||||||
|
|
||||||
|
|
||||||
class OnlyExcludeAllTest(unittest.TestCase):
|
class TestOnlyExcludeAll(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
connect(db="mongoenginetest")
|
connect(db="mongoenginetest")
|
||||||
|
|
||||||
@@ -92,25 +92,23 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
only = ["b", "c"]
|
only = ["b", "c"]
|
||||||
|
|
||||||
qs = MyDoc.objects.fields(**{i: 1 for i in include})
|
qs = MyDoc.objects.fields(**{i: 1 for i in include})
|
||||||
self.assertEqual(
|
assert qs._loaded_fields.as_dict() == {"a": 1, "b": 1, "c": 1, "d": 1, "e": 1}
|
||||||
qs._loaded_fields.as_dict(), {"a": 1, "b": 1, "c": 1, "d": 1, "e": 1}
|
|
||||||
)
|
|
||||||
qs = qs.only(*only)
|
qs = qs.only(*only)
|
||||||
self.assertEqual(qs._loaded_fields.as_dict(), {"b": 1, "c": 1})
|
assert qs._loaded_fields.as_dict() == {"b": 1, "c": 1}
|
||||||
qs = qs.exclude(*exclude)
|
qs = qs.exclude(*exclude)
|
||||||
self.assertEqual(qs._loaded_fields.as_dict(), {"b": 1, "c": 1})
|
assert qs._loaded_fields.as_dict() == {"b": 1, "c": 1}
|
||||||
|
|
||||||
qs = MyDoc.objects.fields(**{i: 1 for i in include})
|
qs = MyDoc.objects.fields(**{i: 1 for i in include})
|
||||||
qs = qs.exclude(*exclude)
|
qs = qs.exclude(*exclude)
|
||||||
self.assertEqual(qs._loaded_fields.as_dict(), {"a": 1, "b": 1, "c": 1})
|
assert qs._loaded_fields.as_dict() == {"a": 1, "b": 1, "c": 1}
|
||||||
qs = qs.only(*only)
|
qs = qs.only(*only)
|
||||||
self.assertEqual(qs._loaded_fields.as_dict(), {"b": 1, "c": 1})
|
assert qs._loaded_fields.as_dict() == {"b": 1, "c": 1}
|
||||||
|
|
||||||
qs = MyDoc.objects.exclude(*exclude)
|
qs = MyDoc.objects.exclude(*exclude)
|
||||||
qs = qs.fields(**{i: 1 for i in include})
|
qs = qs.fields(**{i: 1 for i in include})
|
||||||
self.assertEqual(qs._loaded_fields.as_dict(), {"a": 1, "b": 1, "c": 1})
|
assert qs._loaded_fields.as_dict() == {"a": 1, "b": 1, "c": 1}
|
||||||
qs = qs.only(*only)
|
qs = qs.only(*only)
|
||||||
self.assertEqual(qs._loaded_fields.as_dict(), {"b": 1, "c": 1})
|
assert qs._loaded_fields.as_dict() == {"b": 1, "c": 1}
|
||||||
|
|
||||||
def test_slicing(self):
|
def test_slicing(self):
|
||||||
class MyDoc(Document):
|
class MyDoc(Document):
|
||||||
@@ -129,15 +127,16 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
qs = qs.exclude(*exclude)
|
qs = qs.exclude(*exclude)
|
||||||
qs = qs.only(*only)
|
qs = qs.only(*only)
|
||||||
qs = qs.fields(slice__b=5)
|
qs = qs.fields(slice__b=5)
|
||||||
self.assertEqual(qs._loaded_fields.as_dict(), {"b": {"$slice": 5}, "c": 1})
|
assert qs._loaded_fields.as_dict() == {"b": {"$slice": 5}, "c": 1}
|
||||||
|
|
||||||
qs = qs.fields(slice__c=[5, 1])
|
qs = qs.fields(slice__c=[5, 1])
|
||||||
self.assertEqual(
|
assert qs._loaded_fields.as_dict() == {
|
||||||
qs._loaded_fields.as_dict(), {"b": {"$slice": 5}, "c": {"$slice": [5, 1]}}
|
"b": {"$slice": 5},
|
||||||
)
|
"c": {"$slice": [5, 1]},
|
||||||
|
}
|
||||||
|
|
||||||
qs = qs.exclude("c")
|
qs = qs.exclude("c")
|
||||||
self.assertEqual(qs._loaded_fields.as_dict(), {"b": {"$slice": 5}})
|
assert qs._loaded_fields.as_dict() == {"b": {"$slice": 5}}
|
||||||
|
|
||||||
def test_mix_slice_with_other_fields(self):
|
def test_mix_slice_with_other_fields(self):
|
||||||
class MyDoc(Document):
|
class MyDoc(Document):
|
||||||
@@ -146,7 +145,7 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
c = ListField()
|
c = ListField()
|
||||||
|
|
||||||
qs = MyDoc.objects.fields(a=1, b=0, slice__c=2)
|
qs = MyDoc.objects.fields(a=1, b=0, slice__c=2)
|
||||||
self.assertEqual(qs._loaded_fields.as_dict(), {"c": {"$slice": 2}, "a": 1})
|
assert qs._loaded_fields.as_dict() == {"c": {"$slice": 2}, "a": 1}
|
||||||
|
|
||||||
def test_only(self):
|
def test_only(self):
|
||||||
"""Ensure that QuerySet.only only returns the requested fields.
|
"""Ensure that QuerySet.only only returns the requested fields.
|
||||||
@@ -155,20 +154,20 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
obj = self.Person.objects.only("name").get()
|
obj = self.Person.objects.only("name").get()
|
||||||
self.assertEqual(obj.name, person.name)
|
assert obj.name == person.name
|
||||||
self.assertEqual(obj.age, None)
|
assert obj.age is None
|
||||||
|
|
||||||
obj = self.Person.objects.only("age").get()
|
obj = self.Person.objects.only("age").get()
|
||||||
self.assertEqual(obj.name, None)
|
assert obj.name is None
|
||||||
self.assertEqual(obj.age, person.age)
|
assert obj.age == person.age
|
||||||
|
|
||||||
obj = self.Person.objects.only("name", "age").get()
|
obj = self.Person.objects.only("name", "age").get()
|
||||||
self.assertEqual(obj.name, person.name)
|
assert obj.name == person.name
|
||||||
self.assertEqual(obj.age, person.age)
|
assert obj.age == person.age
|
||||||
|
|
||||||
obj = self.Person.objects.only(*("id", "name")).get()
|
obj = self.Person.objects.only(*("id", "name")).get()
|
||||||
self.assertEqual(obj.name, person.name)
|
assert obj.name == person.name
|
||||||
self.assertEqual(obj.age, None)
|
assert obj.age is None
|
||||||
|
|
||||||
# Check polymorphism still works
|
# Check polymorphism still works
|
||||||
class Employee(self.Person):
|
class Employee(self.Person):
|
||||||
@@ -178,12 +177,12 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
employee.save()
|
employee.save()
|
||||||
|
|
||||||
obj = self.Person.objects(id=employee.id).only("age").get()
|
obj = self.Person.objects(id=employee.id).only("age").get()
|
||||||
self.assertIsInstance(obj, Employee)
|
assert isinstance(obj, Employee)
|
||||||
|
|
||||||
# Check field names are looked up properly
|
# Check field names are looked up properly
|
||||||
obj = Employee.objects(id=employee.id).only("salary").get()
|
obj = Employee.objects(id=employee.id).only("salary").get()
|
||||||
self.assertEqual(obj.salary, employee.salary)
|
assert obj.salary == employee.salary
|
||||||
self.assertEqual(obj.name, None)
|
assert obj.name is None
|
||||||
|
|
||||||
def test_only_with_subfields(self):
|
def test_only_with_subfields(self):
|
||||||
class User(EmbeddedDocument):
|
class User(EmbeddedDocument):
|
||||||
@@ -217,29 +216,29 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
post.save()
|
post.save()
|
||||||
|
|
||||||
obj = BlogPost.objects.only("author.name").get()
|
obj = BlogPost.objects.only("author.name").get()
|
||||||
self.assertEqual(obj.content, None)
|
assert obj.content is None
|
||||||
self.assertEqual(obj.author.email, None)
|
assert obj.author.email is None
|
||||||
self.assertEqual(obj.author.name, "Test User")
|
assert obj.author.name == "Test User"
|
||||||
self.assertEqual(obj.comments, [])
|
assert obj.comments == []
|
||||||
|
|
||||||
obj = BlogPost.objects.only("various.test_dynamic.some").get()
|
obj = BlogPost.objects.only("various.test_dynamic.some").get()
|
||||||
self.assertEqual(obj.various["test_dynamic"].some, True)
|
assert obj.various["test_dynamic"].some is True
|
||||||
|
|
||||||
obj = BlogPost.objects.only("content", "comments.title").get()
|
obj = BlogPost.objects.only("content", "comments.title").get()
|
||||||
self.assertEqual(obj.content, "Had a good coffee today...")
|
assert obj.content == "Had a good coffee today..."
|
||||||
self.assertEqual(obj.author, None)
|
assert obj.author is None
|
||||||
self.assertEqual(obj.comments[0].title, "I aggree")
|
assert obj.comments[0].title == "I aggree"
|
||||||
self.assertEqual(obj.comments[1].title, "Coffee")
|
assert obj.comments[1].title == "Coffee"
|
||||||
self.assertEqual(obj.comments[0].text, None)
|
assert obj.comments[0].text is None
|
||||||
self.assertEqual(obj.comments[1].text, None)
|
assert obj.comments[1].text is None
|
||||||
|
|
||||||
obj = BlogPost.objects.only("comments").get()
|
obj = BlogPost.objects.only("comments").get()
|
||||||
self.assertEqual(obj.content, None)
|
assert obj.content is None
|
||||||
self.assertEqual(obj.author, None)
|
assert obj.author is None
|
||||||
self.assertEqual(obj.comments[0].title, "I aggree")
|
assert obj.comments[0].title == "I aggree"
|
||||||
self.assertEqual(obj.comments[1].title, "Coffee")
|
assert obj.comments[1].title == "Coffee"
|
||||||
self.assertEqual(obj.comments[0].text, "Great post!")
|
assert obj.comments[0].text == "Great post!"
|
||||||
self.assertEqual(obj.comments[1].text, "I hate coffee")
|
assert obj.comments[1].text == "I hate coffee"
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
@@ -268,10 +267,10 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
post.save()
|
post.save()
|
||||||
|
|
||||||
obj = BlogPost.objects.exclude("author", "comments.text").get()
|
obj = BlogPost.objects.exclude("author", "comments.text").get()
|
||||||
self.assertEqual(obj.author, None)
|
assert obj.author is None
|
||||||
self.assertEqual(obj.content, "Had a good coffee today...")
|
assert obj.content == "Had a good coffee today..."
|
||||||
self.assertEqual(obj.comments[0].title, "I aggree")
|
assert obj.comments[0].title == "I aggree"
|
||||||
self.assertEqual(obj.comments[0].text, None)
|
assert obj.comments[0].text is None
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
@@ -303,18 +302,18 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
email.save()
|
email.save()
|
||||||
|
|
||||||
obj = Email.objects.exclude("content_type").exclude("body").get()
|
obj = Email.objects.exclude("content_type").exclude("body").get()
|
||||||
self.assertEqual(obj.sender, "me")
|
assert obj.sender == "me"
|
||||||
self.assertEqual(obj.to, "you")
|
assert obj.to == "you"
|
||||||
self.assertEqual(obj.subject, "From Russia with Love")
|
assert obj.subject == "From Russia with Love"
|
||||||
self.assertEqual(obj.body, None)
|
assert obj.body is None
|
||||||
self.assertEqual(obj.content_type, None)
|
assert obj.content_type is None
|
||||||
|
|
||||||
obj = Email.objects.only("sender", "to").exclude("body", "sender").get()
|
obj = Email.objects.only("sender", "to").exclude("body", "sender").get()
|
||||||
self.assertEqual(obj.sender, None)
|
assert obj.sender is None
|
||||||
self.assertEqual(obj.to, "you")
|
assert obj.to == "you"
|
||||||
self.assertEqual(obj.subject, None)
|
assert obj.subject is None
|
||||||
self.assertEqual(obj.body, None)
|
assert obj.body is None
|
||||||
self.assertEqual(obj.content_type, None)
|
assert obj.content_type is None
|
||||||
|
|
||||||
obj = (
|
obj = (
|
||||||
Email.objects.exclude("attachments.content")
|
Email.objects.exclude("attachments.content")
|
||||||
@@ -322,13 +321,13 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
.only("to", "attachments.name")
|
.only("to", "attachments.name")
|
||||||
.get()
|
.get()
|
||||||
)
|
)
|
||||||
self.assertEqual(obj.attachments[0].name, "file1.doc")
|
assert obj.attachments[0].name == "file1.doc"
|
||||||
self.assertEqual(obj.attachments[0].content, None)
|
assert obj.attachments[0].content is None
|
||||||
self.assertEqual(obj.sender, None)
|
assert obj.sender is None
|
||||||
self.assertEqual(obj.to, "you")
|
assert obj.to == "you"
|
||||||
self.assertEqual(obj.subject, None)
|
assert obj.subject is None
|
||||||
self.assertEqual(obj.body, None)
|
assert obj.body is None
|
||||||
self.assertEqual(obj.content_type, None)
|
assert obj.content_type is None
|
||||||
|
|
||||||
Email.drop_collection()
|
Email.drop_collection()
|
||||||
|
|
||||||
@@ -357,11 +356,11 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
.all_fields()
|
.all_fields()
|
||||||
.get()
|
.get()
|
||||||
)
|
)
|
||||||
self.assertEqual(obj.sender, "me")
|
assert obj.sender == "me"
|
||||||
self.assertEqual(obj.to, "you")
|
assert obj.to == "you"
|
||||||
self.assertEqual(obj.subject, "From Russia with Love")
|
assert obj.subject == "From Russia with Love"
|
||||||
self.assertEqual(obj.body, "Hello!")
|
assert obj.body == "Hello!"
|
||||||
self.assertEqual(obj.content_type, "text/plain")
|
assert obj.content_type == "text/plain"
|
||||||
|
|
||||||
Email.drop_collection()
|
Email.drop_collection()
|
||||||
|
|
||||||
@@ -379,27 +378,27 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
|
|
||||||
# first three
|
# first three
|
||||||
numbers = Numbers.objects.fields(slice__n=3).get()
|
numbers = Numbers.objects.fields(slice__n=3).get()
|
||||||
self.assertEqual(numbers.n, [0, 1, 2])
|
assert numbers.n == [0, 1, 2]
|
||||||
|
|
||||||
# last three
|
# last three
|
||||||
numbers = Numbers.objects.fields(slice__n=-3).get()
|
numbers = Numbers.objects.fields(slice__n=-3).get()
|
||||||
self.assertEqual(numbers.n, [-3, -2, -1])
|
assert numbers.n == [-3, -2, -1]
|
||||||
|
|
||||||
# skip 2, limit 3
|
# skip 2, limit 3
|
||||||
numbers = Numbers.objects.fields(slice__n=[2, 3]).get()
|
numbers = Numbers.objects.fields(slice__n=[2, 3]).get()
|
||||||
self.assertEqual(numbers.n, [2, 3, 4])
|
assert numbers.n == [2, 3, 4]
|
||||||
|
|
||||||
# skip to fifth from last, limit 4
|
# skip to fifth from last, limit 4
|
||||||
numbers = Numbers.objects.fields(slice__n=[-5, 4]).get()
|
numbers = Numbers.objects.fields(slice__n=[-5, 4]).get()
|
||||||
self.assertEqual(numbers.n, [-5, -4, -3, -2])
|
assert numbers.n == [-5, -4, -3, -2]
|
||||||
|
|
||||||
# skip to fifth from last, limit 10
|
# skip to fifth from last, limit 10
|
||||||
numbers = Numbers.objects.fields(slice__n=[-5, 10]).get()
|
numbers = Numbers.objects.fields(slice__n=[-5, 10]).get()
|
||||||
self.assertEqual(numbers.n, [-5, -4, -3, -2, -1])
|
assert numbers.n == [-5, -4, -3, -2, -1]
|
||||||
|
|
||||||
# skip to fifth from last, limit 10 dict method
|
# skip to fifth from last, limit 10 dict method
|
||||||
numbers = Numbers.objects.fields(n={"$slice": [-5, 10]}).get()
|
numbers = Numbers.objects.fields(n={"$slice": [-5, 10]}).get()
|
||||||
self.assertEqual(numbers.n, [-5, -4, -3, -2, -1])
|
assert numbers.n == [-5, -4, -3, -2, -1]
|
||||||
|
|
||||||
def test_slicing_nested_fields(self):
|
def test_slicing_nested_fields(self):
|
||||||
"""Ensure that query slicing an embedded array works.
|
"""Ensure that query slicing an embedded array works.
|
||||||
@@ -419,27 +418,27 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
|
|
||||||
# first three
|
# first three
|
||||||
numbers = Numbers.objects.fields(slice__embedded__n=3).get()
|
numbers = Numbers.objects.fields(slice__embedded__n=3).get()
|
||||||
self.assertEqual(numbers.embedded.n, [0, 1, 2])
|
assert numbers.embedded.n == [0, 1, 2]
|
||||||
|
|
||||||
# last three
|
# last three
|
||||||
numbers = Numbers.objects.fields(slice__embedded__n=-3).get()
|
numbers = Numbers.objects.fields(slice__embedded__n=-3).get()
|
||||||
self.assertEqual(numbers.embedded.n, [-3, -2, -1])
|
assert numbers.embedded.n == [-3, -2, -1]
|
||||||
|
|
||||||
# skip 2, limit 3
|
# skip 2, limit 3
|
||||||
numbers = Numbers.objects.fields(slice__embedded__n=[2, 3]).get()
|
numbers = Numbers.objects.fields(slice__embedded__n=[2, 3]).get()
|
||||||
self.assertEqual(numbers.embedded.n, [2, 3, 4])
|
assert numbers.embedded.n == [2, 3, 4]
|
||||||
|
|
||||||
# skip to fifth from last, limit 4
|
# skip to fifth from last, limit 4
|
||||||
numbers = Numbers.objects.fields(slice__embedded__n=[-5, 4]).get()
|
numbers = Numbers.objects.fields(slice__embedded__n=[-5, 4]).get()
|
||||||
self.assertEqual(numbers.embedded.n, [-5, -4, -3, -2])
|
assert numbers.embedded.n == [-5, -4, -3, -2]
|
||||||
|
|
||||||
# skip to fifth from last, limit 10
|
# skip to fifth from last, limit 10
|
||||||
numbers = Numbers.objects.fields(slice__embedded__n=[-5, 10]).get()
|
numbers = Numbers.objects.fields(slice__embedded__n=[-5, 10]).get()
|
||||||
self.assertEqual(numbers.embedded.n, [-5, -4, -3, -2, -1])
|
assert numbers.embedded.n == [-5, -4, -3, -2, -1]
|
||||||
|
|
||||||
# skip to fifth from last, limit 10 dict method
|
# skip to fifth from last, limit 10 dict method
|
||||||
numbers = Numbers.objects.fields(embedded__n={"$slice": [-5, 10]}).get()
|
numbers = Numbers.objects.fields(embedded__n={"$slice": [-5, 10]}).get()
|
||||||
self.assertEqual(numbers.embedded.n, [-5, -4, -3, -2, -1])
|
assert numbers.embedded.n == [-5, -4, -3, -2, -1]
|
||||||
|
|
||||||
def test_exclude_from_subclasses_docs(self):
|
def test_exclude_from_subclasses_docs(self):
|
||||||
class Base(Document):
|
class Base(Document):
|
||||||
@@ -458,9 +457,10 @@ class OnlyExcludeAllTest(unittest.TestCase):
|
|||||||
User(username="mongodb", password="secret").save()
|
User(username="mongodb", password="secret").save()
|
||||||
|
|
||||||
user = Base.objects().exclude("password", "wibble").first()
|
user = Base.objects().exclude("password", "wibble").first()
|
||||||
self.assertEqual(user.password, None)
|
assert user.password is None
|
||||||
|
|
||||||
self.assertRaises(LookUpError, Base.objects.exclude, "made_up")
|
with pytest.raises(LookUpError):
|
||||||
|
Base.objects.exclude("made_up")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -6,10 +6,7 @@ from mongoengine import *
|
|||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
__all__ = ("GeoQueriesTest",)
|
class TestGeoQueries(MongoDBTestCase):
|
||||||
|
|
||||||
|
|
||||||
class GeoQueriesTest(MongoDBTestCase):
|
|
||||||
def _create_event_data(self, point_field_class=GeoPointField):
|
def _create_event_data(self, point_field_class=GeoPointField):
|
||||||
"""Create some sample data re-used in many of the tests below."""
|
"""Create some sample data re-used in many of the tests below."""
|
||||||
|
|
||||||
@@ -51,14 +48,14 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# note that "near" will show the san francisco event, too,
|
# note that "near" will show the san francisco event, too,
|
||||||
# although it sorts to last.
|
# although it sorts to last.
|
||||||
events = self.Event.objects(location__near=[-87.67892, 41.9120459])
|
events = self.Event.objects(location__near=[-87.67892, 41.9120459])
|
||||||
self.assertEqual(events.count(), 3)
|
assert events.count() == 3
|
||||||
self.assertEqual(list(events), [event1, event3, event2])
|
assert list(events) == [event1, event3, event2]
|
||||||
|
|
||||||
# ensure ordering is respected by "near"
|
# ensure ordering is respected by "near"
|
||||||
events = self.Event.objects(location__near=[-87.67892, 41.9120459])
|
events = self.Event.objects(location__near=[-87.67892, 41.9120459])
|
||||||
events = events.order_by("-date")
|
events = events.order_by("-date")
|
||||||
self.assertEqual(events.count(), 3)
|
assert events.count() == 3
|
||||||
self.assertEqual(list(events), [event3, event1, event2])
|
assert list(events) == [event3, event1, event2]
|
||||||
|
|
||||||
def test_near_and_max_distance(self):
|
def test_near_and_max_distance(self):
|
||||||
"""Ensure the "max_distance" operator works alongside the "near"
|
"""Ensure the "max_distance" operator works alongside the "near"
|
||||||
@@ -69,8 +66,8 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# find events within 10 degrees of san francisco
|
# find events within 10 degrees of san francisco
|
||||||
point = [-122.415579, 37.7566023]
|
point = [-122.415579, 37.7566023]
|
||||||
events = self.Event.objects(location__near=point, location__max_distance=10)
|
events = self.Event.objects(location__near=point, location__max_distance=10)
|
||||||
self.assertEqual(events.count(), 1)
|
assert events.count() == 1
|
||||||
self.assertEqual(events[0], event2)
|
assert events[0] == event2
|
||||||
|
|
||||||
def test_near_and_min_distance(self):
|
def test_near_and_min_distance(self):
|
||||||
"""Ensure the "min_distance" operator works alongside the "near"
|
"""Ensure the "min_distance" operator works alongside the "near"
|
||||||
@@ -81,7 +78,7 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# find events at least 10 degrees away of san francisco
|
# find events at least 10 degrees away of san francisco
|
||||||
point = [-122.415579, 37.7566023]
|
point = [-122.415579, 37.7566023]
|
||||||
events = self.Event.objects(location__near=point, location__min_distance=10)
|
events = self.Event.objects(location__near=point, location__min_distance=10)
|
||||||
self.assertEqual(events.count(), 2)
|
assert events.count() == 2
|
||||||
|
|
||||||
def test_within_distance(self):
|
def test_within_distance(self):
|
||||||
"""Make sure the "within_distance" operator works."""
|
"""Make sure the "within_distance" operator works."""
|
||||||
@@ -90,29 +87,29 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# find events within 5 degrees of pitchfork office, chicago
|
# find events within 5 degrees of pitchfork office, chicago
|
||||||
point_and_distance = [[-87.67892, 41.9120459], 5]
|
point_and_distance = [[-87.67892, 41.9120459], 5]
|
||||||
events = self.Event.objects(location__within_distance=point_and_distance)
|
events = self.Event.objects(location__within_distance=point_and_distance)
|
||||||
self.assertEqual(events.count(), 2)
|
assert events.count() == 2
|
||||||
events = list(events)
|
events = list(events)
|
||||||
self.assertNotIn(event2, events)
|
assert event2 not in events
|
||||||
self.assertIn(event1, events)
|
assert event1 in events
|
||||||
self.assertIn(event3, events)
|
assert event3 in events
|
||||||
|
|
||||||
# find events within 10 degrees of san francisco
|
# find events within 10 degrees of san francisco
|
||||||
point_and_distance = [[-122.415579, 37.7566023], 10]
|
point_and_distance = [[-122.415579, 37.7566023], 10]
|
||||||
events = self.Event.objects(location__within_distance=point_and_distance)
|
events = self.Event.objects(location__within_distance=point_and_distance)
|
||||||
self.assertEqual(events.count(), 1)
|
assert events.count() == 1
|
||||||
self.assertEqual(events[0], event2)
|
assert events[0] == event2
|
||||||
|
|
||||||
# find events within 1 degree of greenpoint, broolyn, nyc, ny
|
# find events within 1 degree of greenpoint, broolyn, nyc, ny
|
||||||
point_and_distance = [[-73.9509714, 40.7237134], 1]
|
point_and_distance = [[-73.9509714, 40.7237134], 1]
|
||||||
events = self.Event.objects(location__within_distance=point_and_distance)
|
events = self.Event.objects(location__within_distance=point_and_distance)
|
||||||
self.assertEqual(events.count(), 0)
|
assert events.count() == 0
|
||||||
|
|
||||||
# ensure ordering is respected by "within_distance"
|
# ensure ordering is respected by "within_distance"
|
||||||
point_and_distance = [[-87.67892, 41.9120459], 10]
|
point_and_distance = [[-87.67892, 41.9120459], 10]
|
||||||
events = self.Event.objects(location__within_distance=point_and_distance)
|
events = self.Event.objects(location__within_distance=point_and_distance)
|
||||||
events = events.order_by("-date")
|
events = events.order_by("-date")
|
||||||
self.assertEqual(events.count(), 2)
|
assert events.count() == 2
|
||||||
self.assertEqual(events[0], event3)
|
assert events[0] == event3
|
||||||
|
|
||||||
def test_within_box(self):
|
def test_within_box(self):
|
||||||
"""Ensure the "within_box" operator works."""
|
"""Ensure the "within_box" operator works."""
|
||||||
@@ -121,8 +118,8 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# check that within_box works
|
# check that within_box works
|
||||||
box = [(-125.0, 35.0), (-100.0, 40.0)]
|
box = [(-125.0, 35.0), (-100.0, 40.0)]
|
||||||
events = self.Event.objects(location__within_box=box)
|
events = self.Event.objects(location__within_box=box)
|
||||||
self.assertEqual(events.count(), 1)
|
assert events.count() == 1
|
||||||
self.assertEqual(events[0].id, event2.id)
|
assert events[0].id == event2.id
|
||||||
|
|
||||||
def test_within_polygon(self):
|
def test_within_polygon(self):
|
||||||
"""Ensure the "within_polygon" operator works."""
|
"""Ensure the "within_polygon" operator works."""
|
||||||
@@ -136,8 +133,8 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
(-87.656164, 41.898061),
|
(-87.656164, 41.898061),
|
||||||
]
|
]
|
||||||
events = self.Event.objects(location__within_polygon=polygon)
|
events = self.Event.objects(location__within_polygon=polygon)
|
||||||
self.assertEqual(events.count(), 1)
|
assert events.count() == 1
|
||||||
self.assertEqual(events[0].id, event1.id)
|
assert events[0].id == event1.id
|
||||||
|
|
||||||
polygon2 = [
|
polygon2 = [
|
||||||
(-1.742249, 54.033586),
|
(-1.742249, 54.033586),
|
||||||
@@ -145,7 +142,7 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
(-4.40094, 53.389881),
|
(-4.40094, 53.389881),
|
||||||
]
|
]
|
||||||
events = self.Event.objects(location__within_polygon=polygon2)
|
events = self.Event.objects(location__within_polygon=polygon2)
|
||||||
self.assertEqual(events.count(), 0)
|
assert events.count() == 0
|
||||||
|
|
||||||
def test_2dsphere_near(self):
|
def test_2dsphere_near(self):
|
||||||
"""Make sure the "near" operator works with a PointField, which
|
"""Make sure the "near" operator works with a PointField, which
|
||||||
@@ -157,14 +154,14 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# note that "near" will show the san francisco event, too,
|
# note that "near" will show the san francisco event, too,
|
||||||
# although it sorts to last.
|
# although it sorts to last.
|
||||||
events = self.Event.objects(location__near=[-87.67892, 41.9120459])
|
events = self.Event.objects(location__near=[-87.67892, 41.9120459])
|
||||||
self.assertEqual(events.count(), 3)
|
assert events.count() == 3
|
||||||
self.assertEqual(list(events), [event1, event3, event2])
|
assert list(events) == [event1, event3, event2]
|
||||||
|
|
||||||
# ensure ordering is respected by "near"
|
# ensure ordering is respected by "near"
|
||||||
events = self.Event.objects(location__near=[-87.67892, 41.9120459])
|
events = self.Event.objects(location__near=[-87.67892, 41.9120459])
|
||||||
events = events.order_by("-date")
|
events = events.order_by("-date")
|
||||||
self.assertEqual(events.count(), 3)
|
assert events.count() == 3
|
||||||
self.assertEqual(list(events), [event3, event1, event2])
|
assert list(events) == [event3, event1, event2]
|
||||||
|
|
||||||
def test_2dsphere_near_and_max_distance(self):
|
def test_2dsphere_near_and_max_distance(self):
|
||||||
"""Ensure the "max_distance" operator works alongside the "near"
|
"""Ensure the "max_distance" operator works alongside the "near"
|
||||||
@@ -175,21 +172,21 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# find events within 10km of san francisco
|
# find events within 10km of san francisco
|
||||||
point = [-122.415579, 37.7566023]
|
point = [-122.415579, 37.7566023]
|
||||||
events = self.Event.objects(location__near=point, location__max_distance=10000)
|
events = self.Event.objects(location__near=point, location__max_distance=10000)
|
||||||
self.assertEqual(events.count(), 1)
|
assert events.count() == 1
|
||||||
self.assertEqual(events[0], event2)
|
assert events[0] == event2
|
||||||
|
|
||||||
# find events within 1km of greenpoint, broolyn, nyc, ny
|
# find events within 1km of greenpoint, broolyn, nyc, ny
|
||||||
events = self.Event.objects(
|
events = self.Event.objects(
|
||||||
location__near=[-73.9509714, 40.7237134], location__max_distance=1000
|
location__near=[-73.9509714, 40.7237134], location__max_distance=1000
|
||||||
)
|
)
|
||||||
self.assertEqual(events.count(), 0)
|
assert events.count() == 0
|
||||||
|
|
||||||
# ensure ordering is respected by "near"
|
# ensure ordering is respected by "near"
|
||||||
events = self.Event.objects(
|
events = self.Event.objects(
|
||||||
location__near=[-87.67892, 41.9120459], location__max_distance=10000
|
location__near=[-87.67892, 41.9120459], location__max_distance=10000
|
||||||
).order_by("-date")
|
).order_by("-date")
|
||||||
self.assertEqual(events.count(), 2)
|
assert events.count() == 2
|
||||||
self.assertEqual(events[0], event3)
|
assert events[0] == event3
|
||||||
|
|
||||||
def test_2dsphere_geo_within_box(self):
|
def test_2dsphere_geo_within_box(self):
|
||||||
"""Ensure the "geo_within_box" operator works with a 2dsphere
|
"""Ensure the "geo_within_box" operator works with a 2dsphere
|
||||||
@@ -200,8 +197,8 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# check that within_box works
|
# check that within_box works
|
||||||
box = [(-125.0, 35.0), (-100.0, 40.0)]
|
box = [(-125.0, 35.0), (-100.0, 40.0)]
|
||||||
events = self.Event.objects(location__geo_within_box=box)
|
events = self.Event.objects(location__geo_within_box=box)
|
||||||
self.assertEqual(events.count(), 1)
|
assert events.count() == 1
|
||||||
self.assertEqual(events[0].id, event2.id)
|
assert events[0].id == event2.id
|
||||||
|
|
||||||
def test_2dsphere_geo_within_polygon(self):
|
def test_2dsphere_geo_within_polygon(self):
|
||||||
"""Ensure the "geo_within_polygon" operator works with a
|
"""Ensure the "geo_within_polygon" operator works with a
|
||||||
@@ -217,8 +214,8 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
(-87.656164, 41.898061),
|
(-87.656164, 41.898061),
|
||||||
]
|
]
|
||||||
events = self.Event.objects(location__geo_within_polygon=polygon)
|
events = self.Event.objects(location__geo_within_polygon=polygon)
|
||||||
self.assertEqual(events.count(), 1)
|
assert events.count() == 1
|
||||||
self.assertEqual(events[0].id, event1.id)
|
assert events[0].id == event1.id
|
||||||
|
|
||||||
polygon2 = [
|
polygon2 = [
|
||||||
(-1.742249, 54.033586),
|
(-1.742249, 54.033586),
|
||||||
@@ -226,7 +223,7 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
(-4.40094, 53.389881),
|
(-4.40094, 53.389881),
|
||||||
]
|
]
|
||||||
events = self.Event.objects(location__geo_within_polygon=polygon2)
|
events = self.Event.objects(location__geo_within_polygon=polygon2)
|
||||||
self.assertEqual(events.count(), 0)
|
assert events.count() == 0
|
||||||
|
|
||||||
def test_2dsphere_near_and_min_max_distance(self):
|
def test_2dsphere_near_and_min_max_distance(self):
|
||||||
"""Ensure "min_distace" and "max_distance" operators work well
|
"""Ensure "min_distace" and "max_distance" operators work well
|
||||||
@@ -240,15 +237,15 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
location__min_distance=1000,
|
location__min_distance=1000,
|
||||||
location__max_distance=10000,
|
location__max_distance=10000,
|
||||||
).order_by("-date")
|
).order_by("-date")
|
||||||
self.assertEqual(events.count(), 1)
|
assert events.count() == 1
|
||||||
self.assertEqual(events[0], event3)
|
assert events[0] == event3
|
||||||
|
|
||||||
# ensure ordering is respected by "near" with "min_distance"
|
# ensure ordering is respected by "near" with "min_distance"
|
||||||
events = self.Event.objects(
|
events = self.Event.objects(
|
||||||
location__near=[-87.67892, 41.9120459], location__min_distance=10000
|
location__near=[-87.67892, 41.9120459], location__min_distance=10000
|
||||||
).order_by("-date")
|
).order_by("-date")
|
||||||
self.assertEqual(events.count(), 1)
|
assert events.count() == 1
|
||||||
self.assertEqual(events[0], event2)
|
assert events[0] == event2
|
||||||
|
|
||||||
def test_2dsphere_geo_within_center(self):
|
def test_2dsphere_geo_within_center(self):
|
||||||
"""Make sure the "geo_within_center" operator works with a
|
"""Make sure the "geo_within_center" operator works with a
|
||||||
@@ -259,11 +256,11 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# find events within 5 degrees of pitchfork office, chicago
|
# find events within 5 degrees of pitchfork office, chicago
|
||||||
point_and_distance = [[-87.67892, 41.9120459], 2]
|
point_and_distance = [[-87.67892, 41.9120459], 2]
|
||||||
events = self.Event.objects(location__geo_within_center=point_and_distance)
|
events = self.Event.objects(location__geo_within_center=point_and_distance)
|
||||||
self.assertEqual(events.count(), 2)
|
assert events.count() == 2
|
||||||
events = list(events)
|
events = list(events)
|
||||||
self.assertNotIn(event2, events)
|
assert event2 not in events
|
||||||
self.assertIn(event1, events)
|
assert event1 in events
|
||||||
self.assertIn(event3, events)
|
assert event3 in events
|
||||||
|
|
||||||
def _test_embedded(self, point_field_class):
|
def _test_embedded(self, point_field_class):
|
||||||
"""Helper test method ensuring given point field class works
|
"""Helper test method ensuring given point field class works
|
||||||
@@ -293,8 +290,8 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# note that "near" will show the san francisco event, too,
|
# note that "near" will show the san francisco event, too,
|
||||||
# although it sorts to last.
|
# although it sorts to last.
|
||||||
events = Event.objects(venue__location__near=[-87.67892, 41.9120459])
|
events = Event.objects(venue__location__near=[-87.67892, 41.9120459])
|
||||||
self.assertEqual(events.count(), 3)
|
assert events.count() == 3
|
||||||
self.assertEqual(list(events), [event1, event3, event2])
|
assert list(events) == [event1, event3, event2]
|
||||||
|
|
||||||
def test_geo_spatial_embedded(self):
|
def test_geo_spatial_embedded(self):
|
||||||
"""Make sure GeoPointField works properly in an embedded document."""
|
"""Make sure GeoPointField works properly in an embedded document."""
|
||||||
@@ -322,55 +319,55 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# Finds both points because they are within 60 km of the reference
|
# Finds both points because they are within 60 km of the reference
|
||||||
# point equidistant between them.
|
# point equidistant between them.
|
||||||
points = Point.objects(location__near_sphere=[-122, 37.5])
|
points = Point.objects(location__near_sphere=[-122, 37.5])
|
||||||
self.assertEqual(points.count(), 2)
|
assert points.count() == 2
|
||||||
|
|
||||||
# Same behavior for _within_spherical_distance
|
# Same behavior for _within_spherical_distance
|
||||||
points = Point.objects(
|
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)
|
assert points.count() == 2
|
||||||
|
|
||||||
points = Point.objects(
|
points = Point.objects(
|
||||||
location__near_sphere=[-122, 37.5], location__max_distance=60 / earth_radius
|
location__near_sphere=[-122, 37.5], location__max_distance=60 / earth_radius
|
||||||
)
|
)
|
||||||
self.assertEqual(points.count(), 2)
|
assert points.count() == 2
|
||||||
|
|
||||||
# Test query works with max_distance, being farer from one point
|
# Test query works with max_distance, being farer from one point
|
||||||
points = Point.objects(
|
points = Point.objects(
|
||||||
location__near_sphere=[-122, 37.8], location__max_distance=60 / earth_radius
|
location__near_sphere=[-122, 37.8], location__max_distance=60 / earth_radius
|
||||||
)
|
)
|
||||||
close_point = points.first()
|
close_point = points.first()
|
||||||
self.assertEqual(points.count(), 1)
|
assert points.count() == 1
|
||||||
|
|
||||||
# Test query works with min_distance, being farer from one point
|
# Test query works with min_distance, being farer from one point
|
||||||
points = Point.objects(
|
points = Point.objects(
|
||||||
location__near_sphere=[-122, 37.8], location__min_distance=60 / earth_radius
|
location__near_sphere=[-122, 37.8], location__min_distance=60 / earth_radius
|
||||||
)
|
)
|
||||||
self.assertEqual(points.count(), 1)
|
assert points.count() == 1
|
||||||
far_point = points.first()
|
far_point = points.first()
|
||||||
self.assertNotEqual(close_point, far_point)
|
assert close_point != far_point
|
||||||
|
|
||||||
# Finds both points, but orders the north point first because it's
|
# Finds both points, but orders the north point first because it's
|
||||||
# closer to the reference point to the north.
|
# closer to the reference point to the north.
|
||||||
points = Point.objects(location__near_sphere=[-122, 38.5])
|
points = Point.objects(location__near_sphere=[-122, 38.5])
|
||||||
self.assertEqual(points.count(), 2)
|
assert points.count() == 2
|
||||||
self.assertEqual(points[0].id, north_point.id)
|
assert points[0].id == north_point.id
|
||||||
self.assertEqual(points[1].id, south_point.id)
|
assert points[1].id == south_point.id
|
||||||
|
|
||||||
# Finds both points, but orders the south point first because it's
|
# Finds both points, but orders the south point first because it's
|
||||||
# closer to the reference point to the south.
|
# closer to the reference point to the south.
|
||||||
points = Point.objects(location__near_sphere=[-122, 36.5])
|
points = Point.objects(location__near_sphere=[-122, 36.5])
|
||||||
self.assertEqual(points.count(), 2)
|
assert points.count() == 2
|
||||||
self.assertEqual(points[0].id, south_point.id)
|
assert points[0].id == south_point.id
|
||||||
self.assertEqual(points[1].id, north_point.id)
|
assert points[1].id == north_point.id
|
||||||
|
|
||||||
# Finds only one point because only the first point is within 60km of
|
# Finds only one point because only the first point is within 60km of
|
||||||
# the reference point to the south.
|
# the reference point to the south.
|
||||||
points = Point.objects(
|
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)
|
assert points.count() == 1
|
||||||
self.assertEqual(points[0].id, south_point.id)
|
assert points[0].id == south_point.id
|
||||||
|
|
||||||
def test_linestring(self):
|
def test_linestring(self):
|
||||||
class Road(Document):
|
class Road(Document):
|
||||||
@@ -384,13 +381,13 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# near
|
# near
|
||||||
point = {"type": "Point", "coordinates": [40, 5]}
|
point = {"type": "Point", "coordinates": [40, 5]}
|
||||||
roads = Road.objects.filter(line__near=point["coordinates"]).count()
|
roads = Road.objects.filter(line__near=point["coordinates"]).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(line__near=point).count()
|
roads = Road.objects.filter(line__near=point).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(line__near={"$geometry": point}).count()
|
roads = Road.objects.filter(line__near={"$geometry": point}).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
# Within
|
# Within
|
||||||
polygon = {
|
polygon = {
|
||||||
@@ -398,37 +395,37 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
"coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
|
"coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
|
||||||
}
|
}
|
||||||
roads = Road.objects.filter(line__geo_within=polygon["coordinates"]).count()
|
roads = Road.objects.filter(line__geo_within=polygon["coordinates"]).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(line__geo_within=polygon).count()
|
roads = Road.objects.filter(line__geo_within=polygon).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(line__geo_within={"$geometry": polygon}).count()
|
roads = Road.objects.filter(line__geo_within={"$geometry": polygon}).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
# Intersects
|
# Intersects
|
||||||
line = {"type": "LineString", "coordinates": [[40, 5], [40, 6]]}
|
line = {"type": "LineString", "coordinates": [[40, 5], [40, 6]]}
|
||||||
roads = Road.objects.filter(line__geo_intersects=line["coordinates"]).count()
|
roads = Road.objects.filter(line__geo_intersects=line["coordinates"]).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(line__geo_intersects=line).count()
|
roads = Road.objects.filter(line__geo_intersects=line).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(line__geo_intersects={"$geometry": line}).count()
|
roads = Road.objects.filter(line__geo_intersects={"$geometry": line}).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
polygon = {
|
polygon = {
|
||||||
"type": "Polygon",
|
"type": "Polygon",
|
||||||
"coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
|
"coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
|
||||||
}
|
}
|
||||||
roads = Road.objects.filter(line__geo_intersects=polygon["coordinates"]).count()
|
roads = Road.objects.filter(line__geo_intersects=polygon["coordinates"]).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(line__geo_intersects=polygon).count()
|
roads = Road.objects.filter(line__geo_intersects=polygon).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(line__geo_intersects={"$geometry": polygon}).count()
|
roads = Road.objects.filter(line__geo_intersects={"$geometry": polygon}).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
def test_polygon(self):
|
def test_polygon(self):
|
||||||
class Road(Document):
|
class Road(Document):
|
||||||
@@ -442,13 +439,13 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
# near
|
# near
|
||||||
point = {"type": "Point", "coordinates": [40, 5]}
|
point = {"type": "Point", "coordinates": [40, 5]}
|
||||||
roads = Road.objects.filter(poly__near=point["coordinates"]).count()
|
roads = Road.objects.filter(poly__near=point["coordinates"]).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(poly__near=point).count()
|
roads = Road.objects.filter(poly__near=point).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(poly__near={"$geometry": point}).count()
|
roads = Road.objects.filter(poly__near={"$geometry": point}).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
# Within
|
# Within
|
||||||
polygon = {
|
polygon = {
|
||||||
@@ -456,37 +453,37 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
"coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
|
"coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
|
||||||
}
|
}
|
||||||
roads = Road.objects.filter(poly__geo_within=polygon["coordinates"]).count()
|
roads = Road.objects.filter(poly__geo_within=polygon["coordinates"]).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(poly__geo_within=polygon).count()
|
roads = Road.objects.filter(poly__geo_within=polygon).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(poly__geo_within={"$geometry": polygon}).count()
|
roads = Road.objects.filter(poly__geo_within={"$geometry": polygon}).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
# Intersects
|
# Intersects
|
||||||
line = {"type": "LineString", "coordinates": [[40, 5], [41, 6]]}
|
line = {"type": "LineString", "coordinates": [[40, 5], [41, 6]]}
|
||||||
roads = Road.objects.filter(poly__geo_intersects=line["coordinates"]).count()
|
roads = Road.objects.filter(poly__geo_intersects=line["coordinates"]).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(poly__geo_intersects=line).count()
|
roads = Road.objects.filter(poly__geo_intersects=line).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(poly__geo_intersects={"$geometry": line}).count()
|
roads = Road.objects.filter(poly__geo_intersects={"$geometry": line}).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
polygon = {
|
polygon = {
|
||||||
"type": "Polygon",
|
"type": "Polygon",
|
||||||
"coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
|
"coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
|
||||||
}
|
}
|
||||||
roads = Road.objects.filter(poly__geo_intersects=polygon["coordinates"]).count()
|
roads = Road.objects.filter(poly__geo_intersects=polygon["coordinates"]).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(poly__geo_intersects=polygon).count()
|
roads = Road.objects.filter(poly__geo_intersects=polygon).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
roads = Road.objects.filter(poly__geo_intersects={"$geometry": polygon}).count()
|
roads = Road.objects.filter(poly__geo_intersects={"$geometry": polygon}).count()
|
||||||
self.assertEqual(1, roads)
|
assert 1 == roads
|
||||||
|
|
||||||
def test_aspymongo_with_only(self):
|
def test_aspymongo_with_only(self):
|
||||||
"""Ensure as_pymongo works with only"""
|
"""Ensure as_pymongo works with only"""
|
||||||
@@ -498,13 +495,10 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
p = Place(location=[24.946861267089844, 60.16311983618494])
|
p = Place(location=[24.946861267089844, 60.16311983618494])
|
||||||
p.save()
|
p.save()
|
||||||
qs = Place.objects().only("location")
|
qs = Place.objects().only("location")
|
||||||
self.assertDictEqual(
|
assert qs.as_pymongo()[0]["location"] == {
|
||||||
qs.as_pymongo()[0]["location"],
|
u"type": u"Point",
|
||||||
{
|
u"coordinates": [24.946861267089844, 60.16311983618494],
|
||||||
u"type": u"Point",
|
}
|
||||||
u"coordinates": [24.946861267089844, 60.16311983618494],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_2dsphere_point_sets_correctly(self):
|
def test_2dsphere_point_sets_correctly(self):
|
||||||
class Location(Document):
|
class Location(Document):
|
||||||
@@ -514,11 +508,11 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
|
|
||||||
Location(loc=[1, 2]).save()
|
Location(loc=[1, 2]).save()
|
||||||
loc = Location.objects.as_pymongo()[0]
|
loc = Location.objects.as_pymongo()[0]
|
||||||
self.assertEqual(loc["loc"], {"type": "Point", "coordinates": [1, 2]})
|
assert loc["loc"] == {"type": "Point", "coordinates": [1, 2]}
|
||||||
|
|
||||||
Location.objects.update(set__loc=[2, 1])
|
Location.objects.update(set__loc=[2, 1])
|
||||||
loc = Location.objects.as_pymongo()[0]
|
loc = Location.objects.as_pymongo()[0]
|
||||||
self.assertEqual(loc["loc"], {"type": "Point", "coordinates": [2, 1]})
|
assert loc["loc"] == {"type": "Point", "coordinates": [2, 1]}
|
||||||
|
|
||||||
def test_2dsphere_linestring_sets_correctly(self):
|
def test_2dsphere_linestring_sets_correctly(self):
|
||||||
class Location(Document):
|
class Location(Document):
|
||||||
@@ -528,15 +522,11 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
|
|
||||||
Location(line=[[1, 2], [2, 2]]).save()
|
Location(line=[[1, 2], [2, 2]]).save()
|
||||||
loc = Location.objects.as_pymongo()[0]
|
loc = Location.objects.as_pymongo()[0]
|
||||||
self.assertEqual(
|
assert loc["line"] == {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}
|
||||||
loc["line"], {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}
|
|
||||||
)
|
|
||||||
|
|
||||||
Location.objects.update(set__line=[[2, 1], [1, 2]])
|
Location.objects.update(set__line=[[2, 1], [1, 2]])
|
||||||
loc = Location.objects.as_pymongo()[0]
|
loc = Location.objects.as_pymongo()[0]
|
||||||
self.assertEqual(
|
assert loc["line"] == {"type": "LineString", "coordinates": [[2, 1], [1, 2]]}
|
||||||
loc["line"], {"type": "LineString", "coordinates": [[2, 1], [1, 2]]}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_geojson_PolygonField(self):
|
def test_geojson_PolygonField(self):
|
||||||
class Location(Document):
|
class Location(Document):
|
||||||
@@ -546,17 +536,17 @@ class GeoQueriesTest(MongoDBTestCase):
|
|||||||
|
|
||||||
Location(poly=[[[40, 5], [40, 6], [41, 6], [40, 5]]]).save()
|
Location(poly=[[[40, 5], [40, 6], [41, 6], [40, 5]]]).save()
|
||||||
loc = Location.objects.as_pymongo()[0]
|
loc = Location.objects.as_pymongo()[0]
|
||||||
self.assertEqual(
|
assert loc["poly"] == {
|
||||||
loc["poly"],
|
"type": "Polygon",
|
||||||
{"type": "Polygon", "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]},
|
"coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
|
||||||
)
|
}
|
||||||
|
|
||||||
Location.objects.update(set__poly=[[[40, 4], [40, 6], [41, 6], [40, 4]]])
|
Location.objects.update(set__poly=[[[40, 4], [40, 6], [41, 6], [40, 4]]])
|
||||||
loc = Location.objects.as_pymongo()[0]
|
loc = Location.objects.as_pymongo()[0]
|
||||||
self.assertEqual(
|
assert loc["poly"] == {
|
||||||
loc["poly"],
|
"type": "Polygon",
|
||||||
{"type": "Polygon", "coordinates": [[[40, 4], [40, 6], [41, 6], [40, 4]]]},
|
"coordinates": [[[40, 4], [40, 6], [41, 6], [40, 4]]],
|
||||||
)
|
}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from mongoengine import connect, Document, IntField, StringField, ListField
|
from mongoengine import Document, IntField, ListField, StringField, connect
|
||||||
|
|
||||||
__all__ = ("FindAndModifyTest",)
|
|
||||||
|
|
||||||
|
|
||||||
class Doc(Document):
|
class Doc(Document):
|
||||||
@@ -10,20 +8,20 @@ class Doc(Document):
|
|||||||
value = IntField()
|
value = IntField()
|
||||||
|
|
||||||
|
|
||||||
class FindAndModifyTest(unittest.TestCase):
|
class TestFindAndModify(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
connect(db="mongoenginetest")
|
connect(db="mongoenginetest")
|
||||||
Doc.drop_collection()
|
Doc.drop_collection()
|
||||||
|
|
||||||
def assertDbEqual(self, docs):
|
def assertDbEqual(self, docs):
|
||||||
self.assertEqual(list(Doc._collection.find().sort("id")), docs)
|
assert list(Doc._collection.find().sort("id")) == docs
|
||||||
|
|
||||||
def test_modify(self):
|
def test_modify(self):
|
||||||
Doc(id=0, value=0).save()
|
Doc(id=0, value=0).save()
|
||||||
doc = Doc(id=1, value=1).save()
|
doc = Doc(id=1, value=1).save()
|
||||||
|
|
||||||
old_doc = Doc.objects(id=1).modify(set__value=-1)
|
old_doc = Doc.objects(id=1).modify(set__value=-1)
|
||||||
self.assertEqual(old_doc.to_json(), doc.to_json())
|
assert old_doc.to_json() == doc.to_json()
|
||||||
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
|
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
|
||||||
|
|
||||||
def test_modify_with_new(self):
|
def test_modify_with_new(self):
|
||||||
@@ -32,18 +30,18 @@ class FindAndModifyTest(unittest.TestCase):
|
|||||||
|
|
||||||
new_doc = Doc.objects(id=1).modify(set__value=-1, new=True)
|
new_doc = Doc.objects(id=1).modify(set__value=-1, new=True)
|
||||||
doc.value = -1
|
doc.value = -1
|
||||||
self.assertEqual(new_doc.to_json(), doc.to_json())
|
assert new_doc.to_json() == doc.to_json()
|
||||||
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
|
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
|
||||||
|
|
||||||
def test_modify_not_existing(self):
|
def test_modify_not_existing(self):
|
||||||
Doc(id=0, value=0).save()
|
Doc(id=0, value=0).save()
|
||||||
self.assertEqual(Doc.objects(id=1).modify(set__value=-1), None)
|
assert Doc.objects(id=1).modify(set__value=-1) is None
|
||||||
self.assertDbEqual([{"_id": 0, "value": 0}])
|
self.assertDbEqual([{"_id": 0, "value": 0}])
|
||||||
|
|
||||||
def test_modify_with_upsert(self):
|
def test_modify_with_upsert(self):
|
||||||
Doc(id=0, value=0).save()
|
Doc(id=0, value=0).save()
|
||||||
old_doc = Doc.objects(id=1).modify(set__value=1, upsert=True)
|
old_doc = Doc.objects(id=1).modify(set__value=1, upsert=True)
|
||||||
self.assertEqual(old_doc, None)
|
assert old_doc is None
|
||||||
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": 1}])
|
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": 1}])
|
||||||
|
|
||||||
def test_modify_with_upsert_existing(self):
|
def test_modify_with_upsert_existing(self):
|
||||||
@@ -51,13 +49,13 @@ class FindAndModifyTest(unittest.TestCase):
|
|||||||
doc = Doc(id=1, value=1).save()
|
doc = Doc(id=1, value=1).save()
|
||||||
|
|
||||||
old_doc = Doc.objects(id=1).modify(set__value=-1, upsert=True)
|
old_doc = Doc.objects(id=1).modify(set__value=-1, upsert=True)
|
||||||
self.assertEqual(old_doc.to_json(), doc.to_json())
|
assert old_doc.to_json() == doc.to_json()
|
||||||
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
|
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
|
||||||
|
|
||||||
def test_modify_with_upsert_with_new(self):
|
def test_modify_with_upsert_with_new(self):
|
||||||
Doc(id=0, value=0).save()
|
Doc(id=0, value=0).save()
|
||||||
new_doc = Doc.objects(id=1).modify(upsert=True, new=True, set__value=1)
|
new_doc = Doc.objects(id=1).modify(upsert=True, new=True, set__value=1)
|
||||||
self.assertEqual(new_doc.to_mongo(), {"_id": 1, "value": 1})
|
assert new_doc.to_mongo() == {"_id": 1, "value": 1}
|
||||||
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": 1}])
|
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": 1}])
|
||||||
|
|
||||||
def test_modify_with_remove(self):
|
def test_modify_with_remove(self):
|
||||||
@@ -65,12 +63,12 @@ class FindAndModifyTest(unittest.TestCase):
|
|||||||
doc = Doc(id=1, value=1).save()
|
doc = Doc(id=1, value=1).save()
|
||||||
|
|
||||||
old_doc = Doc.objects(id=1).modify(remove=True)
|
old_doc = Doc.objects(id=1).modify(remove=True)
|
||||||
self.assertEqual(old_doc.to_json(), doc.to_json())
|
assert old_doc.to_json() == doc.to_json()
|
||||||
self.assertDbEqual([{"_id": 0, "value": 0}])
|
self.assertDbEqual([{"_id": 0, "value": 0}])
|
||||||
|
|
||||||
def test_find_and_modify_with_remove_not_existing(self):
|
def test_find_and_modify_with_remove_not_existing(self):
|
||||||
Doc(id=0, value=0).save()
|
Doc(id=0, value=0).save()
|
||||||
self.assertEqual(Doc.objects(id=1).modify(remove=True), None)
|
assert Doc.objects(id=1).modify(remove=True) is None
|
||||||
self.assertDbEqual([{"_id": 0, "value": 0}])
|
self.assertDbEqual([{"_id": 0, "value": 0}])
|
||||||
|
|
||||||
def test_modify_with_order_by(self):
|
def test_modify_with_order_by(self):
|
||||||
@@ -80,7 +78,7 @@ class FindAndModifyTest(unittest.TestCase):
|
|||||||
doc = Doc(id=3, value=0).save()
|
doc = Doc(id=3, value=0).save()
|
||||||
|
|
||||||
old_doc = Doc.objects().order_by("-id").modify(set__value=-1)
|
old_doc = Doc.objects().order_by("-id").modify(set__value=-1)
|
||||||
self.assertEqual(old_doc.to_json(), doc.to_json())
|
assert old_doc.to_json() == doc.to_json()
|
||||||
self.assertDbEqual(
|
self.assertDbEqual(
|
||||||
[
|
[
|
||||||
{"_id": 0, "value": 3},
|
{"_id": 0, "value": 3},
|
||||||
@@ -95,7 +93,7 @@ class FindAndModifyTest(unittest.TestCase):
|
|||||||
Doc(id=1, value=1).save()
|
Doc(id=1, value=1).save()
|
||||||
|
|
||||||
old_doc = Doc.objects(id=1).only("id").modify(set__value=-1)
|
old_doc = Doc.objects(id=1).only("id").modify(set__value=-1)
|
||||||
self.assertEqual(old_doc.to_mongo(), {"_id": 1})
|
assert old_doc.to_mongo() == {"_id": 1}
|
||||||
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
|
self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
|
||||||
|
|
||||||
def test_modify_with_push(self):
|
def test_modify_with_push(self):
|
||||||
@@ -108,23 +106,23 @@ class FindAndModifyTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Push a new tag via modify with new=False (default).
|
# Push a new tag via modify with new=False (default).
|
||||||
BlogPost(id=blog.id).modify(push__tags="code")
|
BlogPost(id=blog.id).modify(push__tags="code")
|
||||||
self.assertEqual(blog.tags, [])
|
assert blog.tags == []
|
||||||
blog.reload()
|
blog.reload()
|
||||||
self.assertEqual(blog.tags, ["code"])
|
assert blog.tags == ["code"]
|
||||||
|
|
||||||
# Push a new tag via modify with new=True.
|
# Push a new tag via modify with new=True.
|
||||||
blog = BlogPost.objects(id=blog.id).modify(push__tags="java", new=True)
|
blog = BlogPost.objects(id=blog.id).modify(push__tags="java", new=True)
|
||||||
self.assertEqual(blog.tags, ["code", "java"])
|
assert blog.tags == ["code", "java"]
|
||||||
|
|
||||||
# Push a new tag with a positional argument.
|
# Push a new tag with a positional argument.
|
||||||
blog = BlogPost.objects(id=blog.id).modify(push__tags__0="python", new=True)
|
blog = BlogPost.objects(id=blog.id).modify(push__tags__0="python", new=True)
|
||||||
self.assertEqual(blog.tags, ["python", "code", "java"])
|
assert blog.tags == ["python", "code", "java"]
|
||||||
|
|
||||||
# Push multiple new tags with a positional argument.
|
# Push multiple new tags with a positional argument.
|
||||||
blog = BlogPost.objects(id=blog.id).modify(
|
blog = BlogPost.objects(id=blog.id).modify(
|
||||||
push__tags__1=["go", "rust"], new=True
|
push__tags__1=["go", "rust"], new=True
|
||||||
)
|
)
|
||||||
self.assertEqual(blog.tags, ["python", "go", "rust", "code", "java"])
|
assert blog.tags == ["python", "go", "rust", "code", "java"]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import pickle
|
import pickle
|
||||||
import unittest
|
import unittest
|
||||||
from pymongo.mongo_client import MongoClient
|
|
||||||
from mongoengine import Document, StringField, IntField
|
|
||||||
from mongoengine.connection import connect
|
|
||||||
|
|
||||||
__author__ = "stas"
|
from mongoengine import Document, IntField, StringField
|
||||||
|
from mongoengine.connection import connect
|
||||||
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -12,7 +11,7 @@ class Person(Document):
|
|||||||
age = IntField()
|
age = IntField()
|
||||||
|
|
||||||
|
|
||||||
class TestQuerysetPickable(unittest.TestCase):
|
class TestQuerysetPickable(MongoDBTestCase):
|
||||||
"""
|
"""
|
||||||
Test for adding pickling support for QuerySet instances
|
Test for adding pickling support for QuerySet instances
|
||||||
See issue https://github.com/MongoEngine/mongoengine/issues/442
|
See issue https://github.com/MongoEngine/mongoengine/issues/442
|
||||||
@@ -20,11 +19,6 @@ class TestQuerysetPickable(unittest.TestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestQuerysetPickable, self).setUp()
|
super(TestQuerysetPickable, self).setUp()
|
||||||
|
|
||||||
connection = connect(db="test") # type: pymongo.mongo_client.MongoClient
|
|
||||||
|
|
||||||
connection.drop_database("test")
|
|
||||||
|
|
||||||
self.john = Person.objects.create(name="John", age=21)
|
self.john = Person.objects.create(name="John", age=21)
|
||||||
|
|
||||||
def test_picke_simple_qs(self):
|
def test_picke_simple_qs(self):
|
||||||
@@ -43,13 +37,13 @@ class TestQuerysetPickable(unittest.TestCase):
|
|||||||
|
|
||||||
loadedQs = self._get_loaded(qs)
|
loadedQs = self._get_loaded(qs)
|
||||||
|
|
||||||
self.assertEqual(qs.count(), loadedQs.count())
|
assert qs.count() == loadedQs.count()
|
||||||
|
|
||||||
# can update loadedQs
|
# can update loadedQs
|
||||||
loadedQs.update(age=23)
|
loadedQs.update(age=23)
|
||||||
|
|
||||||
# check
|
# check
|
||||||
self.assertEqual(Person.objects.first().age, 23)
|
assert Person.objects.first().age == 23
|
||||||
|
|
||||||
def test_pickle_support_filtration(self):
|
def test_pickle_support_filtration(self):
|
||||||
Person.objects.create(name="Alice", age=22)
|
Person.objects.create(name="Alice", age=22)
|
||||||
@@ -57,9 +51,9 @@ class TestQuerysetPickable(unittest.TestCase):
|
|||||||
Person.objects.create(name="Bob", age=23)
|
Person.objects.create(name="Bob", age=23)
|
||||||
|
|
||||||
qs = Person.objects.filter(age__gte=22)
|
qs = Person.objects.filter(age__gte=22)
|
||||||
self.assertEqual(qs.count(), 2)
|
assert qs.count() == 2
|
||||||
|
|
||||||
loaded = self._get_loaded(qs)
|
loaded = self._get_loaded(qs)
|
||||||
|
|
||||||
self.assertEqual(loaded.count(), 2)
|
assert loaded.count() == 2
|
||||||
self.assertEqual(loaded.filter(name="Bob").first().age, 23)
|
assert loaded.filter(name="Bob").first().age == 23
|
||||||
File diff suppressed because it is too large
Load Diff
255
tests/queryset/test_queryset_aggregation.py
Normal file
255
tests/queryset/test_queryset_aggregation.py
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
from pymongo.read_preferences import ReadPreference
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestQuerysetAggregate(MongoDBTestCase):
|
||||||
|
def test_read_preference_aggregation_framework(self):
|
||||||
|
class Bar(Document):
|
||||||
|
txt = StringField()
|
||||||
|
|
||||||
|
meta = {"indexes": ["txt"]}
|
||||||
|
|
||||||
|
# Aggregates with read_preference
|
||||||
|
pipeline = []
|
||||||
|
bars = Bar.objects.read_preference(
|
||||||
|
ReadPreference.SECONDARY_PREFERRED
|
||||||
|
).aggregate(pipeline)
|
||||||
|
assert (
|
||||||
|
bars._CommandCursor__collection.read_preference
|
||||||
|
== ReadPreference.SECONDARY_PREFERRED
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_queryset_aggregation_framework(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Isabella Luanna", age=16)
|
||||||
|
p2 = Person(name="Wilson Junior", age=21)
|
||||||
|
p3 = Person(name="Sandra Mara", age=37)
|
||||||
|
Person.objects.insert([p1, p2, p3])
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
data = Person.objects(age__lte=22).aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [
|
||||||
|
{"_id": p1.pk, "name": "ISABELLA LUANNA"},
|
||||||
|
{"_id": p2.pk, "name": "WILSON JUNIOR"},
|
||||||
|
]
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
data = Person.objects(age__lte=22).order_by("-name").aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [
|
||||||
|
{"_id": p2.pk, "name": "WILSON JUNIOR"},
|
||||||
|
{"_id": p1.pk, "name": "ISABELLA LUANNA"},
|
||||||
|
]
|
||||||
|
|
||||||
|
pipeline = [
|
||||||
|
{"$group": {"_id": None, "total": {"$sum": 1}, "avg": {"$avg": "$age"}}}
|
||||||
|
]
|
||||||
|
data = (
|
||||||
|
Person.objects(age__gte=17, age__lte=40)
|
||||||
|
.order_by("-age")
|
||||||
|
.aggregate(pipeline)
|
||||||
|
)
|
||||||
|
assert list(data) == [{"_id": None, "avg": 29, "total": 2}]
|
||||||
|
|
||||||
|
pipeline = [{"$match": {"name": "Isabella Luanna"}}]
|
||||||
|
data = Person.objects().aggregate(pipeline)
|
||||||
|
assert list(data) == [{u"_id": p1.pk, u"age": 16, u"name": u"Isabella Luanna"}]
|
||||||
|
|
||||||
|
def test_queryset_aggregation_with_skip(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Isabella Luanna", age=16)
|
||||||
|
p2 = Person(name="Wilson Junior", age=21)
|
||||||
|
p3 = Person(name="Sandra Mara", age=37)
|
||||||
|
Person.objects.insert([p1, p2, p3])
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
data = Person.objects.skip(1).aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [
|
||||||
|
{"_id": p2.pk, "name": "WILSON JUNIOR"},
|
||||||
|
{"_id": p3.pk, "name": "SANDRA MARA"},
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_queryset_aggregation_with_limit(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Isabella Luanna", age=16)
|
||||||
|
p2 = Person(name="Wilson Junior", age=21)
|
||||||
|
p3 = Person(name="Sandra Mara", age=37)
|
||||||
|
Person.objects.insert([p1, p2, p3])
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
data = Person.objects.limit(1).aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [{"_id": p1.pk, "name": "ISABELLA LUANNA"}]
|
||||||
|
|
||||||
|
def test_queryset_aggregation_with_sort(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Isabella Luanna", age=16)
|
||||||
|
p2 = Person(name="Wilson Junior", age=21)
|
||||||
|
p3 = Person(name="Sandra Mara", age=37)
|
||||||
|
Person.objects.insert([p1, p2, p3])
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
data = Person.objects.order_by("name").aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [
|
||||||
|
{"_id": p1.pk, "name": "ISABELLA LUANNA"},
|
||||||
|
{"_id": p3.pk, "name": "SANDRA MARA"},
|
||||||
|
{"_id": p2.pk, "name": "WILSON JUNIOR"},
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_queryset_aggregation_with_skip_with_limit(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Isabella Luanna", age=16)
|
||||||
|
p2 = Person(name="Wilson Junior", age=21)
|
||||||
|
p3 = Person(name="Sandra Mara", age=37)
|
||||||
|
Person.objects.insert([p1, p2, p3])
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
data = list(Person.objects.skip(1).limit(1).aggregate(pipeline))
|
||||||
|
|
||||||
|
assert list(data) == [{"_id": p2.pk, "name": "WILSON JUNIOR"}]
|
||||||
|
|
||||||
|
# Make sure limit/skip chaining order has no impact
|
||||||
|
data2 = Person.objects.limit(1).skip(1).aggregate(pipeline)
|
||||||
|
|
||||||
|
assert data == list(data2)
|
||||||
|
|
||||||
|
def test_queryset_aggregation_with_sort_with_limit(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Isabella Luanna", age=16)
|
||||||
|
p2 = Person(name="Wilson Junior", age=21)
|
||||||
|
p3 = Person(name="Sandra Mara", age=37)
|
||||||
|
Person.objects.insert([p1, p2, p3])
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
data = Person.objects.order_by("name").limit(2).aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [
|
||||||
|
{"_id": p1.pk, "name": "ISABELLA LUANNA"},
|
||||||
|
{"_id": p3.pk, "name": "SANDRA MARA"},
|
||||||
|
]
|
||||||
|
|
||||||
|
# Verify adding limit/skip steps works as expected
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}, {"$limit": 1}]
|
||||||
|
data = Person.objects.order_by("name").limit(2).aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [{"_id": p1.pk, "name": "ISABELLA LUANNA"}]
|
||||||
|
|
||||||
|
pipeline = [
|
||||||
|
{"$project": {"name": {"$toUpper": "$name"}}},
|
||||||
|
{"$skip": 1},
|
||||||
|
{"$limit": 1},
|
||||||
|
]
|
||||||
|
data = Person.objects.order_by("name").limit(2).aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [{"_id": p3.pk, "name": "SANDRA MARA"}]
|
||||||
|
|
||||||
|
def test_queryset_aggregation_with_sort_with_skip(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Isabella Luanna", age=16)
|
||||||
|
p2 = Person(name="Wilson Junior", age=21)
|
||||||
|
p3 = Person(name="Sandra Mara", age=37)
|
||||||
|
Person.objects.insert([p1, p2, p3])
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
data = Person.objects.order_by("name").skip(2).aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [{"_id": p2.pk, "name": "WILSON JUNIOR"}]
|
||||||
|
|
||||||
|
def test_queryset_aggregation_with_sort_with_skip_with_limit(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Isabella Luanna", age=16)
|
||||||
|
p2 = Person(name="Wilson Junior", age=21)
|
||||||
|
p3 = Person(name="Sandra Mara", age=37)
|
||||||
|
Person.objects.insert([p1, p2, p3])
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
data = Person.objects.order_by("name").skip(1).limit(1).aggregate(pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [{"_id": p3.pk, "name": "SANDRA MARA"}]
|
||||||
|
|
||||||
|
def test_queryset_aggregation_deprecated_interface(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Isabella Luanna")
|
||||||
|
p2 = Person(name="Wilson Junior")
|
||||||
|
p3 = Person(name="Sandra Mara")
|
||||||
|
Person.objects.insert([p1, p2, p3])
|
||||||
|
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
|
||||||
|
|
||||||
|
# Make sure a warning is emitted
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("error", DeprecationWarning)
|
||||||
|
with self.assertRaises(DeprecationWarning):
|
||||||
|
Person.objects.order_by("name").limit(2).aggregate(*pipeline)
|
||||||
|
|
||||||
|
# Make sure old interface works as expected with a 1-step pipeline
|
||||||
|
data = Person.objects.order_by("name").limit(2).aggregate(*pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [
|
||||||
|
{"_id": p1.pk, "name": "ISABELLA LUANNA"},
|
||||||
|
{"_id": p3.pk, "name": "SANDRA MARA"},
|
||||||
|
]
|
||||||
|
|
||||||
|
# Make sure old interface works as expected with a 2-steps pipeline
|
||||||
|
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}, {"$limit": 1}]
|
||||||
|
data = Person.objects.order_by("name").limit(2).aggregate(*pipeline)
|
||||||
|
|
||||||
|
assert list(data) == [{"_id": p1.pk, "name": "ISABELLA LUANNA"}]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -1,37 +1,35 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from bson.son import SON
|
from bson.son import SON
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.queryset import Q, transform
|
from mongoengine.queryset import Q, transform
|
||||||
|
|
||||||
__all__ = ("TransformTest",)
|
|
||||||
|
|
||||||
|
class TestTransform(unittest.TestCase):
|
||||||
class TransformTest(unittest.TestCase):
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
connect(db="mongoenginetest")
|
connect(db="mongoenginetest")
|
||||||
|
|
||||||
def test_transform_query(self):
|
def test_transform_query(self):
|
||||||
"""Ensure that the _transform_query function operates correctly.
|
"""Ensure that the _transform_query function operates correctly.
|
||||||
"""
|
"""
|
||||||
self.assertEqual(
|
assert transform.query(name="test", age=30) == {"name": "test", "age": 30}
|
||||||
transform.query(name="test", age=30), {"name": "test", "age": 30}
|
assert transform.query(age__lt=30) == {"age": {"$lt": 30}}
|
||||||
)
|
assert transform.query(age__gt=20, age__lt=50) == {
|
||||||
self.assertEqual(transform.query(age__lt=30), {"age": {"$lt": 30}})
|
"age": {"$gt": 20, "$lt": 50}
|
||||||
self.assertEqual(
|
}
|
||||||
transform.query(age__gt=20, age__lt=50), {"age": {"$gt": 20, "$lt": 50}}
|
assert transform.query(age=20, age__gt=50) == {
|
||||||
)
|
"$and": [{"age": {"$gt": 50}}, {"age": 20}]
|
||||||
self.assertEqual(
|
}
|
||||||
transform.query(age=20, age__gt=50),
|
assert transform.query(friend__age__gte=30) == {"friend.age": {"$gte": 30}}
|
||||||
{"$and": [{"age": {"$gt": 50}}, {"age": 20}]},
|
assert transform.query(name__exists=True) == {"name": {"$exists": True}}
|
||||||
)
|
assert transform.query(name=["Mark"], __raw__={"name": {"$in": "Tom"}}) == {
|
||||||
self.assertEqual(
|
"$and": [{"name": ["Mark"]}, {"name": {"$in": "Tom"}}]
|
||||||
transform.query(friend__age__gte=30), {"friend.age": {"$gte": 30}}
|
}
|
||||||
)
|
assert transform.query(name__in=["Tom"], __raw__={"name": "Mark"}) == {
|
||||||
self.assertEqual(
|
"$and": [{"name": {"$in": ["Tom"]}}, {"name": "Mark"}]
|
||||||
transform.query(name__exists=True), {"name": {"$exists": True}}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
def test_transform_update(self):
|
def test_transform_update(self):
|
||||||
class LisDoc(Document):
|
class LisDoc(Document):
|
||||||
@@ -56,17 +54,17 @@ class TransformTest(unittest.TestCase):
|
|||||||
("push", "$push"),
|
("push", "$push"),
|
||||||
):
|
):
|
||||||
update = transform.update(DicDoc, **{"%s__dictField__test" % k: doc})
|
update = transform.update(DicDoc, **{"%s__dictField__test" % k: doc})
|
||||||
self.assertIsInstance(update[v]["dictField.test"], dict)
|
assert isinstance(update[v]["dictField.test"], dict)
|
||||||
|
|
||||||
# Update special cases
|
# Update special cases
|
||||||
update = transform.update(DicDoc, unset__dictField__test=doc)
|
update = transform.update(DicDoc, unset__dictField__test=doc)
|
||||||
self.assertEqual(update["$unset"]["dictField.test"], 1)
|
assert update["$unset"]["dictField.test"] == 1
|
||||||
|
|
||||||
update = transform.update(DicDoc, pull__dictField__test=doc)
|
update = transform.update(DicDoc, pull__dictField__test=doc)
|
||||||
self.assertIsInstance(update["$pull"]["dictField"]["test"], dict)
|
assert isinstance(update["$pull"]["dictField"]["test"], dict)
|
||||||
|
|
||||||
update = transform.update(LisDoc, pull__foo__in=["a"])
|
update = transform.update(LisDoc, pull__foo__in=["a"])
|
||||||
self.assertEqual(update, {"$pull": {"foo": {"$in": ["a"]}}})
|
assert update == {"$pull": {"foo": {"$in": ["a"]}}}
|
||||||
|
|
||||||
def test_transform_update_push(self):
|
def test_transform_update_push(self):
|
||||||
"""Ensure the differences in behvaior between 'push' and 'push_all'"""
|
"""Ensure the differences in behvaior between 'push' and 'push_all'"""
|
||||||
@@ -75,10 +73,10 @@ class TransformTest(unittest.TestCase):
|
|||||||
tags = ListField(StringField())
|
tags = ListField(StringField())
|
||||||
|
|
||||||
update = transform.update(BlogPost, push__tags=["mongo", "db"])
|
update = transform.update(BlogPost, push__tags=["mongo", "db"])
|
||||||
self.assertEqual(update, {"$push": {"tags": ["mongo", "db"]}})
|
assert update == {"$push": {"tags": ["mongo", "db"]}}
|
||||||
|
|
||||||
update = transform.update(BlogPost, push_all__tags=["mongo", "db"])
|
update = transform.update(BlogPost, push_all__tags=["mongo", "db"])
|
||||||
self.assertEqual(update, {"$push": {"tags": {"$each": ["mongo", "db"]}}})
|
assert update == {"$push": {"tags": {"$each": ["mongo", "db"]}}}
|
||||||
|
|
||||||
def test_transform_update_no_operator_default_to_set(self):
|
def test_transform_update_no_operator_default_to_set(self):
|
||||||
"""Ensure the differences in behvaior between 'push' and 'push_all'"""
|
"""Ensure the differences in behvaior between 'push' and 'push_all'"""
|
||||||
@@ -87,7 +85,7 @@ class TransformTest(unittest.TestCase):
|
|||||||
tags = ListField(StringField())
|
tags = ListField(StringField())
|
||||||
|
|
||||||
update = transform.update(BlogPost, tags=["mongo", "db"])
|
update = transform.update(BlogPost, tags=["mongo", "db"])
|
||||||
self.assertEqual(update, {"$set": {"tags": ["mongo", "db"]}})
|
assert update == {"$set": {"tags": ["mongo", "db"]}}
|
||||||
|
|
||||||
def test_query_field_name(self):
|
def test_query_field_name(self):
|
||||||
"""Ensure that the correct field name is used when querying.
|
"""Ensure that the correct field name is used when querying.
|
||||||
@@ -108,18 +106,18 @@ class TransformTest(unittest.TestCase):
|
|||||||
post = BlogPost(**data)
|
post = BlogPost(**data)
|
||||||
post.save()
|
post.save()
|
||||||
|
|
||||||
self.assertIn("postTitle", BlogPost.objects(title=data["title"])._query)
|
assert "postTitle" in BlogPost.objects(title=data["title"])._query
|
||||||
self.assertFalse("title" in BlogPost.objects(title=data["title"])._query)
|
assert not ("title" in BlogPost.objects(title=data["title"])._query)
|
||||||
self.assertEqual(BlogPost.objects(title=data["title"]).count(), 1)
|
assert BlogPost.objects(title=data["title"]).count() == 1
|
||||||
|
|
||||||
self.assertIn("_id", BlogPost.objects(pk=post.id)._query)
|
assert "_id" in BlogPost.objects(pk=post.id)._query
|
||||||
self.assertEqual(BlogPost.objects(pk=post.id).count(), 1)
|
assert BlogPost.objects(pk=post.id).count() == 1
|
||||||
|
|
||||||
self.assertIn(
|
assert (
|
||||||
"postComments.commentContent",
|
"postComments.commentContent"
|
||||||
BlogPost.objects(comments__content="test")._query,
|
in BlogPost.objects(comments__content="test")._query
|
||||||
)
|
)
|
||||||
self.assertEqual(BlogPost.objects(comments__content="test").count(), 1)
|
assert BlogPost.objects(comments__content="test").count() == 1
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
@@ -137,9 +135,9 @@ class TransformTest(unittest.TestCase):
|
|||||||
post = BlogPost(**data)
|
post = BlogPost(**data)
|
||||||
post.save()
|
post.save()
|
||||||
|
|
||||||
self.assertIn("_id", BlogPost.objects(pk=data["title"])._query)
|
assert "_id" in BlogPost.objects(pk=data["title"])._query
|
||||||
self.assertIn("_id", BlogPost.objects(title=data["title"])._query)
|
assert "_id" in BlogPost.objects(title=data["title"])._query
|
||||||
self.assertEqual(BlogPost.objects(pk=data["title"]).count(), 1)
|
assert BlogPost.objects(pk=data["title"]).count() == 1
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
@@ -165,7 +163,7 @@ class TransformTest(unittest.TestCase):
|
|||||||
q2 = B.objects.filter(a__in=[a1, a2])
|
q2 = B.objects.filter(a__in=[a1, a2])
|
||||||
q2 = q2.filter(a=a1)._query
|
q2 = q2.filter(a=a1)._query
|
||||||
|
|
||||||
self.assertEqual(q1, q2)
|
assert q1 == q2
|
||||||
|
|
||||||
def test_raw_query_and_Q_objects(self):
|
def test_raw_query_and_Q_objects(self):
|
||||||
"""
|
"""
|
||||||
@@ -181,11 +179,11 @@ class TransformTest(unittest.TestCase):
|
|||||||
meta = {"allow_inheritance": False}
|
meta = {"allow_inheritance": False}
|
||||||
|
|
||||||
query = Foo.objects(__raw__={"$nor": [{"name": "bar"}]})._query
|
query = Foo.objects(__raw__={"$nor": [{"name": "bar"}]})._query
|
||||||
self.assertEqual(query, {"$nor": [{"name": "bar"}]})
|
assert query == {"$nor": [{"name": "bar"}]}
|
||||||
|
|
||||||
q1 = {"$or": [{"a": 1}, {"b": 1}]}
|
q1 = {"$or": [{"a": 1}, {"b": 1}]}
|
||||||
query = Foo.objects(Q(__raw__=q1) & Q(c=1))._query
|
query = Foo.objects(Q(__raw__=q1) & Q(c=1))._query
|
||||||
self.assertEqual(query, {"$or": [{"a": 1}, {"b": 1}], "c": 1})
|
assert query == {"$or": [{"a": 1}, {"b": 1}], "c": 1}
|
||||||
|
|
||||||
def test_raw_and_merging(self):
|
def test_raw_and_merging(self):
|
||||||
class Doc(Document):
|
class Doc(Document):
|
||||||
@@ -202,51 +200,39 @@ class TransformTest(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
)._query
|
)._query
|
||||||
|
|
||||||
self.assertEqual(
|
assert raw_query == {
|
||||||
raw_query,
|
"deleted": False,
|
||||||
{
|
"scraped": "yes",
|
||||||
"deleted": False,
|
"$nor": [{"views.extracted": "no"}, {"attachments.views.extracted": "no"}],
|
||||||
"scraped": "yes",
|
}
|
||||||
"$nor": [
|
|
||||||
{"views.extracted": "no"},
|
|
||||||
{"attachments.views.extracted": "no"},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_geojson_PointField(self):
|
def test_geojson_PointField(self):
|
||||||
class Location(Document):
|
class Location(Document):
|
||||||
loc = PointField()
|
loc = PointField()
|
||||||
|
|
||||||
update = transform.update(Location, set__loc=[1, 2])
|
update = transform.update(Location, set__loc=[1, 2])
|
||||||
self.assertEqual(
|
assert update == {"$set": {"loc": {"type": "Point", "coordinates": [1, 2]}}}
|
||||||
update, {"$set": {"loc": {"type": "Point", "coordinates": [1, 2]}}}
|
|
||||||
)
|
|
||||||
|
|
||||||
update = transform.update(
|
update = transform.update(
|
||||||
Location, set__loc={"type": "Point", "coordinates": [1, 2]}
|
Location, set__loc={"type": "Point", "coordinates": [1, 2]}
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert update == {"$set": {"loc": {"type": "Point", "coordinates": [1, 2]}}}
|
||||||
update, {"$set": {"loc": {"type": "Point", "coordinates": [1, 2]}}}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_geojson_LineStringField(self):
|
def test_geojson_LineStringField(self):
|
||||||
class Location(Document):
|
class Location(Document):
|
||||||
line = LineStringField()
|
line = LineStringField()
|
||||||
|
|
||||||
update = transform.update(Location, set__line=[[1, 2], [2, 2]])
|
update = transform.update(Location, set__line=[[1, 2], [2, 2]])
|
||||||
self.assertEqual(
|
assert update == {
|
||||||
update,
|
"$set": {"line": {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}}
|
||||||
{"$set": {"line": {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}}},
|
}
|
||||||
)
|
|
||||||
|
|
||||||
update = transform.update(
|
update = transform.update(
|
||||||
Location, set__line={"type": "LineString", "coordinates": [[1, 2], [2, 2]]}
|
Location, set__line={"type": "LineString", "coordinates": [[1, 2], [2, 2]]}
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert update == {
|
||||||
update,
|
"$set": {"line": {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}}
|
||||||
{"$set": {"line": {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}}},
|
}
|
||||||
)
|
|
||||||
|
|
||||||
def test_geojson_PolygonField(self):
|
def test_geojson_PolygonField(self):
|
||||||
class Location(Document):
|
class Location(Document):
|
||||||
@@ -255,17 +241,14 @@ class TransformTest(unittest.TestCase):
|
|||||||
update = transform.update(
|
update = transform.update(
|
||||||
Location, set__poly=[[[40, 5], [40, 6], [41, 6], [40, 5]]]
|
Location, set__poly=[[[40, 5], [40, 6], [41, 6], [40, 5]]]
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert update == {
|
||||||
update,
|
"$set": {
|
||||||
{
|
"poly": {
|
||||||
"$set": {
|
"type": "Polygon",
|
||||||
"poly": {
|
"coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
|
||||||
"type": "Polygon",
|
|
||||||
"coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
update = transform.update(
|
update = transform.update(
|
||||||
Location,
|
Location,
|
||||||
@@ -274,17 +257,14 @@ class TransformTest(unittest.TestCase):
|
|||||||
"coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
|
"coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert update == {
|
||||||
update,
|
"$set": {
|
||||||
{
|
"poly": {
|
||||||
"$set": {
|
"type": "Polygon",
|
||||||
"poly": {
|
"coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
|
||||||
"type": "Polygon",
|
|
||||||
"coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
def test_type(self):
|
def test_type(self):
|
||||||
class Doc(Document):
|
class Doc(Document):
|
||||||
@@ -293,10 +273,10 @@ class TransformTest(unittest.TestCase):
|
|||||||
Doc(df=True).save()
|
Doc(df=True).save()
|
||||||
Doc(df=7).save()
|
Doc(df=7).save()
|
||||||
Doc(df="df").save()
|
Doc(df="df").save()
|
||||||
self.assertEqual(Doc.objects(df__type=1).count(), 0) # double
|
assert Doc.objects(df__type=1).count() == 0 # double
|
||||||
self.assertEqual(Doc.objects(df__type=8).count(), 1) # bool
|
assert Doc.objects(df__type=8).count() == 1 # bool
|
||||||
self.assertEqual(Doc.objects(df__type=2).count(), 1) # str
|
assert Doc.objects(df__type=2).count() == 1 # str
|
||||||
self.assertEqual(Doc.objects(df__type=16).count(), 1) # int
|
assert Doc.objects(df__type=16).count() == 1 # int
|
||||||
|
|
||||||
def test_last_field_name_like_operator(self):
|
def test_last_field_name_like_operator(self):
|
||||||
class EmbeddedItem(EmbeddedDocument):
|
class EmbeddedItem(EmbeddedDocument):
|
||||||
@@ -311,12 +291,12 @@ class TransformTest(unittest.TestCase):
|
|||||||
doc = Doc(item=EmbeddedItem(type="axe", name="Heroic axe"))
|
doc = Doc(item=EmbeddedItem(type="axe", name="Heroic axe"))
|
||||||
doc.save()
|
doc.save()
|
||||||
|
|
||||||
self.assertEqual(1, Doc.objects(item__type__="axe").count())
|
assert 1 == Doc.objects(item__type__="axe").count()
|
||||||
self.assertEqual(1, Doc.objects(item__name__="Heroic axe").count())
|
assert 1 == Doc.objects(item__name__="Heroic axe").count()
|
||||||
|
|
||||||
Doc.objects(id=doc.id).update(set__item__type__="sword")
|
Doc.objects(id=doc.id).update(set__item__type__="sword")
|
||||||
self.assertEqual(1, Doc.objects(item__type__="sword").count())
|
assert 1 == Doc.objects(item__type__="sword").count()
|
||||||
self.assertEqual(0, Doc.objects(item__type__="axe").count())
|
assert 0 == Doc.objects(item__type__="axe").count()
|
||||||
|
|
||||||
def test_understandable_error_raised(self):
|
def test_understandable_error_raised(self):
|
||||||
class Event(Document):
|
class Event(Document):
|
||||||
@@ -326,7 +306,7 @@ class TransformTest(unittest.TestCase):
|
|||||||
box = [(35.0, -125.0), (40.0, -100.0)]
|
box = [(35.0, -125.0), (40.0, -100.0)]
|
||||||
# I *meant* to execute location__within_box=box
|
# I *meant* to execute location__within_box=box
|
||||||
events = Event.objects(location__within=box)
|
events = Event.objects(location__within=box)
|
||||||
with self.assertRaises(InvalidQueryError):
|
with pytest.raises(InvalidQueryError):
|
||||||
events.count()
|
events.count()
|
||||||
|
|
||||||
def test_update_pull_for_list_fields(self):
|
def test_update_pull_for_list_fields(self):
|
||||||
@@ -349,24 +329,20 @@ class TransformTest(unittest.TestCase):
|
|||||||
|
|
||||||
word = Word(word="abc", index=1)
|
word = Word(word="abc", index=1)
|
||||||
update = transform.update(MainDoc, pull__content__text=word)
|
update = transform.update(MainDoc, pull__content__text=word)
|
||||||
self.assertEqual(
|
assert update == {
|
||||||
update, {"$pull": {"content.text": SON([("word", u"abc"), ("index", 1)])}}
|
"$pull": {"content.text": SON([("word", u"abc"), ("index", 1)])}
|
||||||
)
|
}
|
||||||
|
|
||||||
update = transform.update(MainDoc, pull__content__heading="xyz")
|
update = transform.update(MainDoc, pull__content__heading="xyz")
|
||||||
self.assertEqual(update, {"$pull": {"content.heading": "xyz"}})
|
assert update == {"$pull": {"content.heading": "xyz"}}
|
||||||
|
|
||||||
update = transform.update(MainDoc, pull__content__text__word__in=["foo", "bar"])
|
update = transform.update(MainDoc, pull__content__text__word__in=["foo", "bar"])
|
||||||
self.assertEqual(
|
assert update == {"$pull": {"content.text": {"word": {"$in": ["foo", "bar"]}}}}
|
||||||
update, {"$pull": {"content.text": {"word": {"$in": ["foo", "bar"]}}}}
|
|
||||||
)
|
|
||||||
|
|
||||||
update = transform.update(
|
update = transform.update(
|
||||||
MainDoc, pull__content__text__word__nin=["foo", "bar"]
|
MainDoc, pull__content__text__word__nin=["foo", "bar"]
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert update == {"$pull": {"content.text": {"word": {"$nin": ["foo", "bar"]}}}}
|
||||||
update, {"$pull": {"content.text": {"word": {"$nin": ["foo", "bar"]}}}}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -3,15 +3,14 @@ import re
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.errors import InvalidQueryError
|
from mongoengine.errors import InvalidQueryError
|
||||||
from mongoengine.queryset import Q
|
from mongoengine.queryset import Q
|
||||||
|
|
||||||
__all__ = ("QTest",)
|
|
||||||
|
|
||||||
|
class TestQ(unittest.TestCase):
|
||||||
class QTest(unittest.TestCase):
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
connect(db="mongoenginetest")
|
connect(db="mongoenginetest")
|
||||||
|
|
||||||
@@ -37,10 +36,10 @@ class QTest(unittest.TestCase):
|
|||||||
age = IntField()
|
age = IntField()
|
||||||
|
|
||||||
query = {"$or": [{"age": {"$gte": 18}}, {"name": "test"}]}
|
query = {"$or": [{"age": {"$gte": 18}}, {"name": "test"}]}
|
||||||
self.assertEqual((q1 | q2 | q3 | q4 | q5).to_query(Person), query)
|
assert (q1 | q2 | q3 | q4 | q5).to_query(Person) == query
|
||||||
|
|
||||||
query = {"age": {"$gte": 18}, "name": "test"}
|
query = {"age": {"$gte": 18}, "name": "test"}
|
||||||
self.assertEqual((q1 & q2 & q3 & q4 & q5).to_query(Person), query)
|
assert (q1 & q2 & q3 & q4 & q5).to_query(Person) == query
|
||||||
|
|
||||||
def test_q_with_dbref(self):
|
def test_q_with_dbref(self):
|
||||||
"""Ensure Q objects handle DBRefs correctly"""
|
"""Ensure Q objects handle DBRefs correctly"""
|
||||||
@@ -55,8 +54,8 @@ class QTest(unittest.TestCase):
|
|||||||
user = User.objects.create()
|
user = User.objects.create()
|
||||||
Post.objects.create(created_user=user)
|
Post.objects.create(created_user=user)
|
||||||
|
|
||||||
self.assertEqual(Post.objects.filter(created_user=user).count(), 1)
|
assert Post.objects.filter(created_user=user).count() == 1
|
||||||
self.assertEqual(Post.objects.filter(Q(created_user=user)).count(), 1)
|
assert Post.objects.filter(Q(created_user=user)).count() == 1
|
||||||
|
|
||||||
def test_and_combination(self):
|
def test_and_combination(self):
|
||||||
"""Ensure that Q-objects correctly AND together.
|
"""Ensure that Q-objects correctly AND together.
|
||||||
@@ -67,12 +66,10 @@ class QTest(unittest.TestCase):
|
|||||||
y = StringField()
|
y = StringField()
|
||||||
|
|
||||||
query = (Q(x__lt=7) & Q(x__lt=3)).to_query(TestDoc)
|
query = (Q(x__lt=7) & Q(x__lt=3)).to_query(TestDoc)
|
||||||
self.assertEqual(query, {"$and": [{"x": {"$lt": 7}}, {"x": {"$lt": 3}}]})
|
assert query == {"$and": [{"x": {"$lt": 7}}, {"x": {"$lt": 3}}]}
|
||||||
|
|
||||||
query = (Q(y="a") & Q(x__lt=7) & Q(x__lt=3)).to_query(TestDoc)
|
query = (Q(y="a") & Q(x__lt=7) & Q(x__lt=3)).to_query(TestDoc)
|
||||||
self.assertEqual(
|
assert query == {"$and": [{"y": "a"}, {"x": {"$lt": 7}}, {"x": {"$lt": 3}}]}
|
||||||
query, {"$and": [{"y": "a"}, {"x": {"$lt": 7}}, {"x": {"$lt": 3}}]}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check normal cases work without an error
|
# Check normal cases work without an error
|
||||||
query = Q(x__lt=7) & Q(x__gt=3)
|
query = Q(x__lt=7) & Q(x__gt=3)
|
||||||
@@ -80,7 +77,7 @@ class QTest(unittest.TestCase):
|
|||||||
q1 = Q(x__lt=7)
|
q1 = Q(x__lt=7)
|
||||||
q2 = Q(x__gt=3)
|
q2 = Q(x__gt=3)
|
||||||
query = (q1 & q2).to_query(TestDoc)
|
query = (q1 & q2).to_query(TestDoc)
|
||||||
self.assertEqual(query, {"x": {"$lt": 7, "$gt": 3}})
|
assert query == {"x": {"$lt": 7, "$gt": 3}}
|
||||||
|
|
||||||
# More complex nested example
|
# More complex nested example
|
||||||
query = Q(x__lt=100) & Q(y__ne="NotMyString")
|
query = Q(x__lt=100) & Q(y__ne="NotMyString")
|
||||||
@@ -89,7 +86,7 @@ class QTest(unittest.TestCase):
|
|||||||
"x": {"$lt": 100, "$gt": -100},
|
"x": {"$lt": 100, "$gt": -100},
|
||||||
"y": {"$ne": "NotMyString", "$in": ["a", "b", "c"]},
|
"y": {"$ne": "NotMyString", "$in": ["a", "b", "c"]},
|
||||||
}
|
}
|
||||||
self.assertEqual(query.to_query(TestDoc), mongo_query)
|
assert query.to_query(TestDoc) == mongo_query
|
||||||
|
|
||||||
def test_or_combination(self):
|
def test_or_combination(self):
|
||||||
"""Ensure that Q-objects correctly OR together.
|
"""Ensure that Q-objects correctly OR together.
|
||||||
@@ -101,7 +98,7 @@ class QTest(unittest.TestCase):
|
|||||||
q1 = Q(x__lt=3)
|
q1 = Q(x__lt=3)
|
||||||
q2 = Q(x__gt=7)
|
q2 = Q(x__gt=7)
|
||||||
query = (q1 | q2).to_query(TestDoc)
|
query = (q1 | q2).to_query(TestDoc)
|
||||||
self.assertEqual(query, {"$or": [{"x": {"$lt": 3}}, {"x": {"$gt": 7}}]})
|
assert query == {"$or": [{"x": {"$lt": 3}}, {"x": {"$gt": 7}}]}
|
||||||
|
|
||||||
def test_and_or_combination(self):
|
def test_and_or_combination(self):
|
||||||
"""Ensure that Q-objects handle ANDing ORed components.
|
"""Ensure that Q-objects handle ANDing ORed components.
|
||||||
@@ -115,15 +112,12 @@ class QTest(unittest.TestCase):
|
|||||||
|
|
||||||
query = Q(x__gt=0) | Q(x__exists=False)
|
query = Q(x__gt=0) | Q(x__exists=False)
|
||||||
query &= Q(x__lt=100)
|
query &= Q(x__lt=100)
|
||||||
self.assertEqual(
|
assert query.to_query(TestDoc) == {
|
||||||
query.to_query(TestDoc),
|
"$and": [
|
||||||
{
|
{"$or": [{"x": {"$gt": 0}}, {"x": {"$exists": False}}]},
|
||||||
"$and": [
|
{"x": {"$lt": 100}},
|
||||||
{"$or": [{"x": {"$gt": 0}}, {"x": {"$exists": False}}]},
|
]
|
||||||
{"x": {"$lt": 100}},
|
}
|
||||||
]
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
q1 = Q(x__gt=0) | Q(x__exists=False)
|
q1 = Q(x__gt=0) | Q(x__exists=False)
|
||||||
q2 = Q(x__lt=100) | Q(y=True)
|
q2 = Q(x__lt=100) | Q(y=True)
|
||||||
@@ -133,16 +127,13 @@ class QTest(unittest.TestCase):
|
|||||||
TestDoc(x=10).save()
|
TestDoc(x=10).save()
|
||||||
TestDoc(y=True).save()
|
TestDoc(y=True).save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert query == {
|
||||||
query,
|
"$and": [
|
||||||
{
|
{"$or": [{"x": {"$gt": 0}}, {"x": {"$exists": False}}]},
|
||||||
"$and": [
|
{"$or": [{"x": {"$lt": 100}}, {"y": True}]},
|
||||||
{"$or": [{"x": {"$gt": 0}}, {"x": {"$exists": False}}]},
|
]
|
||||||
{"$or": [{"x": {"$lt": 100}}, {"y": True}]},
|
}
|
||||||
]
|
assert 2 == TestDoc.objects(q1 & q2).count()
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertEqual(2, TestDoc.objects(q1 & q2).count())
|
|
||||||
|
|
||||||
def test_or_and_or_combination(self):
|
def test_or_and_or_combination(self):
|
||||||
"""Ensure that Q-objects handle ORing ANDed ORed components. :)
|
"""Ensure that Q-objects handle ORing ANDed ORed components. :)
|
||||||
@@ -162,26 +153,23 @@ class QTest(unittest.TestCase):
|
|||||||
q2 = Q(x__lt=100) & (Q(y=False) | Q(y__exists=False))
|
q2 = Q(x__lt=100) & (Q(y=False) | Q(y__exists=False))
|
||||||
query = (q1 | q2).to_query(TestDoc)
|
query = (q1 | q2).to_query(TestDoc)
|
||||||
|
|
||||||
self.assertEqual(
|
assert query == {
|
||||||
query,
|
"$or": [
|
||||||
{
|
{
|
||||||
"$or": [
|
"$and": [
|
||||||
{
|
{"x": {"$gt": 0}},
|
||||||
"$and": [
|
{"$or": [{"y": True}, {"y": {"$exists": False}}]},
|
||||||
{"x": {"$gt": 0}},
|
]
|
||||||
{"$or": [{"y": True}, {"y": {"$exists": False}}]},
|
},
|
||||||
]
|
{
|
||||||
},
|
"$and": [
|
||||||
{
|
{"x": {"$lt": 100}},
|
||||||
"$and": [
|
{"$or": [{"y": False}, {"y": {"$exists": False}}]},
|
||||||
{"x": {"$lt": 100}},
|
]
|
||||||
{"$or": [{"y": False}, {"y": {"$exists": False}}]},
|
},
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
]
|
assert 2 == TestDoc.objects(q1 | q2).count()
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertEqual(2, TestDoc.objects(q1 | q2).count())
|
|
||||||
|
|
||||||
def test_multiple_occurence_in_field(self):
|
def test_multiple_occurence_in_field(self):
|
||||||
class Test(Document):
|
class Test(Document):
|
||||||
@@ -194,8 +182,8 @@ class QTest(unittest.TestCase):
|
|||||||
q3 = q1 & q2
|
q3 = q1 & q2
|
||||||
|
|
||||||
query = q3.to_query(Test)
|
query = q3.to_query(Test)
|
||||||
self.assertEqual(query["$and"][0], q1.to_query(Test))
|
assert query["$and"][0] == q1.to_query(Test)
|
||||||
self.assertEqual(query["$and"][1], q2.to_query(Test))
|
assert query["$and"][1] == q2.to_query(Test)
|
||||||
|
|
||||||
def test_q_clone(self):
|
def test_q_clone(self):
|
||||||
class TestDoc(Document):
|
class TestDoc(Document):
|
||||||
@@ -209,15 +197,15 @@ class QTest(unittest.TestCase):
|
|||||||
# Check normal cases work without an error
|
# Check normal cases work without an error
|
||||||
test = TestDoc.objects(Q(x__lt=7) & Q(x__gt=3))
|
test = TestDoc.objects(Q(x__lt=7) & Q(x__gt=3))
|
||||||
|
|
||||||
self.assertEqual(test.count(), 3)
|
assert test.count() == 3
|
||||||
|
|
||||||
test2 = test.clone()
|
test2 = test.clone()
|
||||||
self.assertEqual(test2.count(), 3)
|
assert test2.count() == 3
|
||||||
self.assertNotEqual(test2, test)
|
assert test2 != test
|
||||||
|
|
||||||
test3 = test2.filter(x=6)
|
test3 = test2.filter(x=6)
|
||||||
self.assertEqual(test3.count(), 1)
|
assert test3.count() == 1
|
||||||
self.assertEqual(test.count(), 3)
|
assert test.count() == 3
|
||||||
|
|
||||||
def test_q(self):
|
def test_q(self):
|
||||||
"""Ensure that Q objects may be used to query for documents.
|
"""Ensure that Q objects may be used to query for documents.
|
||||||
@@ -254,19 +242,19 @@ class QTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Check ObjectId lookup works
|
# Check ObjectId lookup works
|
||||||
obj = BlogPost.objects(id=post1.id).first()
|
obj = BlogPost.objects(id=post1.id).first()
|
||||||
self.assertEqual(obj, post1)
|
assert obj == post1
|
||||||
|
|
||||||
# Check Q object combination with one does not exist
|
# Check Q object combination with one does not exist
|
||||||
q = BlogPost.objects(Q(title="Test 5") | Q(published=True))
|
q = BlogPost.objects(Q(title="Test 5") | Q(published=True))
|
||||||
posts = [post.id for post in q]
|
posts = [post.id for post in q]
|
||||||
|
|
||||||
published_posts = (post2, post3)
|
published_posts = (post2, post3)
|
||||||
self.assertTrue(all(obj.id in posts for obj in published_posts))
|
assert all(obj.id in posts for obj in published_posts)
|
||||||
|
|
||||||
q = BlogPost.objects(Q(title="Test 1") | Q(published=True))
|
q = BlogPost.objects(Q(title="Test 1") | Q(published=True))
|
||||||
posts = [post.id for post in q]
|
posts = [post.id for post in q]
|
||||||
published_posts = (post1, post2, post3, post5, post6)
|
published_posts = (post1, post2, post3, post5, post6)
|
||||||
self.assertTrue(all(obj.id in posts for obj in published_posts))
|
assert all(obj.id in posts for obj in published_posts)
|
||||||
|
|
||||||
# Check Q object combination
|
# Check Q object combination
|
||||||
date = datetime.datetime(2010, 1, 10)
|
date = datetime.datetime(2010, 1, 10)
|
||||||
@@ -274,9 +262,9 @@ class QTest(unittest.TestCase):
|
|||||||
posts = [post.id for post in q]
|
posts = [post.id for post in q]
|
||||||
|
|
||||||
published_posts = (post1, post2, post3, post4)
|
published_posts = (post1, post2, post3, post4)
|
||||||
self.assertTrue(all(obj.id in posts for obj in published_posts))
|
assert all(obj.id in posts for obj in published_posts)
|
||||||
|
|
||||||
self.assertFalse(any(obj.id in posts for obj in [post5, post6]))
|
assert not any(obj.id in posts for obj in [post5, post6])
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
@@ -286,15 +274,15 @@ class QTest(unittest.TestCase):
|
|||||||
self.Person(name="user3", age=30).save()
|
self.Person(name="user3", age=30).save()
|
||||||
self.Person(name="user4", age=40).save()
|
self.Person(name="user4", age=40).save()
|
||||||
|
|
||||||
self.assertEqual(self.Person.objects(Q(age__in=[20])).count(), 2)
|
assert self.Person.objects(Q(age__in=[20])).count() == 2
|
||||||
self.assertEqual(self.Person.objects(Q(age__in=[20, 30])).count(), 3)
|
assert self.Person.objects(Q(age__in=[20, 30])).count() == 3
|
||||||
|
|
||||||
# Test invalid query objs
|
# Test invalid query objs
|
||||||
with self.assertRaises(InvalidQueryError):
|
with pytest.raises(InvalidQueryError):
|
||||||
self.Person.objects("user1")
|
self.Person.objects("user1")
|
||||||
|
|
||||||
# filter should fail, too
|
# filter should fail, too
|
||||||
with self.assertRaises(InvalidQueryError):
|
with pytest.raises(InvalidQueryError):
|
||||||
self.Person.objects.filter("user1")
|
self.Person.objects.filter("user1")
|
||||||
|
|
||||||
def test_q_regex(self):
|
def test_q_regex(self):
|
||||||
@@ -304,31 +292,31 @@ class QTest(unittest.TestCase):
|
|||||||
person.save()
|
person.save()
|
||||||
|
|
||||||
obj = self.Person.objects(Q(name=re.compile("^Gui"))).first()
|
obj = self.Person.objects(Q(name=re.compile("^Gui"))).first()
|
||||||
self.assertEqual(obj, person)
|
assert obj == person
|
||||||
obj = self.Person.objects(Q(name=re.compile("^gui"))).first()
|
obj = self.Person.objects(Q(name=re.compile("^gui"))).first()
|
||||||
self.assertEqual(obj, None)
|
assert obj is None
|
||||||
|
|
||||||
obj = self.Person.objects(Q(name=re.compile("^gui", re.I))).first()
|
obj = self.Person.objects(Q(name=re.compile("^gui", re.I))).first()
|
||||||
self.assertEqual(obj, person)
|
assert obj == person
|
||||||
|
|
||||||
obj = self.Person.objects(Q(name__not=re.compile("^bob"))).first()
|
obj = self.Person.objects(Q(name__not=re.compile("^bob"))).first()
|
||||||
self.assertEqual(obj, person)
|
assert obj == person
|
||||||
|
|
||||||
obj = self.Person.objects(Q(name__not=re.compile("^Gui"))).first()
|
obj = self.Person.objects(Q(name__not=re.compile("^Gui"))).first()
|
||||||
self.assertEqual(obj, None)
|
assert obj is None
|
||||||
|
|
||||||
def test_q_repr(self):
|
def test_q_repr(self):
|
||||||
self.assertEqual(repr(Q()), "Q(**{})")
|
assert repr(Q()) == "Q(**{})"
|
||||||
self.assertEqual(repr(Q(name="test")), "Q(**{'name': 'test'})")
|
assert repr(Q(name="test")) == "Q(**{'name': 'test'})"
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
repr(Q(name="test") & Q(age__gte=18)),
|
repr(Q(name="test") & Q(age__gte=18))
|
||||||
"(Q(**{'name': 'test'}) & Q(**{'age__gte': 18}))",
|
== "(Q(**{'name': 'test'}) & Q(**{'age__gte': 18}))"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
repr(Q(name="test") | Q(age__gte=18)),
|
repr(Q(name="test") | Q(age__gte=18))
|
||||||
"(Q(**{'name': 'test'}) | Q(**{'age__gte': 18}))",
|
== "(Q(**{'name': 'test'}) | Q(**{'age__gte': 18}))"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_q_lists(self):
|
def test_q_lists(self):
|
||||||
@@ -343,8 +331,8 @@ class QTest(unittest.TestCase):
|
|||||||
BlogPost(tags=["python", "mongo"]).save()
|
BlogPost(tags=["python", "mongo"]).save()
|
||||||
BlogPost(tags=["python"]).save()
|
BlogPost(tags=["python"]).save()
|
||||||
|
|
||||||
self.assertEqual(BlogPost.objects(Q(tags="mongo")).count(), 1)
|
assert BlogPost.objects(Q(tags="mongo")).count() == 1
|
||||||
self.assertEqual(BlogPost.objects(Q(tags="python")).count(), 2)
|
assert BlogPost.objects(Q(tags="python")).count() == 2
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
@@ -357,12 +345,12 @@ class QTest(unittest.TestCase):
|
|||||||
pk = ObjectId()
|
pk = ObjectId()
|
||||||
User(email="example@example.com", pk=pk).save()
|
User(email="example@example.com", pk=pk).save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
1,
|
1
|
||||||
User.objects.filter(Q(email="example@example.com") | Q(name="John Doe"))
|
== User.objects.filter(Q(email="example@example.com") | Q(name="John Doe"))
|
||||||
.limit(2)
|
.limit(2)
|
||||||
.filter(pk=pk)
|
.filter(pk=pk)
|
||||||
.count(),
|
.count()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_chained_q_or_filtering(self):
|
def test_chained_q_or_filtering(self):
|
||||||
@@ -378,16 +366,58 @@ class QTest(unittest.TestCase):
|
|||||||
Item(postables=[Post(name="a"), Post(name="c")]).save()
|
Item(postables=[Post(name="a"), Post(name="c")]).save()
|
||||||
Item(postables=[Post(name="a"), Post(name="b"), Post(name="c")]).save()
|
Item(postables=[Post(name="a"), Post(name="b"), Post(name="c")]).save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
Item.objects(Q(postables__name="a") & Q(postables__name="b")).count(), 2
|
Item.objects(Q(postables__name="a") & Q(postables__name="b")).count() == 2
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
assert (
|
||||||
Item.objects.filter(postables__name="a")
|
Item.objects.filter(postables__name="a").filter(postables__name="b").count()
|
||||||
.filter(postables__name="b")
|
== 2
|
||||||
.count(),
|
|
||||||
2,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_equality(self):
|
||||||
|
assert Q(name="John") == Q(name="John")
|
||||||
|
assert Q() == Q()
|
||||||
|
|
||||||
|
def test_inequality(self):
|
||||||
|
assert Q(name="John") != Q(name="Ralph")
|
||||||
|
|
||||||
|
def test_operation_equality(self):
|
||||||
|
q1 = Q(name="John") | Q(title="Sir") & Q(surname="Paul")
|
||||||
|
q2 = Q(name="John") | Q(title="Sir") & Q(surname="Paul")
|
||||||
|
assert q1 == q2
|
||||||
|
|
||||||
|
def test_operation_inequality(self):
|
||||||
|
q1 = Q(name="John") | Q(title="Sir")
|
||||||
|
q2 = Q(title="Sir") | Q(name="John")
|
||||||
|
assert q1 != q2
|
||||||
|
|
||||||
|
def test_combine_and_empty(self):
|
||||||
|
q = Q(x=1)
|
||||||
|
assert q & Q() == q
|
||||||
|
assert Q() & q == q
|
||||||
|
|
||||||
|
def test_combine_and_both_empty(self):
|
||||||
|
assert Q() & Q() == Q()
|
||||||
|
|
||||||
|
def test_combine_or_empty(self):
|
||||||
|
q = Q(x=1)
|
||||||
|
assert q | Q() == q
|
||||||
|
assert Q() | q == q
|
||||||
|
|
||||||
|
def test_combine_or_both_empty(self):
|
||||||
|
assert Q() | Q() == Q()
|
||||||
|
|
||||||
|
def test_q_bool(self):
|
||||||
|
assert Q(name="John")
|
||||||
|
assert not Q()
|
||||||
|
|
||||||
|
def test_combine_bool(self):
|
||||||
|
assert not Q() & Q()
|
||||||
|
assert Q() & Q(name="John")
|
||||||
|
assert Q(name="John") & Q()
|
||||||
|
assert Q() | Q(name="John")
|
||||||
|
assert Q(name="John") | Q()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
9
tests/test_ci.py
Normal file
9
tests/test_ci.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
def test_ci_placeholder():
|
||||||
|
# This empty test is used within the CI to
|
||||||
|
# setup the tox venv without running the test suite
|
||||||
|
# if we simply skip all test with pytest -k=wrong_pattern
|
||||||
|
# pytest command would return with exit_code=5 (i.e "no tests run")
|
||||||
|
# making travis fail
|
||||||
|
# this empty test is the recommended way to handle this
|
||||||
|
# as described in https://github.com/pytest-dev/pytest/issues/2393
|
||||||
|
pass
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from mongoengine.common import _import_class
|
import pytest
|
||||||
|
|
||||||
from mongoengine import Document
|
from mongoengine import Document
|
||||||
|
from mongoengine.common import _import_class
|
||||||
|
|
||||||
|
|
||||||
class TestCommon(unittest.TestCase):
|
class TestCommon:
|
||||||
def test__import_class(self):
|
def test__import_class(self):
|
||||||
doc_cls = _import_class("Document")
|
doc_cls = _import_class("Document")
|
||||||
self.assertIs(doc_cls, Document)
|
assert doc_cls is Document
|
||||||
|
|
||||||
def test__import_class_raise_if_not_known(self):
|
def test__import_class_raise_if_not_known(self):
|
||||||
with self.assertRaises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
_import_class("UnknownClass")
|
_import_class("UnknownClass")
|
||||||
|
|||||||
@@ -1,35 +1,32 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from pymongo import MongoClient
|
from bson.tz_util import utc
|
||||||
from pymongo.errors import OperationFailure, InvalidName
|
import pymongo
|
||||||
from pymongo import ReadPreference
|
|
||||||
|
|
||||||
from mongoengine import Document
|
from pymongo import MongoClient, ReadPreference
|
||||||
|
from pymongo.errors import InvalidName, OperationFailure
|
||||||
|
import pytest
|
||||||
|
|
||||||
try:
|
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
|
|
||||||
from bson.tz_util import utc
|
|
||||||
|
|
||||||
from mongoengine import (
|
|
||||||
connect,
|
|
||||||
register_connection,
|
|
||||||
Document,
|
|
||||||
DateTimeField,
|
|
||||||
disconnect_all,
|
|
||||||
StringField,
|
|
||||||
)
|
|
||||||
import mongoengine.connection
|
import mongoengine.connection
|
||||||
|
from mongoengine import (
|
||||||
|
DateTimeField,
|
||||||
|
Document,
|
||||||
|
StringField,
|
||||||
|
connect,
|
||||||
|
disconnect_all,
|
||||||
|
register_connection,
|
||||||
|
)
|
||||||
from mongoengine.connection import (
|
from mongoengine.connection import (
|
||||||
ConnectionFailure,
|
ConnectionFailure,
|
||||||
get_db,
|
|
||||||
get_connection,
|
|
||||||
disconnect,
|
|
||||||
DEFAULT_DATABASE_NAME,
|
DEFAULT_DATABASE_NAME,
|
||||||
|
disconnect,
|
||||||
|
get_connection,
|
||||||
|
get_db,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -37,6 +34,18 @@ def get_tz_awareness(connection):
|
|||||||
return connection.codec_options.tz_aware
|
return connection.codec_options.tz_aware
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import mongomock
|
||||||
|
|
||||||
|
MONGOMOCK_INSTALLED = True
|
||||||
|
except ImportError:
|
||||||
|
MONGOMOCK_INSTALLED = False
|
||||||
|
|
||||||
|
require_mongomock = pytest.mark.skipif(
|
||||||
|
not MONGOMOCK_INSTALLED, reason="you need mongomock installed to run this testcase"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConnectionTest(unittest.TestCase):
|
class ConnectionTest(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@@ -56,15 +65,15 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
connect("mongoenginetest")
|
connect("mongoenginetest")
|
||||||
|
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
|
assert isinstance(conn, pymongo.mongo_client.MongoClient)
|
||||||
|
|
||||||
db = get_db()
|
db = get_db()
|
||||||
self.assertIsInstance(db, pymongo.database.Database)
|
assert isinstance(db, pymongo.database.Database)
|
||||||
self.assertEqual(db.name, "mongoenginetest")
|
assert db.name == "mongoenginetest"
|
||||||
|
|
||||||
connect("mongoenginetest2", alias="testdb")
|
connect("mongoenginetest2", alias="testdb")
|
||||||
conn = get_connection("testdb")
|
conn = get_connection("testdb")
|
||||||
self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
|
assert isinstance(conn, pymongo.mongo_client.MongoClient)
|
||||||
|
|
||||||
def test_connect_disconnect_works_properly(self):
|
def test_connect_disconnect_works_properly(self):
|
||||||
class History1(Document):
|
class History1(Document):
|
||||||
@@ -84,31 +93,27 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
h = History1(name="default").save()
|
h = History1(name="default").save()
|
||||||
h1 = History2(name="db1").save()
|
h1 = History2(name="db1").save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert list(History1.objects().as_pymongo()) == [
|
||||||
list(History1.objects().as_pymongo()), [{"_id": h.id, "name": "default"}]
|
{"_id": h.id, "name": "default"}
|
||||||
)
|
]
|
||||||
self.assertEqual(
|
assert list(History2.objects().as_pymongo()) == [{"_id": h1.id, "name": "db1"}]
|
||||||
list(History2.objects().as_pymongo()), [{"_id": h1.id, "name": "db1"}]
|
|
||||||
)
|
|
||||||
|
|
||||||
disconnect("db1")
|
disconnect("db1")
|
||||||
disconnect("db2")
|
disconnect("db2")
|
||||||
|
|
||||||
with self.assertRaises(ConnectionFailure):
|
with pytest.raises(ConnectionFailure):
|
||||||
list(History1.objects().as_pymongo())
|
list(History1.objects().as_pymongo())
|
||||||
|
|
||||||
with self.assertRaises(ConnectionFailure):
|
with pytest.raises(ConnectionFailure):
|
||||||
list(History2.objects().as_pymongo())
|
list(History2.objects().as_pymongo())
|
||||||
|
|
||||||
connect("db1", alias="db1")
|
connect("db1", alias="db1")
|
||||||
connect("db2", alias="db2")
|
connect("db2", alias="db2")
|
||||||
|
|
||||||
self.assertEqual(
|
assert list(History1.objects().as_pymongo()) == [
|
||||||
list(History1.objects().as_pymongo()), [{"_id": h.id, "name": "default"}]
|
{"_id": h.id, "name": "default"}
|
||||||
)
|
]
|
||||||
self.assertEqual(
|
assert list(History2.objects().as_pymongo()) == [{"_id": h1.id, "name": "db1"}]
|
||||||
list(History2.objects().as_pymongo()), [{"_id": h1.id, "name": "db1"}]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_connect_different_documents_to_different_database(self):
|
def test_connect_different_documents_to_different_database(self):
|
||||||
class History(Document):
|
class History(Document):
|
||||||
@@ -134,68 +139,64 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
h1 = History1(name="db1").save()
|
h1 = History1(name="db1").save()
|
||||||
h2 = History2(name="db2").save()
|
h2 = History2(name="db2").save()
|
||||||
|
|
||||||
self.assertEqual(History._collection.database.name, DEFAULT_DATABASE_NAME)
|
assert History._collection.database.name == DEFAULT_DATABASE_NAME
|
||||||
self.assertEqual(History1._collection.database.name, "db1")
|
assert History1._collection.database.name == "db1"
|
||||||
self.assertEqual(History2._collection.database.name, "db2")
|
assert History2._collection.database.name == "db2"
|
||||||
|
|
||||||
self.assertEqual(
|
assert list(History.objects().as_pymongo()) == [
|
||||||
list(History.objects().as_pymongo()), [{"_id": h.id, "name": "default"}]
|
{"_id": h.id, "name": "default"}
|
||||||
)
|
]
|
||||||
self.assertEqual(
|
assert list(History1.objects().as_pymongo()) == [{"_id": h1.id, "name": "db1"}]
|
||||||
list(History1.objects().as_pymongo()), [{"_id": h1.id, "name": "db1"}]
|
assert list(History2.objects().as_pymongo()) == [{"_id": h2.id, "name": "db2"}]
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
list(History2.objects().as_pymongo()), [{"_id": h2.id, "name": "db2"}]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_connect_fails_if_connect_2_times_with_default_alias(self):
|
def test_connect_fails_if_connect_2_times_with_default_alias(self):
|
||||||
connect("mongoenginetest")
|
connect("mongoenginetest")
|
||||||
|
|
||||||
with self.assertRaises(ConnectionFailure) as ctx_err:
|
with pytest.raises(ConnectionFailure) as exc_info:
|
||||||
connect("mongoenginetest2")
|
connect("mongoenginetest2")
|
||||||
self.assertEqual(
|
assert (
|
||||||
"A different connection with alias `default` was already registered. Use disconnect() first",
|
"A different connection with alias `default` was already registered. Use disconnect() first"
|
||||||
str(ctx_err.exception),
|
== str(exc_info.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_connect_fails_if_connect_2_times_with_custom_alias(self):
|
def test_connect_fails_if_connect_2_times_with_custom_alias(self):
|
||||||
connect("mongoenginetest", alias="alias1")
|
connect("mongoenginetest", alias="alias1")
|
||||||
|
|
||||||
with self.assertRaises(ConnectionFailure) as ctx_err:
|
with pytest.raises(ConnectionFailure) as exc_info:
|
||||||
connect("mongoenginetest2", alias="alias1")
|
connect("mongoenginetest2", alias="alias1")
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
"A different connection with alias `alias1` was already registered. Use disconnect() first",
|
"A different connection with alias `alias1` was already registered. Use disconnect() first"
|
||||||
str(ctx_err.exception),
|
== str(exc_info.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_connect_fails_if_similar_connection_settings_arent_defined_the_same_way(
|
def test_connect_fails_if_similar_connection_settings_arent_defined_the_same_way(
|
||||||
self
|
self,
|
||||||
):
|
):
|
||||||
"""Intended to keep the detecton function simple but robust"""
|
"""Intended to keep the detecton function simple but robust"""
|
||||||
db_name = "mongoenginetest"
|
db_name = "mongoenginetest"
|
||||||
db_alias = "alias1"
|
db_alias = "alias1"
|
||||||
connect(db=db_name, alias=db_alias, host="localhost", port=27017)
|
connect(db=db_name, alias=db_alias, host="localhost", port=27017)
|
||||||
|
|
||||||
with self.assertRaises(ConnectionFailure):
|
with pytest.raises(ConnectionFailure):
|
||||||
connect(host="mongodb://localhost:27017/%s" % db_name, alias=db_alias)
|
connect(host="mongodb://localhost:27017/%s" % db_name, alias=db_alias)
|
||||||
|
|
||||||
def test_connect_passes_silently_connect_multiple_times_with_same_config(self):
|
def test_connect_passes_silently_connect_multiple_times_with_same_config(self):
|
||||||
# test default connection to `test`
|
# test default connection to `test`
|
||||||
connect()
|
connect()
|
||||||
connect()
|
connect()
|
||||||
self.assertEqual(len(mongoengine.connection._connections), 1)
|
assert len(mongoengine.connection._connections) == 1
|
||||||
connect("test01", alias="test01")
|
connect("test01", alias="test01")
|
||||||
connect("test01", alias="test01")
|
connect("test01", alias="test01")
|
||||||
self.assertEqual(len(mongoengine.connection._connections), 2)
|
assert len(mongoengine.connection._connections) == 2
|
||||||
connect(host="mongodb://localhost:27017/mongoenginetest02", alias="test02")
|
connect(host="mongodb://localhost:27017/mongoenginetest02", alias="test02")
|
||||||
connect(host="mongodb://localhost:27017/mongoenginetest02", alias="test02")
|
connect(host="mongodb://localhost:27017/mongoenginetest02", alias="test02")
|
||||||
self.assertEqual(len(mongoengine.connection._connections), 3)
|
assert len(mongoengine.connection._connections) == 3
|
||||||
|
|
||||||
def test_connect_with_invalid_db_name(self):
|
def test_connect_with_invalid_db_name(self):
|
||||||
"""Ensure that connect() method fails fast if db name is invalid
|
"""Ensure that connect() method fails fast if db name is invalid
|
||||||
"""
|
"""
|
||||||
with self.assertRaises(InvalidName):
|
with pytest.raises(InvalidName):
|
||||||
connect("mongomock://localhost")
|
connect("mongomock://localhost")
|
||||||
|
|
||||||
def test_connect_with_db_name_external(self):
|
def test_connect_with_db_name_external(self):
|
||||||
@@ -205,38 +206,34 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
connect("$external")
|
connect("$external")
|
||||||
|
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
|
assert isinstance(conn, pymongo.mongo_client.MongoClient)
|
||||||
|
|
||||||
db = get_db()
|
db = get_db()
|
||||||
self.assertIsInstance(db, pymongo.database.Database)
|
assert isinstance(db, pymongo.database.Database)
|
||||||
self.assertEqual(db.name, "$external")
|
assert db.name == "$external"
|
||||||
|
|
||||||
connect("$external", alias="testdb")
|
connect("$external", alias="testdb")
|
||||||
conn = get_connection("testdb")
|
conn = get_connection("testdb")
|
||||||
self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
|
assert isinstance(conn, pymongo.mongo_client.MongoClient)
|
||||||
|
|
||||||
def test_connect_with_invalid_db_name_type(self):
|
def test_connect_with_invalid_db_name_type(self):
|
||||||
"""Ensure that connect() method fails fast if db name has invalid type
|
"""Ensure that connect() method fails fast if db name has invalid type
|
||||||
"""
|
"""
|
||||||
with self.assertRaises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
non_string_db_name = ["e. g. list instead of a string"]
|
non_string_db_name = ["e. g. list instead of a string"]
|
||||||
connect(non_string_db_name)
|
connect(non_string_db_name)
|
||||||
|
|
||||||
|
@require_mongomock
|
||||||
def test_connect_in_mocking(self):
|
def test_connect_in_mocking(self):
|
||||||
"""Ensure that the connect() method works properly in mocking.
|
"""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")
|
connect("mongoenginetest", host="mongomock://localhost")
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect("mongoenginetest2", host="mongomock://localhost", alias="testdb2")
|
connect("mongoenginetest2", host="mongomock://localhost", alias="testdb2")
|
||||||
conn = get_connection("testdb2")
|
conn = get_connection("testdb2")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
"mongoenginetest3",
|
"mongoenginetest3",
|
||||||
@@ -245,11 +242,11 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
alias="testdb3",
|
alias="testdb3",
|
||||||
)
|
)
|
||||||
conn = get_connection("testdb3")
|
conn = get_connection("testdb3")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect("mongoenginetest4", is_mock=True, alias="testdb4")
|
connect("mongoenginetest4", is_mock=True, alias="testdb4")
|
||||||
conn = get_connection("testdb4")
|
conn = get_connection("testdb4")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
host="mongodb://localhost:27017/mongoenginetest5",
|
host="mongodb://localhost:27017/mongoenginetest5",
|
||||||
@@ -257,11 +254,11 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
alias="testdb5",
|
alias="testdb5",
|
||||||
)
|
)
|
||||||
conn = get_connection("testdb5")
|
conn = get_connection("testdb5")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect(host="mongomock://localhost:27017/mongoenginetest6", alias="testdb6")
|
connect(host="mongomock://localhost:27017/mongoenginetest6", alias="testdb6")
|
||||||
conn = get_connection("testdb6")
|
conn = get_connection("testdb6")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
host="mongomock://localhost:27017/mongoenginetest7",
|
host="mongomock://localhost:27017/mongoenginetest7",
|
||||||
@@ -269,16 +266,12 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
alias="testdb7",
|
alias="testdb7",
|
||||||
)
|
)
|
||||||
conn = get_connection("testdb7")
|
conn = get_connection("testdb7")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
|
@require_mongomock
|
||||||
def test_default_database_with_mocking(self):
|
def test_default_database_with_mocking(self):
|
||||||
"""Ensure that the default database is correctly set when using mongomock.
|
"""Ensure that the default database is correctly set when using mongomock.
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
import mongomock
|
|
||||||
except ImportError:
|
|
||||||
raise SkipTest("you need mongomock installed to run this testcase")
|
|
||||||
|
|
||||||
disconnect_all()
|
disconnect_all()
|
||||||
|
|
||||||
class SomeDocument(Document):
|
class SomeDocument(Document):
|
||||||
@@ -288,37 +281,33 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
some_document = SomeDocument()
|
some_document = SomeDocument()
|
||||||
# database won't exist until we save a document
|
# database won't exist until we save a document
|
||||||
some_document.save()
|
some_document.save()
|
||||||
self.assertEqual(conn.get_default_database().name, "mongoenginetest")
|
assert conn.get_default_database().name == "mongoenginetest"
|
||||||
self.assertEqual(conn.database_names()[0], "mongoenginetest")
|
assert conn.database_names()[0] == "mongoenginetest"
|
||||||
|
|
||||||
|
@require_mongomock
|
||||||
def test_connect_with_host_list(self):
|
def test_connect_with_host_list(self):
|
||||||
"""Ensure that the connect() method works when host is a list
|
"""Ensure that the connect() method works when host is a list
|
||||||
|
|
||||||
Uses mongomock to test w/o needing multiple mongod/mongos processes
|
Uses mongomock to test w/o needing multiple mongod/mongos processes
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
import mongomock
|
|
||||||
except ImportError:
|
|
||||||
raise SkipTest("you need mongomock installed to run this testcase")
|
|
||||||
|
|
||||||
connect(host=["mongomock://localhost"])
|
connect(host=["mongomock://localhost"])
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect(host=["mongodb://localhost"], is_mock=True, alias="testdb2")
|
connect(host=["mongodb://localhost"], is_mock=True, alias="testdb2")
|
||||||
conn = get_connection("testdb2")
|
conn = get_connection("testdb2")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect(host=["localhost"], is_mock=True, alias="testdb3")
|
connect(host=["localhost"], is_mock=True, alias="testdb3")
|
||||||
conn = get_connection("testdb3")
|
conn = get_connection("testdb3")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
host=["mongomock://localhost:27017", "mongomock://localhost:27018"],
|
host=["mongomock://localhost:27017", "mongomock://localhost:27018"],
|
||||||
alias="testdb4",
|
alias="testdb4",
|
||||||
)
|
)
|
||||||
conn = get_connection("testdb4")
|
conn = get_connection("testdb4")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
host=["mongodb://localhost:27017", "mongodb://localhost:27018"],
|
host=["mongodb://localhost:27017", "mongodb://localhost:27018"],
|
||||||
@@ -326,13 +315,13 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
alias="testdb5",
|
alias="testdb5",
|
||||||
)
|
)
|
||||||
conn = get_connection("testdb5")
|
conn = get_connection("testdb5")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
host=["localhost:27017", "localhost:27018"], is_mock=True, alias="testdb6"
|
host=["localhost:27017", "localhost:27018"], is_mock=True, alias="testdb6"
|
||||||
)
|
)
|
||||||
conn = get_connection("testdb6")
|
conn = get_connection("testdb6")
|
||||||
self.assertIsInstance(conn, mongomock.MongoClient)
|
assert isinstance(conn, mongomock.MongoClient)
|
||||||
|
|
||||||
def test_disconnect_cleans_globals(self):
|
def test_disconnect_cleans_globals(self):
|
||||||
"""Ensure that the disconnect() method cleans the globals objects"""
|
"""Ensure that the disconnect() method cleans the globals objects"""
|
||||||
@@ -342,44 +331,42 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
|
|
||||||
connect("mongoenginetest")
|
connect("mongoenginetest")
|
||||||
|
|
||||||
self.assertEqual(len(connections), 1)
|
assert len(connections) == 1
|
||||||
self.assertEqual(len(dbs), 0)
|
assert len(dbs) == 0
|
||||||
self.assertEqual(len(connection_settings), 1)
|
assert len(connection_settings) == 1
|
||||||
|
|
||||||
class TestDoc(Document):
|
class TestDoc(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
TestDoc.drop_collection() # triggers the db
|
TestDoc.drop_collection() # triggers the db
|
||||||
self.assertEqual(len(dbs), 1)
|
assert len(dbs) == 1
|
||||||
|
|
||||||
disconnect()
|
disconnect()
|
||||||
self.assertEqual(len(connections), 0)
|
assert len(connections) == 0
|
||||||
self.assertEqual(len(dbs), 0)
|
assert len(dbs) == 0
|
||||||
self.assertEqual(len(connection_settings), 0)
|
assert len(connection_settings) == 0
|
||||||
|
|
||||||
def test_disconnect_cleans_cached_collection_attribute_in_document(self):
|
def test_disconnect_cleans_cached_collection_attribute_in_document(self):
|
||||||
"""Ensure that the disconnect() method works properly"""
|
"""Ensure that the disconnect() method works properly"""
|
||||||
conn1 = connect("mongoenginetest")
|
connect("mongoenginetest")
|
||||||
|
|
||||||
class History(Document):
|
class History(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertIsNone(History._collection)
|
assert History._collection is None
|
||||||
|
|
||||||
History.drop_collection()
|
History.drop_collection()
|
||||||
|
|
||||||
History.objects.first() # will trigger the caching of _collection attribute
|
History.objects.first() # will trigger the caching of _collection attribute
|
||||||
self.assertIsNotNone(History._collection)
|
assert History._collection is not None
|
||||||
|
|
||||||
disconnect()
|
disconnect()
|
||||||
|
|
||||||
self.assertIsNone(History._collection)
|
assert History._collection is None
|
||||||
|
|
||||||
with self.assertRaises(ConnectionFailure) as ctx_err:
|
with pytest.raises(ConnectionFailure) as exc_info:
|
||||||
History.objects.first()
|
History.objects.first()
|
||||||
self.assertEqual(
|
assert "You have not defined a default connection" == str(exc_info.value)
|
||||||
"You have not defined a default connection", str(ctx_err.exception)
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_connect_disconnect_works_on_same_document(self):
|
def test_connect_disconnect_works_on_same_document(self):
|
||||||
"""Ensure that the connect/disconnect works properly with a single Document"""
|
"""Ensure that the connect/disconnect works properly with a single Document"""
|
||||||
@@ -401,7 +388,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
disconnect()
|
disconnect()
|
||||||
|
|
||||||
# Make sure save doesnt work at this stage
|
# Make sure save doesnt work at this stage
|
||||||
with self.assertRaises(ConnectionFailure):
|
with pytest.raises(ConnectionFailure):
|
||||||
User(name="Wont work").save()
|
User(name="Wont work").save()
|
||||||
|
|
||||||
# Save in db2
|
# Save in db2
|
||||||
@@ -410,13 +397,13 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
disconnect()
|
disconnect()
|
||||||
|
|
||||||
db1_users = list(client[db1].user.find())
|
db1_users = list(client[db1].user.find())
|
||||||
self.assertEqual(db1_users, [{"_id": user1.id, "name": "John is in db1"}])
|
assert db1_users == [{"_id": user1.id, "name": "John is in db1"}]
|
||||||
db2_users = list(client[db2].user.find())
|
db2_users = list(client[db2].user.find())
|
||||||
self.assertEqual(db2_users, [{"_id": user2.id, "name": "Bob is in db2"}])
|
assert db2_users == [{"_id": user2.id, "name": "Bob is in db2"}]
|
||||||
|
|
||||||
def test_disconnect_silently_pass_if_alias_does_not_exist(self):
|
def test_disconnect_silently_pass_if_alias_does_not_exist(self):
|
||||||
connections = mongoengine.connection._connections
|
connections = mongoengine.connection._connections
|
||||||
self.assertEqual(len(connections), 0)
|
assert len(connections) == 0
|
||||||
disconnect(alias="not_exist")
|
disconnect(alias="not_exist")
|
||||||
|
|
||||||
def test_disconnect_all(self):
|
def test_disconnect_all(self):
|
||||||
@@ -439,26 +426,26 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
History1.drop_collection()
|
History1.drop_collection()
|
||||||
History1.objects.first()
|
History1.objects.first()
|
||||||
|
|
||||||
self.assertIsNotNone(History._collection)
|
assert History._collection is not None
|
||||||
self.assertIsNotNone(History1._collection)
|
assert History1._collection is not None
|
||||||
|
|
||||||
self.assertEqual(len(connections), 2)
|
assert len(connections) == 2
|
||||||
self.assertEqual(len(dbs), 2)
|
assert len(dbs) == 2
|
||||||
self.assertEqual(len(connection_settings), 2)
|
assert len(connection_settings) == 2
|
||||||
|
|
||||||
disconnect_all()
|
disconnect_all()
|
||||||
|
|
||||||
self.assertIsNone(History._collection)
|
assert History._collection is None
|
||||||
self.assertIsNone(History1._collection)
|
assert History1._collection is None
|
||||||
|
|
||||||
self.assertEqual(len(connections), 0)
|
assert len(connections) == 0
|
||||||
self.assertEqual(len(dbs), 0)
|
assert len(dbs) == 0
|
||||||
self.assertEqual(len(connection_settings), 0)
|
assert len(connection_settings) == 0
|
||||||
|
|
||||||
with self.assertRaises(ConnectionFailure):
|
with pytest.raises(ConnectionFailure):
|
||||||
History.objects.first()
|
History.objects.first()
|
||||||
|
|
||||||
with self.assertRaises(ConnectionFailure):
|
with pytest.raises(ConnectionFailure):
|
||||||
History1.objects.first()
|
History1.objects.first()
|
||||||
|
|
||||||
def test_disconnect_all_silently_pass_if_no_connection_exist(self):
|
def test_disconnect_all_silently_pass_if_no_connection_exist(self):
|
||||||
@@ -475,7 +462,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
|
|
||||||
expected_connection.server_info()
|
expected_connection.server_info()
|
||||||
|
|
||||||
self.assertEqual(expected_connection, actual_connection)
|
assert expected_connection == actual_connection
|
||||||
|
|
||||||
def test_connect_uri(self):
|
def test_connect_uri(self):
|
||||||
"""Ensure that the connect() method works properly with URIs."""
|
"""Ensure that the connect() method works properly with URIs."""
|
||||||
@@ -492,11 +479,11 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
|
assert isinstance(conn, pymongo.mongo_client.MongoClient)
|
||||||
|
|
||||||
db = get_db()
|
db = get_db()
|
||||||
self.assertIsInstance(db, pymongo.database.Database)
|
assert isinstance(db, pymongo.database.Database)
|
||||||
self.assertEqual(db.name, "mongoenginetest")
|
assert db.name == "mongoenginetest"
|
||||||
|
|
||||||
c.admin.system.users.delete_many({})
|
c.admin.system.users.delete_many({})
|
||||||
c.mongoenginetest.system.users.delete_many({})
|
c.mongoenginetest.system.users.delete_many({})
|
||||||
@@ -508,11 +495,11 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
connect("mongoenginetest", host="mongodb://localhost/")
|
connect("mongoenginetest", host="mongodb://localhost/")
|
||||||
|
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
|
assert isinstance(conn, pymongo.mongo_client.MongoClient)
|
||||||
|
|
||||||
db = get_db()
|
db = get_db()
|
||||||
self.assertIsInstance(db, pymongo.database.Database)
|
assert isinstance(db, pymongo.database.Database)
|
||||||
self.assertEqual(db.name, "mongoenginetest")
|
assert db.name == "mongoenginetest"
|
||||||
|
|
||||||
def test_connect_uri_default_db(self):
|
def test_connect_uri_default_db(self):
|
||||||
"""Ensure connect() defaults to the right database name if
|
"""Ensure connect() defaults to the right database name if
|
||||||
@@ -521,24 +508,25 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
connect(host="mongodb://localhost/")
|
connect(host="mongodb://localhost/")
|
||||||
|
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
|
assert isinstance(conn, pymongo.mongo_client.MongoClient)
|
||||||
|
|
||||||
db = get_db()
|
db = get_db()
|
||||||
self.assertIsInstance(db, pymongo.database.Database)
|
assert isinstance(db, pymongo.database.Database)
|
||||||
self.assertEqual(db.name, "test")
|
assert db.name == "test"
|
||||||
|
|
||||||
def test_uri_without_credentials_doesnt_override_conn_settings(self):
|
def test_uri_without_credentials_doesnt_override_conn_settings(self):
|
||||||
"""Ensure connect() uses the username & password params if the URI
|
"""Ensure connect() uses the username & password params if the URI
|
||||||
doesn't explicitly specify them.
|
doesn't explicitly specify them.
|
||||||
"""
|
"""
|
||||||
c = connect(
|
connect(
|
||||||
host="mongodb://localhost/mongoenginetest", username="user", password="pass"
|
host="mongodb://localhost/mongoenginetest", username="user", password="pass"
|
||||||
)
|
)
|
||||||
|
|
||||||
# OperationFailure means that mongoengine attempted authentication
|
# OperationFailure means that mongoengine attempted authentication
|
||||||
# w/ the provided username/password and failed - that's the desired
|
# w/ the provided username/password and failed - that's the desired
|
||||||
# behavior. If the MongoDB URI would override the credentials
|
# behavior. If the MongoDB URI would override the credentials
|
||||||
self.assertRaises(OperationFailure, get_db)
|
with pytest.raises(OperationFailure):
|
||||||
|
get_db()
|
||||||
|
|
||||||
def test_connect_uri_with_authsource(self):
|
def test_connect_uri_with_authsource(self):
|
||||||
"""Ensure that the connect() method works well with `authSource`
|
"""Ensure that the connect() method works well with `authSource`
|
||||||
@@ -556,7 +544,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
alias="test1",
|
alias="test1",
|
||||||
host="mongodb://username2:password@localhost/mongoenginetest",
|
host="mongodb://username2:password@localhost/mongoenginetest",
|
||||||
)
|
)
|
||||||
self.assertRaises(OperationFailure, test_conn.server_info)
|
with pytest.raises(OperationFailure):
|
||||||
|
test_conn.server_info()
|
||||||
|
|
||||||
# Authentication succeeds with "authSource"
|
# Authentication succeeds with "authSource"
|
||||||
authd_conn = connect(
|
authd_conn = connect(
|
||||||
@@ -568,8 +557,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
db = get_db("test2")
|
db = get_db("test2")
|
||||||
self.assertIsInstance(db, pymongo.database.Database)
|
assert isinstance(db, pymongo.database.Database)
|
||||||
self.assertEqual(db.name, "mongoenginetest")
|
assert db.name == "mongoenginetest"
|
||||||
|
|
||||||
# Clear all users
|
# Clear all users
|
||||||
authd_conn.admin.system.users.delete_many({})
|
authd_conn.admin.system.users.delete_many({})
|
||||||
@@ -579,13 +568,14 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
register_connection("testdb", "mongoenginetest2")
|
register_connection("testdb", "mongoenginetest2")
|
||||||
|
|
||||||
self.assertRaises(ConnectionFailure, get_connection)
|
with pytest.raises(ConnectionFailure):
|
||||||
|
get_connection()
|
||||||
conn = get_connection("testdb")
|
conn = get_connection("testdb")
|
||||||
self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
|
assert isinstance(conn, pymongo.mongo_client.MongoClient)
|
||||||
|
|
||||||
db = get_db("testdb")
|
db = get_db("testdb")
|
||||||
self.assertIsInstance(db, pymongo.database.Database)
|
assert isinstance(db, pymongo.database.Database)
|
||||||
self.assertEqual(db.name, "mongoenginetest2")
|
assert db.name == "mongoenginetest2"
|
||||||
|
|
||||||
def test_register_connection_defaults(self):
|
def test_register_connection_defaults(self):
|
||||||
"""Ensure that defaults are used when the host and port are None.
|
"""Ensure that defaults are used when the host and port are None.
|
||||||
@@ -593,18 +583,18 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
register_connection("testdb", "mongoenginetest", host=None, port=None)
|
register_connection("testdb", "mongoenginetest", host=None, port=None)
|
||||||
|
|
||||||
conn = get_connection("testdb")
|
conn = get_connection("testdb")
|
||||||
self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
|
assert isinstance(conn, pymongo.mongo_client.MongoClient)
|
||||||
|
|
||||||
def test_connection_kwargs(self):
|
def test_connection_kwargs(self):
|
||||||
"""Ensure that connection kwargs get passed to pymongo."""
|
"""Ensure that connection kwargs get passed to pymongo."""
|
||||||
connect("mongoenginetest", alias="t1", tz_aware=True)
|
connect("mongoenginetest", alias="t1", tz_aware=True)
|
||||||
conn = get_connection("t1")
|
conn = get_connection("t1")
|
||||||
|
|
||||||
self.assertTrue(get_tz_awareness(conn))
|
assert get_tz_awareness(conn)
|
||||||
|
|
||||||
connect("mongoenginetest2", alias="t2")
|
connect("mongoenginetest2", alias="t2")
|
||||||
conn = get_connection("t2")
|
conn = get_connection("t2")
|
||||||
self.assertFalse(get_tz_awareness(conn))
|
assert not get_tz_awareness(conn)
|
||||||
|
|
||||||
def test_connection_pool_via_kwarg(self):
|
def test_connection_pool_via_kwarg(self):
|
||||||
"""Ensure we can specify a max connection pool size using
|
"""Ensure we can specify a max connection pool size using
|
||||||
@@ -615,7 +605,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
conn = connect(
|
conn = connect(
|
||||||
"mongoenginetest", alias="max_pool_size_via_kwarg", **pool_size_kwargs
|
"mongoenginetest", alias="max_pool_size_via_kwarg", **pool_size_kwargs
|
||||||
)
|
)
|
||||||
self.assertEqual(conn.max_pool_size, 100)
|
assert conn.max_pool_size == 100
|
||||||
|
|
||||||
def test_connection_pool_via_uri(self):
|
def test_connection_pool_via_uri(self):
|
||||||
"""Ensure we can specify a max connection pool size using
|
"""Ensure we can specify a max connection pool size using
|
||||||
@@ -625,35 +615,37 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
host="mongodb://localhost/test?maxpoolsize=100",
|
host="mongodb://localhost/test?maxpoolsize=100",
|
||||||
alias="max_pool_size_via_uri",
|
alias="max_pool_size_via_uri",
|
||||||
)
|
)
|
||||||
self.assertEqual(conn.max_pool_size, 100)
|
assert conn.max_pool_size == 100
|
||||||
|
|
||||||
def test_write_concern(self):
|
def test_write_concern(self):
|
||||||
"""Ensure write concern can be specified in connect() via
|
"""Ensure write concern can be specified in connect() via
|
||||||
a kwarg or as part of the connection URI.
|
a kwarg or as part of the connection URI.
|
||||||
"""
|
"""
|
||||||
conn1 = connect(alias="conn1", host="mongodb://localhost/testing?w=1&j=true")
|
conn1 = connect(
|
||||||
conn2 = connect("testing", alias="conn2", w=1, j=True)
|
alias="conn1", host="mongodb://localhost/testing?w=1&journal=true"
|
||||||
self.assertEqual(conn1.write_concern.document, {"w": 1, "j": True})
|
)
|
||||||
self.assertEqual(conn2.write_concern.document, {"w": 1, "j": True})
|
conn2 = connect("testing", alias="conn2", w=1, journal=True)
|
||||||
|
assert conn1.write_concern.document == {"w": 1, "j": True}
|
||||||
|
assert conn2.write_concern.document == {"w": 1, "j": True}
|
||||||
|
|
||||||
def test_connect_with_replicaset_via_uri(self):
|
def test_connect_with_replicaset_via_uri(self):
|
||||||
"""Ensure connect() works when specifying a replicaSet via the
|
"""Ensure connect() works when specifying a replicaSet via the
|
||||||
MongoDB URI.
|
MongoDB URI.
|
||||||
"""
|
"""
|
||||||
c = connect(host="mongodb://localhost/test?replicaSet=local-rs")
|
connect(host="mongodb://localhost/test?replicaSet=local-rs")
|
||||||
db = get_db()
|
db = get_db()
|
||||||
self.assertIsInstance(db, pymongo.database.Database)
|
assert isinstance(db, pymongo.database.Database)
|
||||||
self.assertEqual(db.name, "test")
|
assert db.name == "test"
|
||||||
|
|
||||||
def test_connect_with_replicaset_via_kwargs(self):
|
def test_connect_with_replicaset_via_kwargs(self):
|
||||||
"""Ensure connect() works when specifying a replicaSet via the
|
"""Ensure connect() works when specifying a replicaSet via the
|
||||||
connection kwargs
|
connection kwargs
|
||||||
"""
|
"""
|
||||||
c = connect(replicaset="local-rs")
|
c = connect(replicaset="local-rs")
|
||||||
self.assertEqual(c._MongoClient__options.replica_set_name, "local-rs")
|
assert c._MongoClient__options.replica_set_name == "local-rs"
|
||||||
db = get_db()
|
db = get_db()
|
||||||
self.assertIsInstance(db, pymongo.database.Database)
|
assert isinstance(db, pymongo.database.Database)
|
||||||
self.assertEqual(db.name, "test")
|
assert db.name == "test"
|
||||||
|
|
||||||
def test_connect_tz_aware(self):
|
def test_connect_tz_aware(self):
|
||||||
connect("mongoenginetest", tz_aware=True)
|
connect("mongoenginetest", tz_aware=True)
|
||||||
@@ -666,13 +658,13 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
DateDoc(the_date=d).save()
|
DateDoc(the_date=d).save()
|
||||||
|
|
||||||
date_doc = DateDoc.objects.first()
|
date_doc = DateDoc.objects.first()
|
||||||
self.assertEqual(d, date_doc.the_date)
|
assert d == date_doc.the_date
|
||||||
|
|
||||||
def test_read_preference_from_parse(self):
|
def test_read_preference_from_parse(self):
|
||||||
conn = connect(
|
conn = connect(
|
||||||
host="mongodb://a1.vpc,a2.vpc,a3.vpc/prod?readPreference=secondaryPreferred"
|
host="mongodb://a1.vpc,a2.vpc,a3.vpc/prod?readPreference=secondaryPreferred"
|
||||||
)
|
)
|
||||||
self.assertEqual(conn.read_preference, ReadPreference.SECONDARY_PREFERRED)
|
assert conn.read_preference == ReadPreference.SECONDARY_PREFERRED
|
||||||
|
|
||||||
def test_multiple_connection_settings(self):
|
def test_multiple_connection_settings(self):
|
||||||
connect("mongoenginetest", alias="t1", host="localhost")
|
connect("mongoenginetest", alias="t1", host="localhost")
|
||||||
@@ -680,27 +672,27 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
connect("mongoenginetest2", alias="t2", host="127.0.0.1")
|
connect("mongoenginetest2", alias="t2", host="127.0.0.1")
|
||||||
|
|
||||||
mongo_connections = mongoengine.connection._connections
|
mongo_connections = mongoengine.connection._connections
|
||||||
self.assertEqual(len(mongo_connections.items()), 2)
|
assert len(mongo_connections.items()) == 2
|
||||||
self.assertIn("t1", mongo_connections.keys())
|
assert "t1" in mongo_connections.keys()
|
||||||
self.assertIn("t2", mongo_connections.keys())
|
assert "t2" in mongo_connections.keys()
|
||||||
|
|
||||||
# Handle PyMongo 3+ Async Connection
|
# Handle PyMongo 3+ Async Connection
|
||||||
# Ensure we are connected, throws ServerSelectionTimeoutError otherwise.
|
# Ensure we are connected, throws ServerSelectionTimeoutError otherwise.
|
||||||
# Purposely not catching exception to fail test if thrown.
|
# Purposely not catching exception to fail test if thrown.
|
||||||
mongo_connections["t1"].server_info()
|
mongo_connections["t1"].server_info()
|
||||||
mongo_connections["t2"].server_info()
|
mongo_connections["t2"].server_info()
|
||||||
self.assertEqual(mongo_connections["t1"].address[0], "localhost")
|
assert mongo_connections["t1"].address[0] == "localhost"
|
||||||
self.assertEqual(mongo_connections["t2"].address[0], "127.0.0.1")
|
assert mongo_connections["t2"].address[0] == "127.0.0.1"
|
||||||
|
|
||||||
def test_connect_2_databases_uses_same_client_if_only_dbname_differs(self):
|
def test_connect_2_databases_uses_same_client_if_only_dbname_differs(self):
|
||||||
c1 = connect(alias="testdb1", db="testdb1")
|
c1 = connect(alias="testdb1", db="testdb1")
|
||||||
c2 = connect(alias="testdb2", db="testdb2")
|
c2 = connect(alias="testdb2", db="testdb2")
|
||||||
self.assertIs(c1, c2)
|
assert c1 is c2
|
||||||
|
|
||||||
def test_connect_2_databases_uses_different_client_if_different_parameters(self):
|
def test_connect_2_databases_uses_different_client_if_different_parameters(self):
|
||||||
c1 = connect(alias="testdb1", db="testdb1", username="u1")
|
c1 = connect(alias="testdb1", db="testdb1", username="u1")
|
||||||
c2 = connect(alias="testdb2", db="testdb2", username="u2")
|
c2 = connect(alias="testdb2", db="testdb2", username="u2")
|
||||||
self.assertIsNot(c1, c2)
|
assert c1 is not c2
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,18 +1,68 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.connection import get_db
|
from mongoengine.connection import get_db
|
||||||
from mongoengine.context_managers import (
|
from mongoengine.context_managers import (
|
||||||
switch_db,
|
|
||||||
switch_collection,
|
|
||||||
no_sub_classes,
|
|
||||||
no_dereference,
|
no_dereference,
|
||||||
|
no_sub_classes,
|
||||||
query_counter,
|
query_counter,
|
||||||
|
set_read_write_concern,
|
||||||
|
set_write_concern,
|
||||||
|
switch_collection,
|
||||||
|
switch_db,
|
||||||
)
|
)
|
||||||
from mongoengine.pymongo_support import count_documents
|
from mongoengine.pymongo_support import count_documents
|
||||||
|
|
||||||
|
|
||||||
class ContextManagersTest(unittest.TestCase):
|
class TestContextManagers:
|
||||||
|
def test_set_write_concern(self):
|
||||||
|
connect("mongoenginetest")
|
||||||
|
|
||||||
|
class User(Document):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
collection = User._get_collection()
|
||||||
|
original_write_concern = collection.write_concern
|
||||||
|
|
||||||
|
with set_write_concern(
|
||||||
|
collection, {"w": "majority", "j": True, "wtimeout": 1234}
|
||||||
|
) as updated_collection:
|
||||||
|
assert updated_collection.write_concern.document == {
|
||||||
|
"w": "majority",
|
||||||
|
"j": True,
|
||||||
|
"wtimeout": 1234,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert original_write_concern.document == collection.write_concern.document
|
||||||
|
|
||||||
|
def test_set_read_write_concern(self):
|
||||||
|
connect("mongoenginetest")
|
||||||
|
|
||||||
|
class User(Document):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
collection = User._get_collection()
|
||||||
|
|
||||||
|
original_read_concern = collection.read_concern
|
||||||
|
original_write_concern = collection.write_concern
|
||||||
|
|
||||||
|
with set_read_write_concern(
|
||||||
|
collection,
|
||||||
|
{"w": "majority", "j": True, "wtimeout": 1234},
|
||||||
|
{"level": "local"},
|
||||||
|
) as update_collection:
|
||||||
|
assert update_collection.read_concern.document == {"level": "local"}
|
||||||
|
assert update_collection.write_concern.document == {
|
||||||
|
"w": "majority",
|
||||||
|
"j": True,
|
||||||
|
"wtimeout": 1234,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert original_read_concern.document == collection.read_concern.document
|
||||||
|
assert original_write_concern.document == collection.write_concern.document
|
||||||
|
|
||||||
def test_switch_db_context_manager(self):
|
def test_switch_db_context_manager(self):
|
||||||
connect("mongoenginetest")
|
connect("mongoenginetest")
|
||||||
register_connection("testdb-1", "mongoenginetest2")
|
register_connection("testdb-1", "mongoenginetest2")
|
||||||
@@ -23,20 +73,20 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
Group.drop_collection()
|
Group.drop_collection()
|
||||||
|
|
||||||
Group(name="hello - default").save()
|
Group(name="hello - default").save()
|
||||||
self.assertEqual(1, Group.objects.count())
|
assert 1 == Group.objects.count()
|
||||||
|
|
||||||
with switch_db(Group, "testdb-1") as Group:
|
with switch_db(Group, "testdb-1") as Group:
|
||||||
|
|
||||||
self.assertEqual(0, Group.objects.count())
|
assert 0 == Group.objects.count()
|
||||||
|
|
||||||
Group(name="hello").save()
|
Group(name="hello").save()
|
||||||
|
|
||||||
self.assertEqual(1, Group.objects.count())
|
assert 1 == Group.objects.count()
|
||||||
|
|
||||||
Group.drop_collection()
|
Group.drop_collection()
|
||||||
self.assertEqual(0, Group.objects.count())
|
assert 0 == Group.objects.count()
|
||||||
|
|
||||||
self.assertEqual(1, Group.objects.count())
|
assert 1 == Group.objects.count()
|
||||||
|
|
||||||
def test_switch_collection_context_manager(self):
|
def test_switch_collection_context_manager(self):
|
||||||
connect("mongoenginetest")
|
connect("mongoenginetest")
|
||||||
@@ -51,20 +101,20 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
Group.drop_collection() # drops in group1
|
Group.drop_collection() # drops in group1
|
||||||
|
|
||||||
Group(name="hello - group").save()
|
Group(name="hello - group").save()
|
||||||
self.assertEqual(1, Group.objects.count())
|
assert 1 == Group.objects.count()
|
||||||
|
|
||||||
with switch_collection(Group, "group1") as Group:
|
with switch_collection(Group, "group1") as Group:
|
||||||
|
|
||||||
self.assertEqual(0, Group.objects.count())
|
assert 0 == Group.objects.count()
|
||||||
|
|
||||||
Group(name="hello - group1").save()
|
Group(name="hello - group1").save()
|
||||||
|
|
||||||
self.assertEqual(1, Group.objects.count())
|
assert 1 == Group.objects.count()
|
||||||
|
|
||||||
Group.drop_collection()
|
Group.drop_collection()
|
||||||
self.assertEqual(0, Group.objects.count())
|
assert 0 == Group.objects.count()
|
||||||
|
|
||||||
self.assertEqual(1, Group.objects.count())
|
assert 1 == Group.objects.count()
|
||||||
|
|
||||||
def test_no_dereference_context_manager_object_id(self):
|
def test_no_dereference_context_manager_object_id(self):
|
||||||
"""Ensure that DBRef items in ListFields aren't dereferenced.
|
"""Ensure that DBRef items in ListFields aren't dereferenced.
|
||||||
@@ -89,20 +139,20 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
Group(ref=user, members=User.objects, generic=user).save()
|
Group(ref=user, members=User.objects, generic=user).save()
|
||||||
|
|
||||||
with no_dereference(Group) as NoDeRefGroup:
|
with no_dereference(Group) as NoDeRefGroup:
|
||||||
self.assertTrue(Group._fields["members"]._auto_dereference)
|
assert Group._fields["members"]._auto_dereference
|
||||||
self.assertFalse(NoDeRefGroup._fields["members"]._auto_dereference)
|
assert not NoDeRefGroup._fields["members"]._auto_dereference
|
||||||
|
|
||||||
with no_dereference(Group) as Group:
|
with no_dereference(Group) as Group:
|
||||||
group = Group.objects.first()
|
group = Group.objects.first()
|
||||||
for m in group.members:
|
for m in group.members:
|
||||||
self.assertNotIsInstance(m, User)
|
assert not isinstance(m, User)
|
||||||
self.assertNotIsInstance(group.ref, User)
|
assert not isinstance(group.ref, User)
|
||||||
self.assertNotIsInstance(group.generic, User)
|
assert not isinstance(group.generic, User)
|
||||||
|
|
||||||
for m in group.members:
|
for m in group.members:
|
||||||
self.assertIsInstance(m, User)
|
assert isinstance(m, User)
|
||||||
self.assertIsInstance(group.ref, User)
|
assert isinstance(group.ref, User)
|
||||||
self.assertIsInstance(group.generic, User)
|
assert isinstance(group.generic, User)
|
||||||
|
|
||||||
def test_no_dereference_context_manager_dbref(self):
|
def test_no_dereference_context_manager_dbref(self):
|
||||||
"""Ensure that DBRef items in ListFields aren't dereferenced.
|
"""Ensure that DBRef items in ListFields aren't dereferenced.
|
||||||
@@ -127,18 +177,18 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
Group(ref=user, members=User.objects, generic=user).save()
|
Group(ref=user, members=User.objects, generic=user).save()
|
||||||
|
|
||||||
with no_dereference(Group) as NoDeRefGroup:
|
with no_dereference(Group) as NoDeRefGroup:
|
||||||
self.assertTrue(Group._fields["members"]._auto_dereference)
|
assert Group._fields["members"]._auto_dereference
|
||||||
self.assertFalse(NoDeRefGroup._fields["members"]._auto_dereference)
|
assert not NoDeRefGroup._fields["members"]._auto_dereference
|
||||||
|
|
||||||
with no_dereference(Group) as Group:
|
with no_dereference(Group) as Group:
|
||||||
group = Group.objects.first()
|
group = Group.objects.first()
|
||||||
self.assertTrue(all([not isinstance(m, User) for m in group.members]))
|
assert all([not isinstance(m, User) for m in group.members])
|
||||||
self.assertNotIsInstance(group.ref, User)
|
assert not isinstance(group.ref, User)
|
||||||
self.assertNotIsInstance(group.generic, User)
|
assert not isinstance(group.generic, User)
|
||||||
|
|
||||||
self.assertTrue(all([isinstance(m, User) for m in group.members]))
|
assert all([isinstance(m, User) for m in group.members])
|
||||||
self.assertIsInstance(group.ref, User)
|
assert isinstance(group.ref, User)
|
||||||
self.assertIsInstance(group.generic, User)
|
assert isinstance(group.generic, User)
|
||||||
|
|
||||||
def test_no_sub_classes(self):
|
def test_no_sub_classes(self):
|
||||||
class A(Document):
|
class A(Document):
|
||||||
@@ -159,32 +209,32 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
B(x=30).save()
|
B(x=30).save()
|
||||||
C(x=40).save()
|
C(x=40).save()
|
||||||
|
|
||||||
self.assertEqual(A.objects.count(), 5)
|
assert A.objects.count() == 5
|
||||||
self.assertEqual(B.objects.count(), 3)
|
assert B.objects.count() == 3
|
||||||
self.assertEqual(C.objects.count(), 1)
|
assert C.objects.count() == 1
|
||||||
|
|
||||||
with no_sub_classes(A):
|
with no_sub_classes(A):
|
||||||
self.assertEqual(A.objects.count(), 2)
|
assert A.objects.count() == 2
|
||||||
|
|
||||||
for obj in A.objects:
|
for obj in A.objects:
|
||||||
self.assertEqual(obj.__class__, A)
|
assert obj.__class__ == A
|
||||||
|
|
||||||
with no_sub_classes(B):
|
with no_sub_classes(B):
|
||||||
self.assertEqual(B.objects.count(), 2)
|
assert B.objects.count() == 2
|
||||||
|
|
||||||
for obj in B.objects:
|
for obj in B.objects:
|
||||||
self.assertEqual(obj.__class__, B)
|
assert obj.__class__ == B
|
||||||
|
|
||||||
with no_sub_classes(C):
|
with no_sub_classes(C):
|
||||||
self.assertEqual(C.objects.count(), 1)
|
assert C.objects.count() == 1
|
||||||
|
|
||||||
for obj in C.objects:
|
for obj in C.objects:
|
||||||
self.assertEqual(obj.__class__, C)
|
assert obj.__class__ == C
|
||||||
|
|
||||||
# Confirm context manager exit correctly
|
# Confirm context manager exit correctly
|
||||||
self.assertEqual(A.objects.count(), 5)
|
assert A.objects.count() == 5
|
||||||
self.assertEqual(B.objects.count(), 3)
|
assert B.objects.count() == 3
|
||||||
self.assertEqual(C.objects.count(), 1)
|
assert C.objects.count() == 1
|
||||||
|
|
||||||
def test_no_sub_classes_modification_to_document_class_are_temporary(self):
|
def test_no_sub_classes_modification_to_document_class_are_temporary(self):
|
||||||
class A(Document):
|
class A(Document):
|
||||||
@@ -194,28 +244,27 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
class B(A):
|
class B(A):
|
||||||
z = IntField()
|
z = IntField()
|
||||||
|
|
||||||
self.assertEqual(A._subclasses, ("A", "A.B"))
|
assert A._subclasses == ("A", "A.B")
|
||||||
with no_sub_classes(A):
|
with no_sub_classes(A):
|
||||||
self.assertEqual(A._subclasses, ("A",))
|
assert A._subclasses == ("A",)
|
||||||
self.assertEqual(A._subclasses, ("A", "A.B"))
|
assert A._subclasses == ("A", "A.B")
|
||||||
|
|
||||||
self.assertEqual(B._subclasses, ("A.B",))
|
assert B._subclasses == ("A.B",)
|
||||||
with no_sub_classes(B):
|
with no_sub_classes(B):
|
||||||
self.assertEqual(B._subclasses, ("A.B",))
|
assert B._subclasses == ("A.B",)
|
||||||
self.assertEqual(B._subclasses, ("A.B",))
|
assert B._subclasses == ("A.B",)
|
||||||
|
|
||||||
def test_no_subclass_context_manager_does_not_swallow_exception(self):
|
def test_no_subclass_context_manager_does_not_swallow_exception(self):
|
||||||
class User(Document):
|
class User(Document):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
with self.assertRaises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
with no_sub_classes(User):
|
with no_sub_classes(User):
|
||||||
raise TypeError()
|
raise TypeError()
|
||||||
|
|
||||||
def test_query_counter_does_not_swallow_exception(self):
|
def test_query_counter_does_not_swallow_exception(self):
|
||||||
|
with pytest.raises(TypeError):
|
||||||
with self.assertRaises(TypeError):
|
with query_counter():
|
||||||
with query_counter() as q:
|
|
||||||
raise TypeError()
|
raise TypeError()
|
||||||
|
|
||||||
def test_query_counter_temporarily_modifies_profiling_level(self):
|
def test_query_counter_temporarily_modifies_profiling_level(self):
|
||||||
@@ -225,12 +274,12 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
initial_profiling_level = db.profiling_level()
|
initial_profiling_level = db.profiling_level()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
NEW_LEVEL = 1
|
new_level = 1
|
||||||
db.set_profiling_level(NEW_LEVEL)
|
db.set_profiling_level(new_level)
|
||||||
self.assertEqual(db.profiling_level(), NEW_LEVEL)
|
assert db.profiling_level() == new_level
|
||||||
with query_counter() as q:
|
with query_counter():
|
||||||
self.assertEqual(db.profiling_level(), 2)
|
assert db.profiling_level() == 2
|
||||||
self.assertEqual(db.profiling_level(), NEW_LEVEL)
|
assert db.profiling_level() == new_level
|
||||||
except Exception:
|
except Exception:
|
||||||
db.set_profiling_level(
|
db.set_profiling_level(
|
||||||
initial_profiling_level
|
initial_profiling_level
|
||||||
@@ -255,33 +304,77 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
|
|
||||||
counter = 0
|
counter = 0
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, counter)
|
assert q == counter
|
||||||
self.assertEqual(
|
assert q == counter # Ensures previous count query did not get counted
|
||||||
q, counter
|
|
||||||
) # Ensures previous count query did not get counted
|
|
||||||
|
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
issue_1_insert_query()
|
issue_1_insert_query()
|
||||||
counter += 1
|
counter += 1
|
||||||
self.assertEqual(q, counter)
|
assert q == counter
|
||||||
|
|
||||||
for _ in range(4):
|
for _ in range(4):
|
||||||
issue_1_find_query()
|
issue_1_find_query()
|
||||||
counter += 1
|
counter += 1
|
||||||
self.assertEqual(q, counter)
|
assert q == counter
|
||||||
|
|
||||||
for _ in range(3):
|
for _ in range(3):
|
||||||
issue_1_count_query()
|
issue_1_count_query()
|
||||||
counter += 1
|
counter += 1
|
||||||
self.assertEqual(q, counter)
|
assert q == counter
|
||||||
|
|
||||||
self.assertEqual(int(q), counter) # test __int__
|
assert int(q) == counter # test __int__
|
||||||
self.assertEqual(repr(q), str(int(q))) # test __repr__
|
assert repr(q) == str(int(q)) # test __repr__
|
||||||
self.assertGreater(q, -1) # test __gt__
|
assert q > -1 # test __gt__
|
||||||
self.assertGreaterEqual(q, int(q)) # test __gte__
|
assert q >= int(q) # test __gte__
|
||||||
self.assertNotEqual(q, -1)
|
assert q != -1
|
||||||
self.assertLess(q, 1000)
|
assert q < 1000
|
||||||
self.assertLessEqual(q, int(q))
|
assert q <= int(q)
|
||||||
|
|
||||||
|
def test_query_counter_alias(self):
|
||||||
|
"""query_counter works properly with db aliases?"""
|
||||||
|
# Register a connection with db_alias testdb-1
|
||||||
|
register_connection("testdb-1", "mongoenginetest2")
|
||||||
|
|
||||||
|
class A(Document):
|
||||||
|
"""Uses default db_alias"""
|
||||||
|
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
class B(Document):
|
||||||
|
"""Uses testdb-1 db_alias"""
|
||||||
|
|
||||||
|
name = StringField()
|
||||||
|
meta = {"db_alias": "testdb-1"}
|
||||||
|
|
||||||
|
A.drop_collection()
|
||||||
|
B.drop_collection()
|
||||||
|
|
||||||
|
with query_counter() as q:
|
||||||
|
assert q == 0
|
||||||
|
A.objects.create(name="A")
|
||||||
|
assert q == 1
|
||||||
|
a = A.objects.first()
|
||||||
|
assert q == 2
|
||||||
|
a.name = "Test A"
|
||||||
|
a.save()
|
||||||
|
assert q == 3
|
||||||
|
# querying the other db should'nt alter the counter
|
||||||
|
B.objects().first()
|
||||||
|
assert q == 3
|
||||||
|
|
||||||
|
with query_counter(alias="testdb-1") as q:
|
||||||
|
assert q == 0
|
||||||
|
B.objects.create(name="B")
|
||||||
|
assert q == 1
|
||||||
|
b = B.objects.first()
|
||||||
|
assert q == 2
|
||||||
|
b.name = "Test B"
|
||||||
|
b.save()
|
||||||
|
assert b.name == "Test B"
|
||||||
|
assert q == 3
|
||||||
|
# querying the other db should'nt alter the counter
|
||||||
|
A.objects().first()
|
||||||
|
assert q == 3
|
||||||
|
|
||||||
def test_query_counter_counts_getmore_queries(self):
|
def test_query_counter_counts_getmore_queries(self):
|
||||||
connect("mongoenginetest")
|
connect("mongoenginetest")
|
||||||
@@ -296,9 +389,9 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
) # first batch of documents contains 101 documents
|
) # first batch of documents contains 101 documents
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
list(collection.find())
|
list(collection.find())
|
||||||
self.assertEqual(q, 2) # 1st select + 1 getmore
|
assert q == 2 # 1st select + 1 getmore
|
||||||
|
|
||||||
def test_query_counter_ignores_particular_queries(self):
|
def test_query_counter_ignores_particular_queries(self):
|
||||||
connect("mongoenginetest")
|
connect("mongoenginetest")
|
||||||
@@ -308,18 +401,18 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
collection.insert_many([{"test": "garbage %s" % i} for i in range(10)])
|
collection.insert_many([{"test": "garbage %s" % i} for i in range(10)])
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
cursor = collection.find()
|
cursor = collection.find()
|
||||||
self.assertEqual(q, 0) # cursor wasn't opened yet
|
assert q == 0 # cursor wasn't opened yet
|
||||||
_ = next(cursor) # opens the cursor and fires the find query
|
_ = next(cursor) # opens the cursor and fires the find query
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
cursor.close() # issues a `killcursors` query that is ignored by the context
|
cursor.close() # issues a `killcursors` query that is ignored by the context
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
_ = (
|
_ = (
|
||||||
db.system.indexes.find_one()
|
db.system.indexes.find_one()
|
||||||
) # queries on db.system.indexes are ignored as well
|
) # queries on db.system.indexes are ignored as well
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from six import iterkeys
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mongoengine import Document
|
from mongoengine import Document
|
||||||
from mongoengine.base.datastructures import StrictDict, BaseList, BaseDict
|
from mongoengine.base.datastructures import BaseDict, BaseList, StrictDict
|
||||||
|
|
||||||
|
|
||||||
class DocumentStub(object):
|
class DocumentStub(object):
|
||||||
@@ -13,15 +14,15 @@ class DocumentStub(object):
|
|||||||
self._changed_fields.append(key)
|
self._changed_fields.append(key)
|
||||||
|
|
||||||
|
|
||||||
class TestBaseDict(unittest.TestCase):
|
class TestBaseDict:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_basedict(dict_items):
|
def _get_basedict(dict_items):
|
||||||
"""Get a BaseList bound to a fake document instance"""
|
"""Get a BaseList bound to a fake document instance"""
|
||||||
fake_doc = DocumentStub()
|
fake_doc = DocumentStub()
|
||||||
base_list = BaseDict(dict_items, instance=None, name="my_name")
|
base_list = BaseDict(dict_items, instance=None, name="my_name")
|
||||||
base_list._instance = (
|
base_list._instance = (
|
||||||
fake_doc
|
fake_doc # hack to inject the mock, it does not work in the constructor
|
||||||
) # hack to inject the mock, it does not work in the constructor
|
)
|
||||||
return base_list
|
return base_list
|
||||||
|
|
||||||
def test___init___(self):
|
def test___init___(self):
|
||||||
@@ -31,48 +32,48 @@ class TestBaseDict(unittest.TestCase):
|
|||||||
dict_items = {"k": "v"}
|
dict_items = {"k": "v"}
|
||||||
doc = MyDoc()
|
doc = MyDoc()
|
||||||
base_dict = BaseDict(dict_items, instance=doc, name="my_name")
|
base_dict = BaseDict(dict_items, instance=doc, name="my_name")
|
||||||
self.assertIsInstance(base_dict._instance, Document)
|
assert isinstance(base_dict._instance, Document)
|
||||||
self.assertEqual(base_dict._name, "my_name")
|
assert base_dict._name == "my_name"
|
||||||
self.assertEqual(base_dict, dict_items)
|
assert base_dict == dict_items
|
||||||
|
|
||||||
def test_setdefault_calls_mark_as_changed(self):
|
def test_setdefault_calls_mark_as_changed(self):
|
||||||
base_dict = self._get_basedict({})
|
base_dict = self._get_basedict({})
|
||||||
base_dict.setdefault("k", "v")
|
base_dict.setdefault("k", "v")
|
||||||
self.assertEqual(base_dict._instance._changed_fields, [base_dict._name])
|
assert base_dict._instance._changed_fields == [base_dict._name]
|
||||||
|
|
||||||
def test_popitems_calls_mark_as_changed(self):
|
def test_popitems_calls_mark_as_changed(self):
|
||||||
base_dict = self._get_basedict({"k": "v"})
|
base_dict = self._get_basedict({"k": "v"})
|
||||||
self.assertEqual(base_dict.popitem(), ("k", "v"))
|
assert base_dict.popitem() == ("k", "v")
|
||||||
self.assertEqual(base_dict._instance._changed_fields, [base_dict._name])
|
assert base_dict._instance._changed_fields == [base_dict._name]
|
||||||
self.assertFalse(base_dict)
|
assert not base_dict
|
||||||
|
|
||||||
def test_pop_calls_mark_as_changed(self):
|
def test_pop_calls_mark_as_changed(self):
|
||||||
base_dict = self._get_basedict({"k": "v"})
|
base_dict = self._get_basedict({"k": "v"})
|
||||||
self.assertEqual(base_dict.pop("k"), "v")
|
assert base_dict.pop("k") == "v"
|
||||||
self.assertEqual(base_dict._instance._changed_fields, [base_dict._name])
|
assert base_dict._instance._changed_fields == [base_dict._name]
|
||||||
self.assertFalse(base_dict)
|
assert not base_dict
|
||||||
|
|
||||||
def test_pop_calls_does_not_mark_as_changed_when_it_fails(self):
|
def test_pop_calls_does_not_mark_as_changed_when_it_fails(self):
|
||||||
base_dict = self._get_basedict({"k": "v"})
|
base_dict = self._get_basedict({"k": "v"})
|
||||||
with self.assertRaises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
base_dict.pop("X")
|
base_dict.pop("X")
|
||||||
self.assertFalse(base_dict._instance._changed_fields)
|
assert not base_dict._instance._changed_fields
|
||||||
|
|
||||||
def test_clear_calls_mark_as_changed(self):
|
def test_clear_calls_mark_as_changed(self):
|
||||||
base_dict = self._get_basedict({"k": "v"})
|
base_dict = self._get_basedict({"k": "v"})
|
||||||
base_dict.clear()
|
base_dict.clear()
|
||||||
self.assertEqual(base_dict._instance._changed_fields, ["my_name"])
|
assert base_dict._instance._changed_fields == ["my_name"]
|
||||||
self.assertEqual(base_dict, {})
|
assert base_dict == {}
|
||||||
|
|
||||||
def test___delitem___calls_mark_as_changed(self):
|
def test___delitem___calls_mark_as_changed(self):
|
||||||
base_dict = self._get_basedict({"k": "v"})
|
base_dict = self._get_basedict({"k": "v"})
|
||||||
del base_dict["k"]
|
del base_dict["k"]
|
||||||
self.assertEqual(base_dict._instance._changed_fields, ["my_name.k"])
|
assert base_dict._instance._changed_fields == ["my_name.k"]
|
||||||
self.assertEqual(base_dict, {})
|
assert base_dict == {}
|
||||||
|
|
||||||
def test___getitem____KeyError(self):
|
def test___getitem____KeyError(self):
|
||||||
base_dict = self._get_basedict({})
|
base_dict = self._get_basedict({})
|
||||||
with self.assertRaises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
base_dict["new"]
|
base_dict["new"]
|
||||||
|
|
||||||
def test___getitem____simple_value(self):
|
def test___getitem____simple_value(self):
|
||||||
@@ -82,62 +83,62 @@ class TestBaseDict(unittest.TestCase):
|
|||||||
def test___getitem____sublist_gets_converted_to_BaseList(self):
|
def test___getitem____sublist_gets_converted_to_BaseList(self):
|
||||||
base_dict = self._get_basedict({"k": [0, 1, 2]})
|
base_dict = self._get_basedict({"k": [0, 1, 2]})
|
||||||
sub_list = base_dict["k"]
|
sub_list = base_dict["k"]
|
||||||
self.assertEqual(sub_list, [0, 1, 2])
|
assert sub_list == [0, 1, 2]
|
||||||
self.assertIsInstance(sub_list, BaseList)
|
assert isinstance(sub_list, BaseList)
|
||||||
self.assertIs(sub_list._instance, base_dict._instance)
|
assert sub_list._instance is base_dict._instance
|
||||||
self.assertEqual(sub_list._name, "my_name.k")
|
assert sub_list._name == "my_name.k"
|
||||||
self.assertEqual(base_dict._instance._changed_fields, [])
|
assert base_dict._instance._changed_fields == []
|
||||||
|
|
||||||
# Challenge mark_as_changed from sublist
|
# Challenge mark_as_changed from sublist
|
||||||
sub_list[1] = None
|
sub_list[1] = None
|
||||||
self.assertEqual(base_dict._instance._changed_fields, ["my_name.k.1"])
|
assert base_dict._instance._changed_fields == ["my_name.k.1"]
|
||||||
|
|
||||||
def test___getitem____subdict_gets_converted_to_BaseDict(self):
|
def test___getitem____subdict_gets_converted_to_BaseDict(self):
|
||||||
base_dict = self._get_basedict({"k": {"subk": "subv"}})
|
base_dict = self._get_basedict({"k": {"subk": "subv"}})
|
||||||
sub_dict = base_dict["k"]
|
sub_dict = base_dict["k"]
|
||||||
self.assertEqual(sub_dict, {"subk": "subv"})
|
assert sub_dict == {"subk": "subv"}
|
||||||
self.assertIsInstance(sub_dict, BaseDict)
|
assert isinstance(sub_dict, BaseDict)
|
||||||
self.assertIs(sub_dict._instance, base_dict._instance)
|
assert sub_dict._instance is base_dict._instance
|
||||||
self.assertEqual(sub_dict._name, "my_name.k")
|
assert sub_dict._name == "my_name.k"
|
||||||
self.assertEqual(base_dict._instance._changed_fields, [])
|
assert base_dict._instance._changed_fields == []
|
||||||
|
|
||||||
# Challenge mark_as_changed from subdict
|
# Challenge mark_as_changed from subdict
|
||||||
sub_dict["subk"] = None
|
sub_dict["subk"] = None
|
||||||
self.assertEqual(base_dict._instance._changed_fields, ["my_name.k.subk"])
|
assert base_dict._instance._changed_fields == ["my_name.k.subk"]
|
||||||
|
|
||||||
def test_get_sublist_gets_converted_to_BaseList_just_like__getitem__(self):
|
def test_get_sublist_gets_converted_to_BaseList_just_like__getitem__(self):
|
||||||
base_dict = self._get_basedict({"k": [0, 1, 2]})
|
base_dict = self._get_basedict({"k": [0, 1, 2]})
|
||||||
sub_list = base_dict.get("k")
|
sub_list = base_dict.get("k")
|
||||||
self.assertEqual(sub_list, [0, 1, 2])
|
assert sub_list == [0, 1, 2]
|
||||||
self.assertIsInstance(sub_list, BaseList)
|
assert isinstance(sub_list, BaseList)
|
||||||
|
|
||||||
def test_get_returns_the_same_as___getitem__(self):
|
def test_get_returns_the_same_as___getitem__(self):
|
||||||
base_dict = self._get_basedict({"k": [0, 1, 2]})
|
base_dict = self._get_basedict({"k": [0, 1, 2]})
|
||||||
get_ = base_dict.get("k")
|
get_ = base_dict.get("k")
|
||||||
getitem_ = base_dict["k"]
|
getitem_ = base_dict["k"]
|
||||||
self.assertEqual(get_, getitem_)
|
assert get_ == getitem_
|
||||||
|
|
||||||
def test_get_default(self):
|
def test_get_default(self):
|
||||||
base_dict = self._get_basedict({})
|
base_dict = self._get_basedict({})
|
||||||
sentinel = object()
|
sentinel = object()
|
||||||
self.assertEqual(base_dict.get("new"), None)
|
assert base_dict.get("new") is None
|
||||||
self.assertIs(base_dict.get("new", sentinel), sentinel)
|
assert base_dict.get("new", sentinel) is sentinel
|
||||||
|
|
||||||
def test___setitem___calls_mark_as_changed(self):
|
def test___setitem___calls_mark_as_changed(self):
|
||||||
base_dict = self._get_basedict({})
|
base_dict = self._get_basedict({})
|
||||||
base_dict["k"] = "v"
|
base_dict["k"] = "v"
|
||||||
self.assertEqual(base_dict._instance._changed_fields, ["my_name.k"])
|
assert base_dict._instance._changed_fields == ["my_name.k"]
|
||||||
self.assertEqual(base_dict, {"k": "v"})
|
assert base_dict == {"k": "v"}
|
||||||
|
|
||||||
def test_update_calls_mark_as_changed(self):
|
def test_update_calls_mark_as_changed(self):
|
||||||
base_dict = self._get_basedict({})
|
base_dict = self._get_basedict({})
|
||||||
base_dict.update({"k": "v"})
|
base_dict.update({"k": "v"})
|
||||||
self.assertEqual(base_dict._instance._changed_fields, ["my_name"])
|
assert base_dict._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test___setattr____not_tracked_by_changes(self):
|
def test___setattr____not_tracked_by_changes(self):
|
||||||
base_dict = self._get_basedict({})
|
base_dict = self._get_basedict({})
|
||||||
base_dict.a_new_attr = "test"
|
base_dict.a_new_attr = "test"
|
||||||
self.assertEqual(base_dict._instance._changed_fields, [])
|
assert base_dict._instance._changed_fields == []
|
||||||
|
|
||||||
def test___delattr____tracked_by_changes(self):
|
def test___delattr____tracked_by_changes(self):
|
||||||
# This is probably a bug as __setattr__ is not tracked
|
# This is probably a bug as __setattr__ is not tracked
|
||||||
@@ -146,18 +147,18 @@ class TestBaseDict(unittest.TestCase):
|
|||||||
base_dict = self._get_basedict({})
|
base_dict = self._get_basedict({})
|
||||||
base_dict.a_new_attr = "test"
|
base_dict.a_new_attr = "test"
|
||||||
del base_dict.a_new_attr
|
del base_dict.a_new_attr
|
||||||
self.assertEqual(base_dict._instance._changed_fields, ["my_name.a_new_attr"])
|
assert base_dict._instance._changed_fields == ["my_name.a_new_attr"]
|
||||||
|
|
||||||
|
|
||||||
class TestBaseList(unittest.TestCase):
|
class TestBaseList:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_baselist(list_items):
|
def _get_baselist(list_items):
|
||||||
"""Get a BaseList bound to a fake document instance"""
|
"""Get a BaseList bound to a fake document instance"""
|
||||||
fake_doc = DocumentStub()
|
fake_doc = DocumentStub()
|
||||||
base_list = BaseList(list_items, instance=None, name="my_name")
|
base_list = BaseList(list_items, instance=None, name="my_name")
|
||||||
base_list._instance = (
|
base_list._instance = (
|
||||||
fake_doc
|
fake_doc # hack to inject the mock, it does not work in the constructor
|
||||||
) # hack to inject the mock, it does not work in the constructor
|
)
|
||||||
return base_list
|
return base_list
|
||||||
|
|
||||||
def test___init___(self):
|
def test___init___(self):
|
||||||
@@ -167,14 +168,14 @@ class TestBaseList(unittest.TestCase):
|
|||||||
list_items = [True]
|
list_items = [True]
|
||||||
doc = MyDoc()
|
doc = MyDoc()
|
||||||
base_list = BaseList(list_items, instance=doc, name="my_name")
|
base_list = BaseList(list_items, instance=doc, name="my_name")
|
||||||
self.assertIsInstance(base_list._instance, Document)
|
assert isinstance(base_list._instance, Document)
|
||||||
self.assertEqual(base_list._name, "my_name")
|
assert base_list._name == "my_name"
|
||||||
self.assertEqual(base_list, list_items)
|
assert base_list == list_items
|
||||||
|
|
||||||
def test___iter__(self):
|
def test___iter__(self):
|
||||||
values = [True, False, True, False]
|
values = [True, False, True, False]
|
||||||
base_list = BaseList(values, instance=None, name="my_name")
|
base_list = BaseList(values, instance=None, name="my_name")
|
||||||
self.assertEqual(values, list(base_list))
|
assert values == list(base_list)
|
||||||
|
|
||||||
def test___iter___allow_modification_while_iterating_withou_error(self):
|
def test___iter___allow_modification_while_iterating_withou_error(self):
|
||||||
# regular list allows for this, thus this subclass must comply to that
|
# regular list allows for this, thus this subclass must comply to that
|
||||||
@@ -185,9 +186,9 @@ class TestBaseList(unittest.TestCase):
|
|||||||
|
|
||||||
def test_append_calls_mark_as_changed(self):
|
def test_append_calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([])
|
base_list = self._get_baselist([])
|
||||||
self.assertFalse(base_list._instance._changed_fields)
|
assert not base_list._instance._changed_fields
|
||||||
base_list.append(True)
|
base_list.append(True)
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test_subclass_append(self):
|
def test_subclass_append(self):
|
||||||
# Due to the way mark_as_changed_wrapper is implemented
|
# Due to the way mark_as_changed_wrapper is implemented
|
||||||
@@ -200,198 +201,198 @@ class TestBaseList(unittest.TestCase):
|
|||||||
|
|
||||||
def test___getitem__using_simple_index(self):
|
def test___getitem__using_simple_index(self):
|
||||||
base_list = self._get_baselist([0, 1, 2])
|
base_list = self._get_baselist([0, 1, 2])
|
||||||
self.assertEqual(base_list[0], 0)
|
assert base_list[0] == 0
|
||||||
self.assertEqual(base_list[1], 1)
|
assert base_list[1] == 1
|
||||||
self.assertEqual(base_list[-1], 2)
|
assert base_list[-1] == 2
|
||||||
|
|
||||||
def test___getitem__using_slice(self):
|
def test___getitem__using_slice(self):
|
||||||
base_list = self._get_baselist([0, 1, 2])
|
base_list = self._get_baselist([0, 1, 2])
|
||||||
self.assertEqual(base_list[1:3], [1, 2])
|
assert base_list[1:3] == [1, 2]
|
||||||
self.assertEqual(base_list[0:3:2], [0, 2])
|
assert base_list[0:3:2] == [0, 2]
|
||||||
|
|
||||||
def test___getitem___using_slice_returns_list(self):
|
def test___getitem___using_slice_returns_list(self):
|
||||||
# Bug: using slice does not properly handles the instance
|
# Bug: using slice does not properly handles the instance
|
||||||
# and mark_as_changed behaviour.
|
# and mark_as_changed behaviour.
|
||||||
base_list = self._get_baselist([0, 1, 2])
|
base_list = self._get_baselist([0, 1, 2])
|
||||||
sliced = base_list[1:3]
|
sliced = base_list[1:3]
|
||||||
self.assertEqual(sliced, [1, 2])
|
assert sliced == [1, 2]
|
||||||
self.assertIsInstance(sliced, list)
|
assert isinstance(sliced, list)
|
||||||
self.assertEqual(base_list._instance._changed_fields, [])
|
assert base_list._instance._changed_fields == []
|
||||||
|
|
||||||
def test___getitem__sublist_returns_BaseList_bound_to_instance(self):
|
def test___getitem__sublist_returns_BaseList_bound_to_instance(self):
|
||||||
base_list = self._get_baselist([[1, 2], [3, 4]])
|
base_list = self._get_baselist([[1, 2], [3, 4]])
|
||||||
sub_list = base_list[0]
|
sub_list = base_list[0]
|
||||||
self.assertEqual(sub_list, [1, 2])
|
assert sub_list == [1, 2]
|
||||||
self.assertIsInstance(sub_list, BaseList)
|
assert isinstance(sub_list, BaseList)
|
||||||
self.assertIs(sub_list._instance, base_list._instance)
|
assert sub_list._instance is base_list._instance
|
||||||
self.assertEqual(sub_list._name, "my_name.0")
|
assert sub_list._name == "my_name.0"
|
||||||
self.assertEqual(base_list._instance._changed_fields, [])
|
assert base_list._instance._changed_fields == []
|
||||||
|
|
||||||
# Challenge mark_as_changed from sublist
|
# Challenge mark_as_changed from sublist
|
||||||
sub_list[1] = None
|
sub_list[1] = None
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name.0.1"])
|
assert base_list._instance._changed_fields == ["my_name.0.1"]
|
||||||
|
|
||||||
def test___getitem__subdict_returns_BaseList_bound_to_instance(self):
|
def test___getitem__subdict_returns_BaseList_bound_to_instance(self):
|
||||||
base_list = self._get_baselist([{"subk": "subv"}])
|
base_list = self._get_baselist([{"subk": "subv"}])
|
||||||
sub_dict = base_list[0]
|
sub_dict = base_list[0]
|
||||||
self.assertEqual(sub_dict, {"subk": "subv"})
|
assert sub_dict == {"subk": "subv"}
|
||||||
self.assertIsInstance(sub_dict, BaseDict)
|
assert isinstance(sub_dict, BaseDict)
|
||||||
self.assertIs(sub_dict._instance, base_list._instance)
|
assert sub_dict._instance is base_list._instance
|
||||||
self.assertEqual(sub_dict._name, "my_name.0")
|
assert sub_dict._name == "my_name.0"
|
||||||
self.assertEqual(base_list._instance._changed_fields, [])
|
assert base_list._instance._changed_fields == []
|
||||||
|
|
||||||
# Challenge mark_as_changed from subdict
|
# Challenge mark_as_changed from subdict
|
||||||
sub_dict["subk"] = None
|
sub_dict["subk"] = None
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name.0.subk"])
|
assert base_list._instance._changed_fields == ["my_name.0.subk"]
|
||||||
|
|
||||||
def test_extend_calls_mark_as_changed(self):
|
def test_extend_calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([])
|
base_list = self._get_baselist([])
|
||||||
base_list.extend([True])
|
base_list.extend([True])
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test_insert_calls_mark_as_changed(self):
|
def test_insert_calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([])
|
base_list = self._get_baselist([])
|
||||||
base_list.insert(0, True)
|
base_list.insert(0, True)
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test_remove_calls_mark_as_changed(self):
|
def test_remove_calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([True])
|
base_list = self._get_baselist([True])
|
||||||
base_list.remove(True)
|
base_list.remove(True)
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test_remove_not_mark_as_changed_when_it_fails(self):
|
def test_remove_not_mark_as_changed_when_it_fails(self):
|
||||||
base_list = self._get_baselist([True])
|
base_list = self._get_baselist([True])
|
||||||
with self.assertRaises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
base_list.remove(False)
|
base_list.remove(False)
|
||||||
self.assertFalse(base_list._instance._changed_fields)
|
assert not base_list._instance._changed_fields
|
||||||
|
|
||||||
def test_pop_calls_mark_as_changed(self):
|
def test_pop_calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([True])
|
base_list = self._get_baselist([True])
|
||||||
base_list.pop()
|
base_list.pop()
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test_reverse_calls_mark_as_changed(self):
|
def test_reverse_calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([True, False])
|
base_list = self._get_baselist([True, False])
|
||||||
base_list.reverse()
|
base_list.reverse()
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test___delitem___calls_mark_as_changed(self):
|
def test___delitem___calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([True])
|
base_list = self._get_baselist([True])
|
||||||
del base_list[0]
|
del base_list[0]
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test___setitem___calls_with_full_slice_mark_as_changed(self):
|
def test___setitem___calls_with_full_slice_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([])
|
base_list = self._get_baselist([])
|
||||||
base_list[:] = [
|
base_list[:] = [
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
] # Will use __setslice__ under py2 and __setitem__ under py3
|
]
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
self.assertEqual(base_list, [0, 1])
|
assert base_list == [0, 1]
|
||||||
|
|
||||||
def test___setitem___calls_with_partial_slice_mark_as_changed(self):
|
def test___setitem___calls_with_partial_slice_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([0, 1, 2])
|
base_list = self._get_baselist([0, 1, 2])
|
||||||
base_list[0:2] = [
|
base_list[0:2] = [
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
] # Will use __setslice__ under py2 and __setitem__ under py3
|
]
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
self.assertEqual(base_list, [1, 0, 2])
|
assert base_list == [1, 0, 2]
|
||||||
|
|
||||||
def test___setitem___calls_with_step_slice_mark_as_changed(self):
|
def test___setitem___calls_with_step_slice_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([0, 1, 2])
|
base_list = self._get_baselist([0, 1, 2])
|
||||||
base_list[0:3:2] = [-1, -2] # uses __setitem__ in both py2 & 3
|
base_list[0:3:2] = [-1, -2] # uses __setitem__
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
self.assertEqual(base_list, [-1, 1, -2])
|
assert base_list == [-1, 1, -2]
|
||||||
|
|
||||||
def test___setitem___with_slice(self):
|
def test___setitem___with_slice(self):
|
||||||
base_list = self._get_baselist([0, 1, 2, 3, 4, 5])
|
base_list = self._get_baselist([0, 1, 2, 3, 4, 5])
|
||||||
base_list[0:6:2] = [None, None, None]
|
base_list[0:6:2] = [None, None, None]
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
self.assertEqual(base_list, [None, 1, None, 3, None, 5])
|
assert base_list == [None, 1, None, 3, None, 5]
|
||||||
|
|
||||||
def test___setitem___item_0_calls_mark_as_changed(self):
|
def test___setitem___item_0_calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([True])
|
base_list = self._get_baselist([True])
|
||||||
base_list[0] = False
|
base_list[0] = False
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
self.assertEqual(base_list, [False])
|
assert base_list == [False]
|
||||||
|
|
||||||
def test___setitem___item_1_calls_mark_as_changed(self):
|
def test___setitem___item_1_calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([True, True])
|
base_list = self._get_baselist([True, True])
|
||||||
base_list[1] = False
|
base_list[1] = False
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name.1"])
|
assert base_list._instance._changed_fields == ["my_name.1"]
|
||||||
self.assertEqual(base_list, [True, False])
|
assert base_list == [True, False]
|
||||||
|
|
||||||
def test___delslice___calls_mark_as_changed(self):
|
def test___delslice___calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([0, 1])
|
base_list = self._get_baselist([0, 1])
|
||||||
del base_list[0:1]
|
del base_list[0:1]
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
self.assertEqual(base_list, [1])
|
assert base_list == [1]
|
||||||
|
|
||||||
def test___iadd___calls_mark_as_changed(self):
|
def test___iadd___calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([True])
|
base_list = self._get_baselist([True])
|
||||||
base_list += [False]
|
base_list += [False]
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test___imul___calls_mark_as_changed(self):
|
def test___imul___calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([True])
|
base_list = self._get_baselist([True])
|
||||||
self.assertEqual(base_list._instance._changed_fields, [])
|
assert base_list._instance._changed_fields == []
|
||||||
base_list *= 2
|
base_list *= 2
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test_sort_calls_not_marked_as_changed_when_it_fails(self):
|
def test_sort_calls_not_marked_as_changed_when_it_fails(self):
|
||||||
base_list = self._get_baselist([True])
|
base_list = self._get_baselist([True])
|
||||||
with self.assertRaises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
base_list.sort(key=1)
|
base_list.sort(key=1)
|
||||||
|
|
||||||
self.assertEqual(base_list._instance._changed_fields, [])
|
assert base_list._instance._changed_fields == []
|
||||||
|
|
||||||
def test_sort_calls_mark_as_changed(self):
|
def test_sort_calls_mark_as_changed(self):
|
||||||
base_list = self._get_baselist([True, False])
|
base_list = self._get_baselist([True, False])
|
||||||
base_list.sort()
|
base_list.sort()
|
||||||
self.assertEqual(base_list._instance._changed_fields, ["my_name"])
|
assert base_list._instance._changed_fields == ["my_name"]
|
||||||
|
|
||||||
def test_sort_calls_with_key(self):
|
def test_sort_calls_with_key(self):
|
||||||
base_list = self._get_baselist([1, 2, 11])
|
base_list = self._get_baselist([1, 2, 11])
|
||||||
base_list.sort(key=lambda i: str(i))
|
base_list.sort(key=lambda i: str(i))
|
||||||
self.assertEqual(base_list, [1, 11, 2])
|
assert base_list == [1, 11, 2]
|
||||||
|
|
||||||
|
|
||||||
class TestStrictDict(unittest.TestCase):
|
class TestStrictDict(unittest.TestCase):
|
||||||
def strict_dict_class(self, *args, **kwargs):
|
|
||||||
return StrictDict.create(*args, **kwargs)
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.dtype = self.strict_dict_class(("a", "b", "c"))
|
self.dtype = self.strict_dict_class(("a", "b", "c"))
|
||||||
|
|
||||||
|
def strict_dict_class(self, *args, **kwargs):
|
||||||
|
return StrictDict.create(*args, **kwargs)
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
d = self.dtype(a=1, b=1, c=1)
|
d = self.dtype(a=1, b=1, c=1)
|
||||||
self.assertEqual((d.a, d.b, d.c), (1, 1, 1))
|
assert (d.a, d.b, d.c) == (1, 1, 1)
|
||||||
|
|
||||||
def test_iterkeys(self):
|
def test_iterkeys(self):
|
||||||
d = self.dtype(a=1)
|
d = self.dtype(a=1)
|
||||||
self.assertEqual(list(iterkeys(d)), ["a"])
|
assert list(d.keys()) == ["a"]
|
||||||
|
|
||||||
def test_len(self):
|
def test_len(self):
|
||||||
d = self.dtype(a=1)
|
d = self.dtype(a=1)
|
||||||
self.assertEqual(len(d), 1)
|
assert len(d) == 1
|
||||||
|
|
||||||
def test_pop(self):
|
def test_pop(self):
|
||||||
d = self.dtype(a=1)
|
d = self.dtype(a=1)
|
||||||
self.assertIn("a", d)
|
assert "a" in d
|
||||||
d.pop("a")
|
d.pop("a")
|
||||||
self.assertNotIn("a", d)
|
assert "a" not in d
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
d = self.dtype(a=1, b=2, c=3)
|
d = self.dtype(a=1, b=2, c=3)
|
||||||
self.assertEqual(repr(d), '{"a": 1, "b": 2, "c": 3}')
|
assert repr(d) == '{"a": 1, "b": 2, "c": 3}'
|
||||||
|
|
||||||
# make sure quotes are escaped properly
|
# make sure quotes are escaped properly
|
||||||
d = self.dtype(a='"', b="'", c="")
|
d = self.dtype(a='"', b="'", c="")
|
||||||
self.assertEqual(repr(d), '{"a": \'"\', "b": "\'", "c": \'\'}')
|
assert repr(d) == '{"a": \'"\', "b": "\'", "c": \'\'}'
|
||||||
|
|
||||||
def test_init_fails_on_nonexisting_attrs(self):
|
def test_init_fails_on_nonexisting_attrs(self):
|
||||||
with self.assertRaises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
self.dtype(a=1, b=2, d=3)
|
self.dtype(a=1, b=2, d=3)
|
||||||
|
|
||||||
def test_eq(self):
|
def test_eq(self):
|
||||||
@@ -403,45 +404,46 @@ class TestStrictDict(unittest.TestCase):
|
|||||||
h = self.strict_dict_class(("a", "c", "b"))(a=1, b=1, c=1)
|
h = self.strict_dict_class(("a", "c", "b"))(a=1, b=1, c=1)
|
||||||
i = self.strict_dict_class(("a", "c", "b"))(a=1, b=1, c=2)
|
i = self.strict_dict_class(("a", "c", "b"))(a=1, b=1, c=2)
|
||||||
|
|
||||||
self.assertEqual(d, dd)
|
assert d == dd
|
||||||
self.assertNotEqual(d, e)
|
assert d != e
|
||||||
self.assertNotEqual(d, f)
|
assert d != f
|
||||||
self.assertNotEqual(d, g)
|
assert d != g
|
||||||
self.assertNotEqual(f, d)
|
assert f != d
|
||||||
self.assertEqual(d, h)
|
assert d == h
|
||||||
self.assertNotEqual(d, i)
|
assert d != i
|
||||||
|
|
||||||
def test_setattr_getattr(self):
|
def test_setattr_getattr(self):
|
||||||
d = self.dtype()
|
d = self.dtype()
|
||||||
d.a = 1
|
d.a = 1
|
||||||
self.assertEqual(d.a, 1)
|
assert d.a == 1
|
||||||
self.assertRaises(AttributeError, getattr, d, "b")
|
with pytest.raises(AttributeError):
|
||||||
|
getattr(d, "b")
|
||||||
|
|
||||||
def test_setattr_raises_on_nonexisting_attr(self):
|
def test_setattr_raises_on_nonexisting_attr(self):
|
||||||
d = self.dtype()
|
d = self.dtype()
|
||||||
with self.assertRaises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
d.x = 1
|
d.x = 1
|
||||||
|
|
||||||
def test_setattr_getattr_special(self):
|
def test_setattr_getattr_special(self):
|
||||||
d = self.strict_dict_class(["items"])
|
d = self.strict_dict_class(["items"])
|
||||||
d.items = 1
|
d.items = 1
|
||||||
self.assertEqual(d.items, 1)
|
assert d.items == 1
|
||||||
|
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
d = self.dtype(a=1)
|
d = self.dtype(a=1)
|
||||||
self.assertEqual(d.get("a"), 1)
|
assert d.get("a") == 1
|
||||||
self.assertEqual(d.get("b", "bla"), "bla")
|
assert d.get("b", "bla") == "bla"
|
||||||
|
|
||||||
def test_items(self):
|
def test_items(self):
|
||||||
d = self.dtype(a=1)
|
d = self.dtype(a=1)
|
||||||
self.assertEqual(d.items(), [("a", 1)])
|
assert d.items() == [("a", 1)]
|
||||||
d = self.dtype(a=1, b=2)
|
d = self.dtype(a=1, b=2)
|
||||||
self.assertEqual(d.items(), [("a", 1), ("b", 2)])
|
assert d.items() == [("a", 1), ("b", 2)]
|
||||||
|
|
||||||
def test_mappings_protocol(self):
|
def test_mappings_protocol(self):
|
||||||
d = self.dtype(a=1, b=2)
|
d = self.dtype(a=1, b=2)
|
||||||
self.assertEqual(dict(d), {"a": 1, "b": 2})
|
assert dict(d) == {"a": 1, "b": 2}
|
||||||
self.assertEqual(dict(**d), {"a": 1, "b": 2})
|
assert dict(**d) == {"a": 1, "b": 2}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -2,10 +2,8 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from bson import DBRef, ObjectId
|
from bson import DBRef, ObjectId
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.connection import get_db
|
|
||||||
from mongoengine.context_managers import query_counter
|
from mongoengine.context_managers import query_counter
|
||||||
|
|
||||||
|
|
||||||
@@ -42,37 +40,37 @@ class FieldTest(unittest.TestCase):
|
|||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
len(group_obj._data["members"])
|
len(group_obj._data["members"])
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
len(group_obj.members)
|
len(group_obj.members)
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
# Document select_related
|
# Document select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first().select_related()
|
group_obj = Group.objects.first().select_related()
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
# Queryset select_related
|
# Queryset select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
group_objs = Group.objects.select_related()
|
group_objs = Group.objects.select_related()
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
for group_obj in group_objs:
|
for group_obj in group_objs:
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
User.drop_collection()
|
User.drop_collection()
|
||||||
Group.drop_collection()
|
Group.drop_collection()
|
||||||
@@ -99,40 +97,40 @@ class FieldTest(unittest.TestCase):
|
|||||||
group.reload() # Confirm reload works
|
group.reload() # Confirm reload works
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
self.assertTrue(group_obj._data["members"]._dereferenced)
|
assert group_obj._data["members"]._dereferenced
|
||||||
|
|
||||||
# verifies that no additional queries gets executed
|
# verifies that no additional queries gets executed
|
||||||
# if we re-iterate over the ListField once it is
|
# if we re-iterate over the ListField once it is
|
||||||
# dereferenced
|
# dereferenced
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
self.assertTrue(group_obj._data["members"]._dereferenced)
|
assert group_obj._data["members"]._dereferenced
|
||||||
|
|
||||||
# Document select_related
|
# Document select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first().select_related()
|
group_obj = Group.objects.first().select_related()
|
||||||
|
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
# Queryset select_related
|
# Queryset select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
group_objs = Group.objects.select_related()
|
group_objs = Group.objects.select_related()
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
for group_obj in group_objs:
|
for group_obj in group_objs:
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
def test_list_item_dereference_orphan_dbref(self):
|
def test_list_item_dereference_orphan_dbref(self):
|
||||||
"""Ensure that orphan DBRef items in ListFields are dereferenced.
|
"""Ensure that orphan DBRef items in ListFields are dereferenced.
|
||||||
@@ -159,21 +157,21 @@ class FieldTest(unittest.TestCase):
|
|||||||
# Group.members list is an orphan DBRef
|
# Group.members list is an orphan DBRef
|
||||||
User.objects[0].delete()
|
User.objects[0].delete()
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
self.assertTrue(group_obj._data["members"]._dereferenced)
|
assert group_obj._data["members"]._dereferenced
|
||||||
|
|
||||||
# verifies that no additional queries gets executed
|
# verifies that no additional queries gets executed
|
||||||
# if we re-iterate over the ListField once it is
|
# if we re-iterate over the ListField once it is
|
||||||
# dereferenced
|
# dereferenced
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
self.assertTrue(group_obj._data["members"]._dereferenced)
|
assert group_obj._data["members"]._dereferenced
|
||||||
|
|
||||||
User.drop_collection()
|
User.drop_collection()
|
||||||
Group.drop_collection()
|
Group.drop_collection()
|
||||||
@@ -197,8 +195,8 @@ class FieldTest(unittest.TestCase):
|
|||||||
Group(members=User.objects).save()
|
Group(members=User.objects).save()
|
||||||
group = Group.objects.first()
|
group = Group.objects.first()
|
||||||
|
|
||||||
self.assertEqual(Group._get_collection().find_one()["members"], [1])
|
assert Group._get_collection().find_one()["members"] == [1]
|
||||||
self.assertEqual(group.members, [user])
|
assert group.members == [user]
|
||||||
|
|
||||||
def test_handle_old_style_references(self):
|
def test_handle_old_style_references(self):
|
||||||
"""Ensure that DBRef items in ListFields are dereferenced.
|
"""Ensure that DBRef items in ListFields are dereferenced.
|
||||||
@@ -231,8 +229,8 @@ class FieldTest(unittest.TestCase):
|
|||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
group = Group.objects.first()
|
group = Group.objects.first()
|
||||||
self.assertEqual(group.members[0].name, "user 1")
|
assert group.members[0].name == "user 1"
|
||||||
self.assertEqual(group.members[-1].name, "String!")
|
assert group.members[-1].name == "String!"
|
||||||
|
|
||||||
def test_migrate_references(self):
|
def test_migrate_references(self):
|
||||||
"""Example of migrating ReferenceField storage
|
"""Example of migrating ReferenceField storage
|
||||||
@@ -253,12 +251,12 @@ class FieldTest(unittest.TestCase):
|
|||||||
group = Group(author=user, members=[user]).save()
|
group = Group(author=user, members=[user]).save()
|
||||||
|
|
||||||
raw_data = Group._get_collection().find_one()
|
raw_data = Group._get_collection().find_one()
|
||||||
self.assertIsInstance(raw_data["author"], DBRef)
|
assert isinstance(raw_data["author"], DBRef)
|
||||||
self.assertIsInstance(raw_data["members"][0], DBRef)
|
assert isinstance(raw_data["members"][0], DBRef)
|
||||||
group = Group.objects.first()
|
group = Group.objects.first()
|
||||||
|
|
||||||
self.assertEqual(group.author, user)
|
assert group.author == user
|
||||||
self.assertEqual(group.members, [user])
|
assert group.members == [user]
|
||||||
|
|
||||||
# Migrate the model definition
|
# Migrate the model definition
|
||||||
class Group(Document):
|
class Group(Document):
|
||||||
@@ -273,12 +271,12 @@ class FieldTest(unittest.TestCase):
|
|||||||
g.save()
|
g.save()
|
||||||
|
|
||||||
group = Group.objects.first()
|
group = Group.objects.first()
|
||||||
self.assertEqual(group.author, user)
|
assert group.author == user
|
||||||
self.assertEqual(group.members, [user])
|
assert group.members == [user]
|
||||||
|
|
||||||
raw_data = Group._get_collection().find_one()
|
raw_data = Group._get_collection().find_one()
|
||||||
self.assertIsInstance(raw_data["author"], ObjectId)
|
assert isinstance(raw_data["author"], ObjectId)
|
||||||
self.assertIsInstance(raw_data["members"][0], ObjectId)
|
assert isinstance(raw_data["members"][0], ObjectId)
|
||||||
|
|
||||||
def test_recursive_reference(self):
|
def test_recursive_reference(self):
|
||||||
"""Ensure that ReferenceFields can reference their own documents.
|
"""Ensure that ReferenceFields can reference their own documents.
|
||||||
@@ -309,43 +307,43 @@ class FieldTest(unittest.TestCase):
|
|||||||
Employee(name="Funky Gibbon", boss=bill, friends=friends).save()
|
Employee(name="Funky Gibbon", boss=bill, friends=friends).save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
peter = Employee.objects.with_id(peter.id)
|
peter = Employee.objects.with_id(peter.id)
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
peter.boss
|
peter.boss
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
peter.friends
|
peter.friends
|
||||||
self.assertEqual(q, 3)
|
assert q == 3
|
||||||
|
|
||||||
# Document select_related
|
# Document select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
peter = Employee.objects.with_id(peter.id).select_related()
|
peter = Employee.objects.with_id(peter.id).select_related()
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
self.assertEqual(peter.boss, bill)
|
assert peter.boss == bill
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
self.assertEqual(peter.friends, friends)
|
assert peter.friends == friends
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
# Queryset select_related
|
# Queryset select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
employees = Employee.objects(boss=bill).select_related()
|
employees = Employee.objects(boss=bill).select_related()
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
for employee in employees:
|
for employee in employees:
|
||||||
self.assertEqual(employee.boss, bill)
|
assert employee.boss == bill
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
self.assertEqual(employee.friends, friends)
|
assert employee.friends == friends
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
def test_list_of_lists_of_references(self):
|
def test_list_of_lists_of_references(self):
|
||||||
class User(Document):
|
class User(Document):
|
||||||
@@ -366,10 +364,10 @@ class FieldTest(unittest.TestCase):
|
|||||||
u3 = User.objects.create(name="u3")
|
u3 = User.objects.create(name="u3")
|
||||||
|
|
||||||
SimpleList.objects.create(users=[u1, u2, u3])
|
SimpleList.objects.create(users=[u1, u2, u3])
|
||||||
self.assertEqual(SimpleList.objects.all()[0].users, [u1, u2, u3])
|
assert SimpleList.objects.all()[0].users == [u1, u2, u3]
|
||||||
|
|
||||||
Post.objects.create(user_lists=[[u1, u2], [u3]])
|
Post.objects.create(user_lists=[[u1, u2], [u3]])
|
||||||
self.assertEqual(Post.objects.all()[0].user_lists, [[u1, u2], [u3]])
|
assert Post.objects.all()[0].user_lists == [[u1, u2], [u3]]
|
||||||
|
|
||||||
def test_circular_reference(self):
|
def test_circular_reference(self):
|
||||||
"""Ensure you can handle circular references
|
"""Ensure you can handle circular references
|
||||||
@@ -403,9 +401,7 @@ class FieldTest(unittest.TestCase):
|
|||||||
daughter.relations.append(self_rel)
|
daughter.relations.append(self_rel)
|
||||||
daughter.save()
|
daughter.save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert "[<Person: Mother>, <Person: Daughter>]" == "%s" % Person.objects()
|
||||||
"[<Person: Mother>, <Person: Daughter>]", "%s" % Person.objects()
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_circular_reference_on_self(self):
|
def test_circular_reference_on_self(self):
|
||||||
"""Ensure you can handle circular references
|
"""Ensure you can handle circular references
|
||||||
@@ -432,9 +428,7 @@ class FieldTest(unittest.TestCase):
|
|||||||
daughter.relations.append(daughter)
|
daughter.relations.append(daughter)
|
||||||
daughter.save()
|
daughter.save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert "[<Person: Mother>, <Person: Daughter>]" == "%s" % Person.objects()
|
||||||
"[<Person: Mother>, <Person: Daughter>]", "%s" % Person.objects()
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_circular_tree_reference(self):
|
def test_circular_tree_reference(self):
|
||||||
"""Ensure you can handle circular references with more than one level
|
"""Ensure you can handle circular references with more than one level
|
||||||
@@ -473,9 +467,9 @@ class FieldTest(unittest.TestCase):
|
|||||||
anna.other.name = "Anna's friends"
|
anna.other.name = "Anna's friends"
|
||||||
anna.save()
|
anna.save()
|
||||||
|
|
||||||
self.assertEqual(
|
assert (
|
||||||
"[<Person: Paul>, <Person: Maria>, <Person: Julia>, <Person: Anna>]",
|
"[<Person: Paul>, <Person: Maria>, <Person: Julia>, <Person: Anna>]"
|
||||||
"%s" % Person.objects(),
|
== "%s" % Person.objects()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_generic_reference(self):
|
def test_generic_reference(self):
|
||||||
@@ -516,52 +510,52 @@ class FieldTest(unittest.TestCase):
|
|||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for m in group_obj.members:
|
for m in group_obj.members:
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
# Document select_related
|
# Document select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first().select_related()
|
group_obj = Group.objects.first().select_related()
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for m in group_obj.members:
|
for m in group_obj.members:
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
# Queryset select_related
|
# Queryset select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_objs = Group.objects.select_related()
|
group_objs = Group.objects.select_related()
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for group_obj in group_objs:
|
for group_obj in group_objs:
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for m in group_obj.members:
|
for m in group_obj.members:
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
def test_generic_reference_orphan_dbref(self):
|
def test_generic_reference_orphan_dbref(self):
|
||||||
"""Ensure that generic orphan DBRef items in ListFields are dereferenced.
|
"""Ensure that generic orphan DBRef items in ListFields are dereferenced.
|
||||||
@@ -604,18 +598,18 @@ class FieldTest(unittest.TestCase):
|
|||||||
# an orphan DBRef in the GenericReference ListField
|
# an orphan DBRef in the GenericReference ListField
|
||||||
UserA.objects[0].delete()
|
UserA.objects[0].delete()
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
self.assertTrue(group_obj._data["members"]._dereferenced)
|
assert group_obj._data["members"]._dereferenced
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
self.assertTrue(group_obj._data["members"]._dereferenced)
|
assert group_obj._data["members"]._dereferenced
|
||||||
|
|
||||||
UserA.drop_collection()
|
UserA.drop_collection()
|
||||||
UserB.drop_collection()
|
UserB.drop_collection()
|
||||||
@@ -660,52 +654,52 @@ class FieldTest(unittest.TestCase):
|
|||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for m in group_obj.members:
|
for m in group_obj.members:
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
# Document select_related
|
# Document select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first().select_related()
|
group_obj = Group.objects.first().select_related()
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for m in group_obj.members:
|
for m in group_obj.members:
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
# Queryset select_related
|
# Queryset select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_objs = Group.objects.select_related()
|
group_objs = Group.objects.select_related()
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for group_obj in group_objs:
|
for group_obj in group_objs:
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for m in group_obj.members:
|
for m in group_obj.members:
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
UserA.drop_collection()
|
UserA.drop_collection()
|
||||||
UserB.drop_collection()
|
UserB.drop_collection()
|
||||||
@@ -735,43 +729,43 @@ class FieldTest(unittest.TestCase):
|
|||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIsInstance(m, User)
|
assert isinstance(m, User)
|
||||||
|
|
||||||
# Document select_related
|
# Document select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first().select_related()
|
group_obj = Group.objects.first().select_related()
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIsInstance(m, User)
|
assert isinstance(m, User)
|
||||||
|
|
||||||
# Queryset select_related
|
# Queryset select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_objs = Group.objects.select_related()
|
group_objs = Group.objects.select_related()
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
for group_obj in group_objs:
|
for group_obj in group_objs:
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIsInstance(m, User)
|
assert isinstance(m, User)
|
||||||
|
|
||||||
User.drop_collection()
|
User.drop_collection()
|
||||||
Group.drop_collection()
|
Group.drop_collection()
|
||||||
@@ -813,65 +807,65 @@ class FieldTest(unittest.TestCase):
|
|||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
# Document select_related
|
# Document select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first().select_related()
|
group_obj = Group.objects.first().select_related()
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
# Queryset select_related
|
# Queryset select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_objs = Group.objects.select_related()
|
group_objs = Group.objects.select_related()
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for group_obj in group_objs:
|
for group_obj in group_objs:
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
Group.objects.delete()
|
Group.objects.delete()
|
||||||
Group().save()
|
Group().save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
self.assertEqual(group_obj.members, {})
|
assert group_obj.members == {}
|
||||||
|
|
||||||
UserA.drop_collection()
|
UserA.drop_collection()
|
||||||
UserB.drop_collection()
|
UserB.drop_collection()
|
||||||
@@ -903,52 +897,52 @@ class FieldTest(unittest.TestCase):
|
|||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIsInstance(m, UserA)
|
assert isinstance(m, UserA)
|
||||||
|
|
||||||
# Document select_related
|
# Document select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first().select_related()
|
group_obj = Group.objects.first().select_related()
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIsInstance(m, UserA)
|
assert isinstance(m, UserA)
|
||||||
|
|
||||||
# Queryset select_related
|
# Queryset select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_objs = Group.objects.select_related()
|
group_objs = Group.objects.select_related()
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
for group_obj in group_objs:
|
for group_obj in group_objs:
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIsInstance(m, UserA)
|
assert isinstance(m, UserA)
|
||||||
|
|
||||||
UserA.drop_collection()
|
UserA.drop_collection()
|
||||||
Group.drop_collection()
|
Group.drop_collection()
|
||||||
@@ -990,64 +984,64 @@ class FieldTest(unittest.TestCase):
|
|||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
# Document select_related
|
# Document select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first().select_related()
|
group_obj = Group.objects.first().select_related()
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
# Queryset select_related
|
# Queryset select_related
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_objs = Group.objects.select_related()
|
group_objs = Group.objects.select_related()
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for group_obj in group_objs:
|
for group_obj in group_objs:
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 4)
|
assert q == 4
|
||||||
|
|
||||||
for k, m in iteritems(group_obj.members):
|
for k, m in group_obj.members.items():
|
||||||
self.assertIn("User", m.__class__.__name__)
|
assert "User" in m.__class__.__name__
|
||||||
|
|
||||||
Group.objects.delete()
|
Group.objects.delete()
|
||||||
Group().save()
|
Group().save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
[m for m in group_obj.members]
|
[m for m in group_obj.members]
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
UserA.drop_collection()
|
UserA.drop_collection()
|
||||||
UserB.drop_collection()
|
UserB.drop_collection()
|
||||||
@@ -1075,8 +1069,8 @@ class FieldTest(unittest.TestCase):
|
|||||||
root.save()
|
root.save()
|
||||||
|
|
||||||
root = root.reload()
|
root = root.reload()
|
||||||
self.assertEqual(root.children, [company])
|
assert root.children == [company]
|
||||||
self.assertEqual(company.parents, [root])
|
assert company.parents == [root]
|
||||||
|
|
||||||
def test_dict_in_dbref_instance(self):
|
def test_dict_in_dbref_instance(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
@@ -1102,8 +1096,8 @@ class FieldTest(unittest.TestCase):
|
|||||||
room_101.save()
|
room_101.save()
|
||||||
|
|
||||||
room = Room.objects.first().select_related()
|
room = Room.objects.first().select_related()
|
||||||
self.assertEqual(room.staffs_with_position[0]["staff"], sarah)
|
assert room.staffs_with_position[0]["staff"] == sarah
|
||||||
self.assertEqual(room.staffs_with_position[1]["staff"], bob)
|
assert room.staffs_with_position[1]["staff"] == bob
|
||||||
|
|
||||||
def test_document_reload_no_inheritance(self):
|
def test_document_reload_no_inheritance(self):
|
||||||
class Foo(Document):
|
class Foo(Document):
|
||||||
@@ -1133,8 +1127,8 @@ class FieldTest(unittest.TestCase):
|
|||||||
foo.save()
|
foo.save()
|
||||||
foo.reload()
|
foo.reload()
|
||||||
|
|
||||||
self.assertEqual(type(foo.bar), Bar)
|
assert type(foo.bar) == Bar
|
||||||
self.assertEqual(type(foo.baz), Baz)
|
assert type(foo.baz) == Baz
|
||||||
|
|
||||||
def test_document_reload_reference_integrity(self):
|
def test_document_reload_reference_integrity(self):
|
||||||
"""
|
"""
|
||||||
@@ -1166,13 +1160,13 @@ class FieldTest(unittest.TestCase):
|
|||||||
concurrent_change_user = User.objects.get(id=1)
|
concurrent_change_user = User.objects.get(id=1)
|
||||||
concurrent_change_user.name = "new-name"
|
concurrent_change_user.name = "new-name"
|
||||||
concurrent_change_user.save()
|
concurrent_change_user.save()
|
||||||
self.assertNotEqual(user.name, "new-name")
|
assert user.name != "new-name"
|
||||||
|
|
||||||
msg = Message.objects.get(id=1)
|
msg = Message.objects.get(id=1)
|
||||||
msg.reload()
|
msg.reload()
|
||||||
self.assertEqual(msg.topic, topic)
|
assert msg.topic == topic
|
||||||
self.assertEqual(msg.author, user)
|
assert msg.author == user
|
||||||
self.assertEqual(msg.author.name, "new-name")
|
assert msg.author.name == "new-name"
|
||||||
|
|
||||||
def test_list_lookup_not_checked_in_map(self):
|
def test_list_lookup_not_checked_in_map(self):
|
||||||
"""Ensure we dereference list data correctly
|
"""Ensure we dereference list data correctly
|
||||||
@@ -1194,8 +1188,8 @@ class FieldTest(unittest.TestCase):
|
|||||||
Message(id=1, comments=[c1, c2]).save()
|
Message(id=1, comments=[c1, c2]).save()
|
||||||
|
|
||||||
msg = Message.objects.get(id=1)
|
msg = Message.objects.get(id=1)
|
||||||
self.assertEqual(0, msg.comments[0].id)
|
assert 0 == msg.comments[0].id
|
||||||
self.assertEqual(1, msg.comments[1].id)
|
assert 1 == msg.comments[1].id
|
||||||
|
|
||||||
def test_list_item_dereference_dref_false_save_doesnt_cause_extra_queries(self):
|
def test_list_item_dereference_dref_false_save_doesnt_cause_extra_queries(self):
|
||||||
"""Ensure that DBRef items in ListFields are dereferenced.
|
"""Ensure that DBRef items in ListFields are dereferenced.
|
||||||
@@ -1217,15 +1211,15 @@ class FieldTest(unittest.TestCase):
|
|||||||
Group(name="Test", members=User.objects).save()
|
Group(name="Test", members=User.objects).save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
group_obj.name = "new test"
|
group_obj.name = "new test"
|
||||||
group_obj.save()
|
group_obj.save()
|
||||||
|
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
def test_list_item_dereference_dref_true_save_doesnt_cause_extra_queries(self):
|
def test_list_item_dereference_dref_true_save_doesnt_cause_extra_queries(self):
|
||||||
"""Ensure that DBRef items in ListFields are dereferenced.
|
"""Ensure that DBRef items in ListFields are dereferenced.
|
||||||
@@ -1247,15 +1241,15 @@ class FieldTest(unittest.TestCase):
|
|||||||
Group(name="Test", members=User.objects).save()
|
Group(name="Test", members=User.objects).save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
group_obj.name = "new test"
|
group_obj.name = "new test"
|
||||||
group_obj.save()
|
group_obj.save()
|
||||||
|
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
def test_generic_reference_save_doesnt_cause_extra_queries(self):
|
def test_generic_reference_save_doesnt_cause_extra_queries(self):
|
||||||
class UserA(Document):
|
class UserA(Document):
|
||||||
@@ -1287,15 +1281,15 @@ class FieldTest(unittest.TestCase):
|
|||||||
Group(name="test", members=members).save()
|
Group(name="test", members=members).save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
group_obj = Group.objects.first()
|
group_obj = Group.objects.first()
|
||||||
self.assertEqual(q, 1)
|
assert q == 1
|
||||||
|
|
||||||
group_obj.name = "new test"
|
group_obj.name = "new test"
|
||||||
group_obj.save()
|
group_obj.save()
|
||||||
|
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
def test_objectid_reference_across_databases(self):
|
def test_objectid_reference_across_databases(self):
|
||||||
# mongoenginetest - Is default connection alias from setUp()
|
# mongoenginetest - Is default connection alias from setUp()
|
||||||
@@ -1319,10 +1313,10 @@ class FieldTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Can't use query_counter across databases - so test the _data object
|
# Can't use query_counter across databases - so test the _data object
|
||||||
book = Book.objects.first()
|
book = Book.objects.first()
|
||||||
self.assertNotIsInstance(book._data["author"], User)
|
assert not isinstance(book._data["author"], User)
|
||||||
|
|
||||||
book.select_related()
|
book.select_related()
|
||||||
self.assertIsInstance(book._data["author"], User)
|
assert isinstance(book._data["author"], User)
|
||||||
|
|
||||||
def test_non_ascii_pk(self):
|
def test_non_ascii_pk(self):
|
||||||
"""
|
"""
|
||||||
@@ -1346,7 +1340,7 @@ class FieldTest(unittest.TestCase):
|
|||||||
BrandGroup(title="top_brands", brands=[brand1, brand2]).save()
|
BrandGroup(title="top_brands", brands=[brand1, brand2]).save()
|
||||||
brand_groups = BrandGroup.objects().all()
|
brand_groups = BrandGroup.objects().all()
|
||||||
|
|
||||||
self.assertEqual(2, len([brand for bg in brand_groups for brand in bg.brands]))
|
assert 2 == len([brand for bg in brand_groups for brand in bg.brands])
|
||||||
|
|
||||||
def test_dereferencing_embedded_listfield_referencefield(self):
|
def test_dereferencing_embedded_listfield_referencefield(self):
|
||||||
class Tag(Document):
|
class Tag(Document):
|
||||||
@@ -1370,7 +1364,7 @@ class FieldTest(unittest.TestCase):
|
|||||||
Page(tags=[tag], posts=[post]).save()
|
Page(tags=[tag], posts=[post]).save()
|
||||||
|
|
||||||
page = Page.objects.first()
|
page = Page.objects.first()
|
||||||
self.assertEqual(page.tags[0], page.posts[0].tags[0])
|
assert page.tags[0] == page.posts[0].tags[0]
|
||||||
|
|
||||||
def test_select_related_follows_embedded_referencefields(self):
|
def test_select_related_follows_embedded_referencefields(self):
|
||||||
class Song(Document):
|
class Song(Document):
|
||||||
@@ -1390,12 +1384,12 @@ class FieldTest(unittest.TestCase):
|
|||||||
playlist = Playlist.objects.create(items=items)
|
playlist = Playlist.objects.create(items=items)
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(q, 0)
|
assert q == 0
|
||||||
|
|
||||||
playlist = Playlist.objects.first().select_related()
|
playlist = Playlist.objects.first().select_related()
|
||||||
songs = [item.song for item in playlist.items]
|
songs = [item.song for item in playlist.items]
|
||||||
|
|
||||||
self.assertEqual(q, 2)
|
assert q == 2
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from pymongo import ReadPreference
|
from pymongo import MongoClient, ReadPreference
|
||||||
from pymongo import MongoClient
|
|
||||||
|
|
||||||
import mongoengine
|
import mongoengine
|
||||||
from mongoengine.connection import ConnectionFailure
|
from mongoengine.connection import ConnectionFailure
|
||||||
@@ -25,21 +24,20 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
def test_replicaset_uri_passes_read_preference(self):
|
def test_replicaset_uri_passes_read_preference(self):
|
||||||
"""Requires a replica set called "rs" on port 27017
|
"""Requires a replica set called "rs" on port 27017
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = mongoengine.connect(
|
conn = mongoengine.connect(
|
||||||
db="mongoenginetest",
|
db="mongoenginetest",
|
||||||
host="mongodb://localhost/mongoenginetest?replicaSet=rs",
|
host="mongodb://localhost/mongoenginetest?replicaSet=rs",
|
||||||
read_preference=READ_PREF,
|
read_preference=READ_PREF,
|
||||||
)
|
)
|
||||||
except ConnectionFailure as e:
|
except ConnectionFailure:
|
||||||
return
|
return
|
||||||
|
|
||||||
if not isinstance(conn, CONN_CLASS):
|
if not isinstance(conn, CONN_CLASS):
|
||||||
# really???
|
# really???
|
||||||
return
|
return
|
||||||
|
|
||||||
self.assertEqual(conn.read_preference, READ_PREF)
|
assert conn.read_preference == READ_PREF
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from mongoengine import signals
|
|||||||
signal_output = []
|
signal_output = []
|
||||||
|
|
||||||
|
|
||||||
class SignalTests(unittest.TestCase):
|
class TestSignal(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
Testing signals before/after saving and deleting.
|
Testing signals before/after saving and deleting.
|
||||||
"""
|
"""
|
||||||
@@ -58,7 +58,9 @@ class SignalTests(unittest.TestCase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def post_save(cls, sender, document, **kwargs):
|
def post_save(cls, sender, document, **kwargs):
|
||||||
dirty_keys = document._delta()[0].keys() + document._delta()[1].keys()
|
dirty_keys = list(document._delta()[0].keys()) + list(
|
||||||
|
document._delta()[1].keys()
|
||||||
|
)
|
||||||
signal_output.append("post_save signal, %s" % document)
|
signal_output.append("post_save signal, %s" % document)
|
||||||
signal_output.append("post_save dirty keys, %s" % dirty_keys)
|
signal_output.append("post_save dirty keys, %s" % dirty_keys)
|
||||||
if kwargs.pop("created", False):
|
if kwargs.pop("created", False):
|
||||||
@@ -245,7 +247,7 @@ class SignalTests(unittest.TestCase):
|
|||||||
# Note that there is a chance that the following assert fails in case
|
# Note that there is a chance that the following assert fails in case
|
||||||
# some receivers (eventually created in other tests)
|
# some receivers (eventually created in other tests)
|
||||||
# gets garbage collected (https://pythonhosted.org/blinker/#blinker.base.Signal.connect)
|
# gets garbage collected (https://pythonhosted.org/blinker/#blinker.base.Signal.connect)
|
||||||
self.assertEqual(self.pre_signals, post_signals)
|
assert self.pre_signals == post_signals
|
||||||
|
|
||||||
def test_model_signals(self):
|
def test_model_signals(self):
|
||||||
""" Model saves should throw some signals. """
|
""" Model saves should throw some signals. """
|
||||||
@@ -265,99 +267,78 @@ class SignalTests(unittest.TestCase):
|
|||||||
a = self.Author(name="Bill Shakespeare")
|
a = self.Author(name="Bill Shakespeare")
|
||||||
a.save()
|
a.save()
|
||||||
self.get_signal_output(lambda: None) # eliminate signal output
|
self.get_signal_output(lambda: None) # eliminate signal output
|
||||||
a1 = self.Author.objects(name="Bill Shakespeare")[0]
|
_ = self.Author.objects(name="Bill Shakespeare")[0]
|
||||||
|
|
||||||
self.assertEqual(
|
assert self.get_signal_output(create_author) == [
|
||||||
self.get_signal_output(create_author),
|
"pre_init signal, Author",
|
||||||
[
|
{"name": "Bill Shakespeare"},
|
||||||
"pre_init signal, Author",
|
"post_init signal, Bill Shakespeare, document._created = True",
|
||||||
{"name": "Bill Shakespeare"},
|
]
|
||||||
"post_init signal, Bill Shakespeare, document._created = True",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
a1 = self.Author(name="Bill Shakespeare")
|
a1 = self.Author(name="Bill Shakespeare")
|
||||||
self.assertEqual(
|
assert self.get_signal_output(a1.save) == [
|
||||||
self.get_signal_output(a1.save),
|
"pre_save signal, Bill Shakespeare",
|
||||||
[
|
{},
|
||||||
"pre_save signal, Bill Shakespeare",
|
"pre_save_post_validation signal, Bill Shakespeare",
|
||||||
{},
|
"Is created",
|
||||||
"pre_save_post_validation signal, Bill Shakespeare",
|
{},
|
||||||
"Is created",
|
"post_save signal, Bill Shakespeare",
|
||||||
{},
|
"post_save dirty keys, ['name']",
|
||||||
"post_save signal, Bill Shakespeare",
|
"Is created",
|
||||||
"post_save dirty keys, ['name']",
|
{},
|
||||||
"Is created",
|
]
|
||||||
{},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
a1.reload()
|
a1.reload()
|
||||||
a1.name = "William Shakespeare"
|
a1.name = "William Shakespeare"
|
||||||
self.assertEqual(
|
assert self.get_signal_output(a1.save) == [
|
||||||
self.get_signal_output(a1.save),
|
"pre_save signal, William Shakespeare",
|
||||||
[
|
{},
|
||||||
"pre_save signal, William Shakespeare",
|
"pre_save_post_validation signal, William Shakespeare",
|
||||||
{},
|
"Is updated",
|
||||||
"pre_save_post_validation signal, William Shakespeare",
|
{},
|
||||||
"Is updated",
|
"post_save signal, William Shakespeare",
|
||||||
{},
|
"post_save dirty keys, ['name']",
|
||||||
"post_save signal, William Shakespeare",
|
"Is updated",
|
||||||
"post_save dirty keys, ['name']",
|
{},
|
||||||
"Is updated",
|
]
|
||||||
{},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(
|
assert self.get_signal_output(a1.delete) == [
|
||||||
self.get_signal_output(a1.delete),
|
"pre_delete signal, William Shakespeare",
|
||||||
[
|
{},
|
||||||
"pre_delete signal, William Shakespeare",
|
"post_delete signal, William Shakespeare",
|
||||||
{},
|
{},
|
||||||
"post_delete signal, William Shakespeare",
|
]
|
||||||
{},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(
|
assert self.get_signal_output(load_existing_author) == [
|
||||||
self.get_signal_output(load_existing_author),
|
"pre_init signal, Author",
|
||||||
[
|
{"id": 2, "name": "Bill Shakespeare"},
|
||||||
"pre_init signal, Author",
|
"post_init signal, Bill Shakespeare, document._created = False",
|
||||||
{"id": 2, "name": "Bill Shakespeare"},
|
]
|
||||||
"post_init signal, Bill Shakespeare, document._created = False",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(
|
assert self.get_signal_output(bulk_create_author_with_load) == [
|
||||||
self.get_signal_output(bulk_create_author_with_load),
|
"pre_init signal, Author",
|
||||||
[
|
{"name": "Bill Shakespeare"},
|
||||||
"pre_init signal, Author",
|
"post_init signal, Bill Shakespeare, document._created = True",
|
||||||
{"name": "Bill Shakespeare"},
|
"pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
||||||
"post_init signal, Bill Shakespeare, document._created = True",
|
{},
|
||||||
"pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
"pre_init signal, Author",
|
||||||
{},
|
{"id": 3, "name": "Bill Shakespeare"},
|
||||||
"pre_init signal, Author",
|
"post_init signal, Bill Shakespeare, document._created = False",
|
||||||
{"id": 3, "name": "Bill Shakespeare"},
|
"post_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
||||||
"post_init signal, Bill Shakespeare, document._created = False",
|
"Is loaded",
|
||||||
"post_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
{},
|
||||||
"Is loaded",
|
]
|
||||||
{},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(
|
assert self.get_signal_output(bulk_create_author_without_load) == [
|
||||||
self.get_signal_output(bulk_create_author_without_load),
|
"pre_init signal, Author",
|
||||||
[
|
{"name": "Bill Shakespeare"},
|
||||||
"pre_init signal, Author",
|
"post_init signal, Bill Shakespeare, document._created = True",
|
||||||
{"name": "Bill Shakespeare"},
|
"pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
||||||
"post_init signal, Bill Shakespeare, document._created = True",
|
{},
|
||||||
"pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
"post_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
||||||
{},
|
"Not loaded",
|
||||||
"post_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
{},
|
||||||
"Not loaded",
|
]
|
||||||
{},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_signal_kwargs(self):
|
def test_signal_kwargs(self):
|
||||||
""" Make sure signal_kwargs is passed to signals calls. """
|
""" Make sure signal_kwargs is passed to signals calls. """
|
||||||
@@ -367,83 +348,74 @@ class SignalTests(unittest.TestCase):
|
|||||||
a.save(signal_kwargs={"live": True, "die": False})
|
a.save(signal_kwargs={"live": True, "die": False})
|
||||||
a.delete(signal_kwargs={"live": False, "die": True})
|
a.delete(signal_kwargs={"live": False, "die": True})
|
||||||
|
|
||||||
self.assertEqual(
|
assert self.get_signal_output(live_and_let_die) == [
|
||||||
self.get_signal_output(live_and_let_die),
|
"pre_init signal, Author",
|
||||||
[
|
{"name": "Bill Shakespeare"},
|
||||||
"pre_init signal, Author",
|
"post_init signal, Bill Shakespeare, document._created = True",
|
||||||
{"name": "Bill Shakespeare"},
|
"pre_save signal, Bill Shakespeare",
|
||||||
"post_init signal, Bill Shakespeare, document._created = True",
|
{"die": False, "live": True},
|
||||||
"pre_save signal, Bill Shakespeare",
|
"pre_save_post_validation signal, Bill Shakespeare",
|
||||||
{"die": False, "live": True},
|
"Is created",
|
||||||
"pre_save_post_validation signal, Bill Shakespeare",
|
{"die": False, "live": True},
|
||||||
"Is created",
|
"post_save signal, Bill Shakespeare",
|
||||||
{"die": False, "live": True},
|
"post_save dirty keys, ['name']",
|
||||||
"post_save signal, Bill Shakespeare",
|
"Is created",
|
||||||
"post_save dirty keys, ['name']",
|
{"die": False, "live": True},
|
||||||
"Is created",
|
"pre_delete signal, Bill Shakespeare",
|
||||||
{"die": False, "live": True},
|
{"die": True, "live": False},
|
||||||
"pre_delete signal, Bill Shakespeare",
|
"post_delete signal, Bill Shakespeare",
|
||||||
{"die": True, "live": False},
|
{"die": True, "live": False},
|
||||||
"post_delete signal, Bill Shakespeare",
|
]
|
||||||
{"die": True, "live": False},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def bulk_create_author():
|
def bulk_create_author():
|
||||||
a1 = self.Author(name="Bill Shakespeare")
|
a1 = self.Author(name="Bill Shakespeare")
|
||||||
self.Author.objects.insert([a1], signal_kwargs={"key": True})
|
self.Author.objects.insert([a1], signal_kwargs={"key": True})
|
||||||
|
|
||||||
self.assertEqual(
|
assert self.get_signal_output(bulk_create_author) == [
|
||||||
self.get_signal_output(bulk_create_author),
|
"pre_init signal, Author",
|
||||||
[
|
{"name": "Bill Shakespeare"},
|
||||||
"pre_init signal, Author",
|
"post_init signal, Bill Shakespeare, document._created = True",
|
||||||
{"name": "Bill Shakespeare"},
|
"pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
||||||
"post_init signal, Bill Shakespeare, document._created = True",
|
{"key": True},
|
||||||
"pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
"pre_init signal, Author",
|
||||||
{"key": True},
|
{"id": 2, "name": "Bill Shakespeare"},
|
||||||
"pre_init signal, Author",
|
"post_init signal, Bill Shakespeare, document._created = False",
|
||||||
{"id": 2, "name": "Bill Shakespeare"},
|
"post_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
||||||
"post_init signal, Bill Shakespeare, document._created = False",
|
"Is loaded",
|
||||||
"post_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
{"key": True},
|
||||||
"Is loaded",
|
]
|
||||||
{"key": True},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_queryset_delete_signals(self):
|
def test_queryset_delete_signals(self):
|
||||||
""" Queryset delete should throw some signals. """
|
""" Queryset delete should throw some signals. """
|
||||||
|
|
||||||
self.Another(name="Bill Shakespeare").save()
|
self.Another(name="Bill Shakespeare").save()
|
||||||
self.assertEqual(
|
assert self.get_signal_output(self.Another.objects.delete) == [
|
||||||
self.get_signal_output(self.Another.objects.delete),
|
"pre_delete signal, Bill Shakespeare",
|
||||||
[
|
{},
|
||||||
"pre_delete signal, Bill Shakespeare",
|
"post_delete signal, Bill Shakespeare",
|
||||||
{},
|
{},
|
||||||
"post_delete signal, Bill Shakespeare",
|
]
|
||||||
{},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_signals_with_explicit_doc_ids(self):
|
def test_signals_with_explicit_doc_ids(self):
|
||||||
""" Model saves must have a created flag the first time."""
|
""" Model saves must have a created flag the first time."""
|
||||||
ei = self.ExplicitId(id=123)
|
ei = self.ExplicitId(id=123)
|
||||||
# post save must received the created flag, even if there's already
|
# post save must received the created flag, even if there's already
|
||||||
# an object id present
|
# an object id present
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
|
assert self.get_signal_output(ei.save) == ["Is created"]
|
||||||
# second time, it must be an update
|
# second time, it must be an update
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is updated"])
|
assert self.get_signal_output(ei.save) == ["Is updated"]
|
||||||
|
|
||||||
def test_signals_with_switch_collection(self):
|
def test_signals_with_switch_collection(self):
|
||||||
ei = self.ExplicitId(id=123)
|
ei = self.ExplicitId(id=123)
|
||||||
ei.switch_collection("explicit__1")
|
ei.switch_collection("explicit__1")
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
|
assert self.get_signal_output(ei.save) == ["Is created"]
|
||||||
ei.switch_collection("explicit__1")
|
ei.switch_collection("explicit__1")
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is updated"])
|
assert self.get_signal_output(ei.save) == ["Is updated"]
|
||||||
|
|
||||||
ei.switch_collection("explicit__1", keep_created=False)
|
ei.switch_collection("explicit__1", keep_created=False)
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
|
assert self.get_signal_output(ei.save) == ["Is created"]
|
||||||
ei.switch_collection("explicit__1", keep_created=False)
|
ei.switch_collection("explicit__1", keep_created=False)
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
|
assert self.get_signal_output(ei.save) == ["Is created"]
|
||||||
|
|
||||||
def test_signals_with_switch_db(self):
|
def test_signals_with_switch_db(self):
|
||||||
connect("mongoenginetest")
|
connect("mongoenginetest")
|
||||||
@@ -451,14 +423,14 @@ class SignalTests(unittest.TestCase):
|
|||||||
|
|
||||||
ei = self.ExplicitId(id=123)
|
ei = self.ExplicitId(id=123)
|
||||||
ei.switch_db("testdb-1")
|
ei.switch_db("testdb-1")
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
|
assert self.get_signal_output(ei.save) == ["Is created"]
|
||||||
ei.switch_db("testdb-1")
|
ei.switch_db("testdb-1")
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is updated"])
|
assert self.get_signal_output(ei.save) == ["Is updated"]
|
||||||
|
|
||||||
ei.switch_db("testdb-1", keep_created=False)
|
ei.switch_db("testdb-1", keep_created=False)
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
|
assert self.get_signal_output(ei.save) == ["Is created"]
|
||||||
ei.switch_db("testdb-1", keep_created=False)
|
ei.switch_db("testdb-1", keep_created=False)
|
||||||
self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
|
assert self.get_signal_output(ei.save) == ["Is created"]
|
||||||
|
|
||||||
def test_signals_bulk_insert(self):
|
def test_signals_bulk_insert(self):
|
||||||
def bulk_set_active_post():
|
def bulk_set_active_post():
|
||||||
@@ -470,16 +442,13 @@ class SignalTests(unittest.TestCase):
|
|||||||
self.Post.objects.insert(posts)
|
self.Post.objects.insert(posts)
|
||||||
|
|
||||||
results = self.get_signal_output(bulk_set_active_post)
|
results = self.get_signal_output(bulk_set_active_post)
|
||||||
self.assertEqual(
|
assert results == [
|
||||||
results,
|
"pre_bulk_insert signal, [(<Post: Post 1>, {'active': False}), (<Post: Post 2>, {'active': False}), (<Post: Post 3>, {'active': False})]",
|
||||||
[
|
{},
|
||||||
"pre_bulk_insert signal, [(<Post: Post 1>, {'active': False}), (<Post: Post 2>, {'active': False}), (<Post: Post 3>, {'active': False})]",
|
"post_bulk_insert signal, [(<Post: Post 1>, {'active': True}), (<Post: Post 2>, {'active': True}), (<Post: Post 3>, {'active': True})]",
|
||||||
{},
|
"Is loaded",
|
||||||
"post_bulk_insert signal, [(<Post: Post 1>, {'active': True}), (<Post: Post 2>, {'active': True}), (<Post: Post 3>, {'active': True})]",
|
{},
|
||||||
"Is loaded",
|
]
|
||||||
{},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user