Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86ad8d119d | ||
|
|
34d273015c | ||
|
|
7147043d63 | ||
|
|
b9b536133d | ||
|
|
f244207168 | ||
|
|
0620ac5641 | ||
|
|
3b9a167022 | ||
|
|
b479bb7c6b | ||
|
|
8ef771912d | ||
|
|
2d1c9afbb7 | ||
|
|
9ff5d8426c | ||
|
|
467e9c3ddf | ||
|
|
0d5e028c55 | ||
|
|
5858ea1bf0 | ||
|
|
1f220b4eaf | ||
|
|
97c99ca40d | ||
|
|
80a3b1c88c | ||
|
|
68447af127 | ||
|
|
d033e3b133 | ||
|
|
4428842e77 | ||
|
|
f38cc6edd3 | ||
|
|
aeb4f8f4da | ||
|
|
1b7c2085c9 | ||
|
|
48b979599f | ||
|
|
af3d3b7ee6 | ||
|
|
56fe126f3a | ||
|
|
04905d4b37 | ||
|
|
460df112f4 | ||
|
|
772096ec55 | ||
|
|
98d64f41c6 | ||
|
|
9a3bca8ab6 | ||
|
|
5781753cc8 | ||
|
|
fd3699a519 | ||
|
|
4f6a24411d | ||
|
|
de3888a48b | ||
|
|
700fe80a00 | ||
|
|
49e33b978d | ||
|
|
81197d6061 | ||
|
|
aa368be4d3 | ||
|
|
0f1fce4a7b | ||
|
|
cc591a634a | ||
|
|
6e332e782b | ||
|
|
0e9920b190 | ||
|
|
fd35df07c4 | ||
|
|
8f3d21c312 | ||
|
|
7b772e3a4a | ||
|
|
59438a4768 | ||
|
|
fe9f7f1f80 | ||
|
|
6b5231265c | ||
|
|
0014346de1 |
143
.github/workflows/github-actions.yml
vendored
Normal file
143
.github/workflows/github-actions.yml
vendored
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
name: MongoengineCI
|
||||||
|
on:
|
||||||
|
# All PR
|
||||||
|
pull_request:
|
||||||
|
# master branch merge
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
# release tags
|
||||||
|
create:
|
||||||
|
tags:
|
||||||
|
- 'v[0-9]+\.[0-9]+\.[0-9]+*'
|
||||||
|
env:
|
||||||
|
MONGODB_3_6: 3.6.14
|
||||||
|
MONGODB_4_0: 4.0.23
|
||||||
|
MONGODB_4_2: 4.2
|
||||||
|
MONGODB_4_4: 4.4
|
||||||
|
|
||||||
|
PYMONGO_3_4: 3.4
|
||||||
|
PYMONGO_3_6: 3.6
|
||||||
|
PYMONGO_3_9: 3.9
|
||||||
|
PYMONGO_3_11: 3.11
|
||||||
|
|
||||||
|
MAIN_PYTHON_VERSION: 3.7
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linting:
|
||||||
|
# Run pre-commit (https://pre-commit.com/)
|
||||||
|
# which runs pre-configured linter & autoformatter
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python 3.7
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
- run: bash .github/workflows/install_ci_python_dep.sh
|
||||||
|
- run: pre-commit run -a
|
||||||
|
|
||||||
|
test:
|
||||||
|
# Test suite run against recent python versions
|
||||||
|
# and against a few combination of MongoDB and pymongo
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
python-version: [3.6, 3.7, 3.8, 3.9, pypy3]
|
||||||
|
MONGODB: [$MONGODB_4_0]
|
||||||
|
PYMONGO: [$PYMONGO_3_11]
|
||||||
|
include:
|
||||||
|
- python-version: 3.7
|
||||||
|
MONGODB: $MONGODB_3_6
|
||||||
|
PYMONGO: $PYMONGO_3_9
|
||||||
|
- python-version: 3.7
|
||||||
|
MONGODB: $MONGODB_4_2
|
||||||
|
PYMONGO: $PYMONGO_3_6
|
||||||
|
- python-version: 3.7
|
||||||
|
MONGODB: $MONGODB_4_4
|
||||||
|
PYMONGO: $PYMONGO_3_11
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: install mongo and ci dependencies
|
||||||
|
run: |
|
||||||
|
bash .github/workflows/install_mongo.sh ${{ matrix.MONGODB }}
|
||||||
|
bash .github/workflows/install_ci_python_dep.sh
|
||||||
|
bash .github/workflows/start_mongo.sh ${{ matrix.MONGODB }}
|
||||||
|
- name: tox dry-run (to pre-install venv)
|
||||||
|
run: tox -e $(echo py${{ matrix.python-version }}-mg${{ matrix.PYMONGO }} | tr -d . | sed -e 's/pypypy/pypy/') -- -a "-k=test_ci_placeholder"
|
||||||
|
- name: Run test suite
|
||||||
|
run: tox -e $(echo py${{ matrix.python-version }}-mg${{ matrix.PYMONGO }} | tr -d . | sed -e 's/pypypy/pypy/') -- -a "--cov=mongoengine"
|
||||||
|
- name: Send coverage to Coveralls
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
COVERALLS_SERVICE_NAME: github
|
||||||
|
if: ${{ matrix.python-version == env.MAIN_PYTHON_VERSION }}
|
||||||
|
run: coveralls
|
||||||
|
|
||||||
|
build_doc_dryrun:
|
||||||
|
# ensures that readthedocs can be built continuously
|
||||||
|
# to avoid that it breaks when new releases are being created
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
- name: install python dep
|
||||||
|
run: |
|
||||||
|
pip install -e .
|
||||||
|
pip install -r docs/requirements.txt
|
||||||
|
- name: build doc
|
||||||
|
run: |
|
||||||
|
cd docs
|
||||||
|
make html-readthedocs
|
||||||
|
|
||||||
|
build-n-publish-dummy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [linting, test, build_doc_dryrun]
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Set up Python 3.7
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
- name: build dummy wheel for test-pypi
|
||||||
|
run: |
|
||||||
|
pip install wheel
|
||||||
|
python setup.py egg_info -b ".dev`date '+%Y%m%d%H%M%S'`" build sdist bdist_wheel
|
||||||
|
# - name: publish test-pypi
|
||||||
|
# # Although working and recommended, test-pypi has a limit
|
||||||
|
# # in the size of projects so it's better to avoid publishing
|
||||||
|
# # until there is a way to garbage collect these dummy releases
|
||||||
|
# uses: pypa/gh-action-pypi-publish@master
|
||||||
|
# with:
|
||||||
|
# password: ${{ secrets.test_pypi_token }}
|
||||||
|
# repository_url: https://test.pypi.org/legacy/
|
||||||
|
|
||||||
|
build-n-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [linting, test, build_doc_dryrun, build-n-publish-dummy]
|
||||||
|
if: github.event_name == 'create' && startsWith(github.ref, 'refs/tags/v')
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Set up Python 3.7
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
# todo separate build from publish
|
||||||
|
# https://stackoverflow.com/questions/59349905/which-properties-does-github-event-in-a-github-workflow-have
|
||||||
|
- name: build dummy wheel for test-pypi
|
||||||
|
run: |
|
||||||
|
pip install wheel
|
||||||
|
python setup.py sdist bdist_wheel
|
||||||
|
- name: publish pypi
|
||||||
|
uses: pypa/gh-action-pypi-publish@master
|
||||||
|
with:
|
||||||
|
password: ${{ secrets.pypi_token }}
|
||||||
5
.github/workflows/install_ci_python_dep.sh
vendored
Normal file
5
.github/workflows/install_ci_python_dep.sh
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
pip install --upgrade pip
|
||||||
|
pip install coveralls
|
||||||
|
pip install pre-commit
|
||||||
|
pip install tox
|
||||||
18
.github/workflows/install_mongo.sh
vendored
Normal file
18
.github/workflows/install_mongo.sh
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
MONGODB=$1
|
||||||
|
|
||||||
|
# Mongo > 4.0 follows different name convention for download links
|
||||||
|
mongo_build=mongodb-linux-x86_64-${MONGODB}
|
||||||
|
|
||||||
|
if [[ "$MONGODB" == *"4.2"* ]]; then
|
||||||
|
mongo_build=mongodb-linux-x86_64-ubuntu1804-v${MONGODB}-latest
|
||||||
|
elif [[ "$MONGODB" == *"4.4"* ]]; then
|
||||||
|
mongo_build=mongodb-linux-x86_64-ubuntu1804-v${MONGODB}-latest
|
||||||
|
fi
|
||||||
|
|
||||||
|
wget http://fastdl.mongodb.org/linux/$mongo_build.tgz
|
||||||
|
tar xzf $mongo_build.tgz
|
||||||
|
|
||||||
|
mongodb_dir=$(find ${PWD}/ -type d -name "mongodb-linux-x86_64*")
|
||||||
|
$mongodb_dir/bin/mongod --version
|
||||||
9
.github/workflows/start_mongo.sh
vendored
Normal file
9
.github/workflows/start_mongo.sh
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
MONGODB=$1
|
||||||
|
|
||||||
|
mongodb_dir=$(find ${PWD}/ -type d -name "mongodb-linux-x86_64*")
|
||||||
|
|
||||||
|
mkdir $mongodb_dir/data
|
||||||
|
$mongodb_dir/bin/mongod --dbpath $mongodb_dir/data --logpath $mongodb_dir/mongodb.log --fork
|
||||||
|
mongo --eval 'db.version();' # Make sure mongo is awake
|
||||||
107
.travis.yml
107
.travis.yml
@@ -1,107 +0,0 @@
|
|||||||
# For full coverage, we'd have to test all supported Python, MongoDB, and
|
|
||||||
# PyMongo combinations. However, that would result in an overly long build
|
|
||||||
# with a very large number of jobs, hence we only test a subset of all the
|
|
||||||
# combinations.
|
|
||||||
# * Python3.7, MongoDB v3.4 & the latest PyMongo v3.x is currently the "main" setup,
|
|
||||||
# Other combinations are tested. See below for the details or check the travis jobs
|
|
||||||
|
|
||||||
# We should periodically check MongoDB Server versions supported by MongoDB
|
|
||||||
# Inc., add newly released versions to the test matrix, and remove versions
|
|
||||||
# which have reached their End of Life. See:
|
|
||||||
# 1. https://www.mongodb.com/support-policy.
|
|
||||||
# 2. https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#python-driver-compatibility
|
|
||||||
#
|
|
||||||
# Reminder: Update README.rst if you change MongoDB versions we test.
|
|
||||||
|
|
||||||
language: python
|
|
||||||
dist: xenial
|
|
||||||
python:
|
|
||||||
- 3.6
|
|
||||||
- 3.7
|
|
||||||
- 3.8
|
|
||||||
- 3.9
|
|
||||||
- pypy3
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- MONGODB_3_4=3.4.19
|
|
||||||
- MONGODB_3_6=3.6.13
|
|
||||||
- MONGODB_4_0=4.0.13
|
|
||||||
|
|
||||||
- PYMONGO_3_4=3.4
|
|
||||||
- PYMONGO_3_6=3.6
|
|
||||||
- PYMONGO_3_9=3.9
|
|
||||||
- PYMONGO_3_11=3.11
|
|
||||||
|
|
||||||
- MAIN_PYTHON_VERSION=3.7
|
|
||||||
matrix:
|
|
||||||
- MONGODB=${MONGODB_3_4} PYMONGO=${PYMONGO_3_11}
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
# Finish the build as soon as one job fails
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
include:
|
|
||||||
- python: 3.7
|
|
||||||
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_11}
|
|
||||||
- python: 3.8
|
|
||||||
env: MONGODB=${MONGODB_4_0} PYMONGO=${PYMONGO_3_11}
|
|
||||||
|
|
||||||
install:
|
|
||||||
# Install Mongo
|
|
||||||
- wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-${MONGODB}.tgz
|
|
||||||
- tar xzf mongodb-linux-x86_64-${MONGODB}.tgz
|
|
||||||
- ${PWD}/mongodb-linux-x86_64-${MONGODB}/bin/mongod --version
|
|
||||||
# Install Python dependencies.
|
|
||||||
- pip install --upgrade pip
|
|
||||||
- pip install coveralls
|
|
||||||
- pip install pre-commit
|
|
||||||
- pip install tox
|
|
||||||
# tox dryrun to setup the tox venv (we run a mock test).
|
|
||||||
- tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -a "-k=test_ci_placeholder"
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- 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
|
|
||||||
# Run pre-commit hooks (black, flake8, etc) on entire codebase
|
|
||||||
- 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
|
|
||||||
|
|
||||||
script:
|
|
||||||
- tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -a "--cov=mongoengine"
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == $MAIN_PYTHON_VERSION ]]; then coveralls --verbose; else echo "coveralls only sent for py37"; fi
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
irc: irc.freenode.org#mongoengine
|
|
||||||
|
|
||||||
# Only run builds on the master branch and GitHub releases (tagged as vX.Y.Z)
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- /^v.*$/
|
|
||||||
|
|
||||||
# Whenever a new release is created via GitHub, publish it on PyPI.
|
|
||||||
deploy:
|
|
||||||
provider: pypi
|
|
||||||
user: the_drow
|
|
||||||
password:
|
|
||||||
secure: QMyatmWBnC6ZN3XLW2+fTBDU4LQcp1m/LjR2/0uamyeUzWKdlOoh/Wx5elOgLwt/8N9ppdPeG83ose1jOz69l5G0MUMjv8n/RIcMFSpCT59tGYqn3kh55b0cIZXFT9ar+5cxlif6a5rS72IHm5li7QQyxexJIII6Uxp0kpvUmek=
|
|
||||||
|
|
||||||
# Create a source distribution and a pure python wheel for faster installs.
|
|
||||||
distributions: "sdist bdist_wheel"
|
|
||||||
|
|
||||||
# Only deploy on tagged commits (aka GitHub releases) and only for the parent
|
|
||||||
# repo's builds running Python v3.7 along with PyMongo v3.x and MongoDB v3.4.
|
|
||||||
# We run Travis against many different Python, PyMongo, and MongoDB versions
|
|
||||||
# and we don't want the deploy to occur multiple times).
|
|
||||||
on:
|
|
||||||
tags: true
|
|
||||||
repo: MongoEngine/mongoengine
|
|
||||||
condition: ($PYMONGO = ${PYMONGO_3_11}) && ($MONGODB = ${MONGODB_3_4})
|
|
||||||
python: 3.7
|
|
||||||
108
.travis_.yml
Normal file
108
.travis_.yml
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
## For full coverage, we'd have to test all supported Python, MongoDB, and
|
||||||
|
## PyMongo combinations. However, that would result in an overly long build
|
||||||
|
## with a very large number of jobs, hence we only test a subset of all the
|
||||||
|
## combinations.
|
||||||
|
## * Python3.7, MongoDB v3.4 & the latest PyMongo v3.x is currently the "main" setup,
|
||||||
|
## Other combinations are tested. See below for the details or check the travis jobs
|
||||||
|
#
|
||||||
|
## We should periodically check MongoDB Server versions supported by MongoDB
|
||||||
|
## Inc., add newly released versions to the test matrix, and remove versions
|
||||||
|
## which have reached their End of Life. See:
|
||||||
|
## 1. https://www.mongodb.com/support-policy.
|
||||||
|
## 2. https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#python-driver-compatibility
|
||||||
|
##
|
||||||
|
## Reminder: Update README.rst if you change MongoDB versions we test.
|
||||||
|
#
|
||||||
|
#language: python
|
||||||
|
#dist: xenial
|
||||||
|
#python:
|
||||||
|
# - 3.6
|
||||||
|
# - 3.7
|
||||||
|
# - 3.8
|
||||||
|
# - 3.9
|
||||||
|
# - pypy3
|
||||||
|
#
|
||||||
|
#env:
|
||||||
|
# global:
|
||||||
|
# - MONGODB_3_4=3.4.19
|
||||||
|
# - MONGODB_3_6=3.6.13
|
||||||
|
# - MONGODB_4_0=4.0.13
|
||||||
|
#
|
||||||
|
# - PYMONGO_3_4=3.4
|
||||||
|
# - PYMONGO_3_6=3.6
|
||||||
|
# - PYMONGO_3_9=3.9
|
||||||
|
# - PYMONGO_3_11=3.11
|
||||||
|
#
|
||||||
|
# - MAIN_PYTHON_VERSION=3.7
|
||||||
|
# matrix:
|
||||||
|
# - MONGODB=${MONGODB_3_4} PYMONGO=${PYMONGO_3_11}
|
||||||
|
#
|
||||||
|
#matrix:
|
||||||
|
# # Finish the build as soon as one job fails
|
||||||
|
# fast_finish: true
|
||||||
|
#
|
||||||
|
# include:
|
||||||
|
# - python: 3.7
|
||||||
|
# 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_11}
|
||||||
|
# - python: 3.8
|
||||||
|
# env: MONGODB=${MONGODB_4_0} PYMONGO=${PYMONGO_3_11}
|
||||||
|
#
|
||||||
|
#install:
|
||||||
|
# # Install Mongo
|
||||||
|
# - wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-${MONGODB}.tgz
|
||||||
|
# - tar xzf mongodb-linux-x86_64-${MONGODB}.tgz
|
||||||
|
# - ${PWD}/mongodb-linux-x86_64-${MONGODB}/bin/mongod --version
|
||||||
|
# # Install Python dependencies.
|
||||||
|
# - pip install --upgrade pip
|
||||||
|
# - pip install coveralls
|
||||||
|
# - pip install pre-commit
|
||||||
|
# - pip install tox
|
||||||
|
# # tox dryrun to setup the tox venv (we run a mock test).
|
||||||
|
# - tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -a "-k=test_ci_placeholder"
|
||||||
|
#
|
||||||
|
#before_script:
|
||||||
|
# - 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
|
||||||
|
# # Run pre-commit hooks (black, flake8, etc) on entire codebase
|
||||||
|
# - 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
|
||||||
|
#
|
||||||
|
#script:
|
||||||
|
# - tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -a "--cov=mongoengine"
|
||||||
|
#
|
||||||
|
#after_success:
|
||||||
|
# - if [[ $TRAVIS_PYTHON_VERSION == $MAIN_PYTHON_VERSION ]]; then coveralls --verbose; else echo "coveralls only sent for py37"; fi
|
||||||
|
#
|
||||||
|
#notifications:
|
||||||
|
# irc: irc.freenode.org#mongoengine
|
||||||
|
#
|
||||||
|
## Only run builds on the master branch and GitHub releases (tagged as vX.Y.Z)
|
||||||
|
#branches:
|
||||||
|
# # Only run builds on the master branch and GitHub releases (tagged as vX.Y.Z)
|
||||||
|
# only:
|
||||||
|
# - master
|
||||||
|
# - /^v.*$/
|
||||||
|
#
|
||||||
|
## Whenever a new release is created via GitHub, publish it on PyPI.
|
||||||
|
#deploy:
|
||||||
|
# provider: pypi
|
||||||
|
# user: the_drow
|
||||||
|
# password:
|
||||||
|
# secure: QMyatmWBnC6ZN3XLW2+fTBDU4LQcp1m/LjR2/0uamyeUzWKdlOoh/Wx5elOgLwt/8N9ppdPeG83ose1jOz69l5G0MUMjv8n/RIcMFSpCT59tGYqn3kh55b0cIZXFT9ar+5cxlif6a5rS72IHm5li7QQyxexJIII6Uxp0kpvUmek=
|
||||||
|
#
|
||||||
|
# # Create a source distribution and a pure python wheel for faster installs.
|
||||||
|
# distributions: "sdist bdist_wheel"
|
||||||
|
#
|
||||||
|
# # Only deploy on tagged commits (aka GitHub releases) and only for the parent
|
||||||
|
# # repo's builds running Python v3.7 along with PyMongo v3.x and MongoDB v3.4.
|
||||||
|
# # We run Travis against many different Python, PyMongo, and MongoDB versions
|
||||||
|
# # and we don't want the deploy to occur multiple times).
|
||||||
|
# on:
|
||||||
|
# tags: true
|
||||||
|
# repo: MongoEngine/mongoengine
|
||||||
|
# condition: ($PYMONGO = ${PYMONGO_3_11}) && ($MONGODB = ${MONGODB_3_4})
|
||||||
|
# python: 3.7
|
||||||
@@ -35,6 +35,12 @@ html:
|
|||||||
@echo
|
@echo
|
||||||
@echo "Build finished. Check $(BUILDDIR)/html/index.html"
|
@echo "Build finished. Check $(BUILDDIR)/html/index.html"
|
||||||
|
|
||||||
|
html-readthedocs:
|
||||||
|
$(SPHINXBUILD) -T -E -b readthedocs $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||||
|
|
||||||
|
|
||||||
dirhtml:
|
dirhtml:
|
||||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||||
@echo
|
@echo
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
=========
|
=========
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
@@ -7,6 +8,17 @@ 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.23.0
|
||||||
|
===========
|
||||||
|
- Bugfix: manually setting SequenceField in DynamicDocument doesn't increment the counter #2471
|
||||||
|
- Add MongoDB 4.2 and 4.4 to CI
|
||||||
|
- Add support for allowDiskUse on querysets #2468
|
||||||
|
|
||||||
|
Changes in 0.22.1
|
||||||
|
=================
|
||||||
|
- Declare that Py3.5 is not supported in package metadata #2449
|
||||||
|
- Moved CI from Travis to Github-Actions
|
||||||
|
|
||||||
Changes in 0.22.0
|
Changes in 0.22.0
|
||||||
=================
|
=================
|
||||||
- Fix LazyReferenceField dereferencing in embedded documents #2426
|
- Fix LazyReferenceField dereferencing in embedded documents #2426
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ sys.path.insert(0, os.path.abspath(".."))
|
|||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = ["sphinx.ext.autodoc", "sphinx.ext.todo"]
|
extensions = ["sphinx.ext.autodoc", "sphinx.ext.todo", "readthedocs_ext.readthedocs"]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ["_templates"]
|
templates_path = ["_templates"]
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ the :attr:`host` to
|
|||||||
)
|
)
|
||||||
|
|
||||||
will establish connection to ``production`` database using
|
will establish connection to ``production`` database using
|
||||||
``admin`` username and ``qwerty`` password.
|
``admin`` username and ``12345`` password.
|
||||||
|
|
||||||
.. note:: Calling :func:`~mongoengine.connect` without argument will establish
|
.. note:: Calling :func:`~mongoengine.connect` without argument will establish
|
||||||
a connection to the "test" database by default
|
a connection to the "test" database by default
|
||||||
|
|||||||
@@ -432,10 +432,10 @@ Document collections
|
|||||||
====================
|
====================
|
||||||
Document classes that inherit **directly** from :class:`~mongoengine.Document`
|
Document classes that inherit **directly** from :class:`~mongoengine.Document`
|
||||||
will have their own **collection** in the database. The name of the collection
|
will have their own **collection** in the database. The name of the collection
|
||||||
is by default the name of the class, converted to lowercase (so in the example
|
is by default the name of the class converted to snake_case (e.g if your Document class
|
||||||
above, the collection would be called `page`). If you need to change the name
|
is named `CompanyUser`, the corresponding collection would be `company_user`). If you need
|
||||||
of the collection (e.g. to use MongoEngine with an existing database), then
|
to change the name of the collection (e.g. to use MongoEngine with an existing database),
|
||||||
create a class dictionary attribute called :attr:`meta` on your document, and
|
then create a class dictionary attribute called :attr:`meta` on your document, and
|
||||||
set :attr:`collection` to the name of the collection that you want your
|
set :attr:`collection` to the name of the collection that you want your
|
||||||
document class to use::
|
document class to use::
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
pymongo>=3.11
|
Sphinx==3.3.0
|
||||||
Sphinx==3.2.1
|
|
||||||
sphinx-rtd-theme==0.5.0
|
sphinx-rtd-theme==0.5.0
|
||||||
|
readthedocs-sphinx-ext==2.1.1
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
VERSION = (0, 22, 0)
|
VERSION = (0, 23, 0)
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ class BaseDocument:
|
|||||||
# Handle dynamic data only if an initialised dynamic document
|
# Handle dynamic data only if an initialised dynamic document
|
||||||
if self._dynamic and not self._dynamic_lock:
|
if self._dynamic and not self._dynamic_lock:
|
||||||
|
|
||||||
if not hasattr(self, name) and not name.startswith("_"):
|
if name not in self._fields_ordered and not name.startswith("_"):
|
||||||
DynamicField = _import_class("DynamicField")
|
DynamicField = _import_class("DynamicField")
|
||||||
field = DynamicField(db_field=name, null=True)
|
field = DynamicField(db_field=name, null=True)
|
||||||
field.name = name
|
field.name = name
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class Document(BaseDocument, metaclass=TopLevelDocumentMetaclass):
|
|||||||
|
|
||||||
By default, the MongoDB collection used to store documents created using a
|
By default, the MongoDB collection used to store documents created using a
|
||||||
:class:`~mongoengine.Document` subclass will be the name of the subclass
|
:class:`~mongoengine.Document` subclass will be the name of the subclass
|
||||||
converted to lowercase. A different collection may be specified by
|
converted to snake_case. A different collection may be specified by
|
||||||
providing :attr:`collection` to the :attr:`meta` dictionary in the class
|
providing :attr:`collection` to the :attr:`meta` dictionary in the class
|
||||||
definition.
|
definition.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
import inspect
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
@@ -514,7 +515,7 @@ class BooleanField(BaseField):
|
|||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
try:
|
try:
|
||||||
value = bool(value)
|
value = bool(value)
|
||||||
except ValueError:
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@@ -1028,17 +1029,6 @@ def key_not_string(d):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def key_has_dot_or_dollar(d):
|
|
||||||
"""Helper function to recursively determine if any key in a
|
|
||||||
dictionary contains a dot or a dollar sign.
|
|
||||||
"""
|
|
||||||
for k, v in d.items():
|
|
||||||
if ("." in k or k.startswith("$")) or (
|
|
||||||
isinstance(v, dict) and key_has_dot_or_dollar(v)
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def key_starts_with_dollar(d):
|
def key_starts_with_dollar(d):
|
||||||
"""Helper function to recursively determine if any key in a
|
"""Helper function to recursively determine if any key in a
|
||||||
dictionary starts with a dollar
|
dictionary starts with a dollar
|
||||||
@@ -1172,7 +1162,7 @@ class ReferenceField(BaseField):
|
|||||||
|
|
||||||
:param document_type: The type of Document that will be referenced
|
:param document_type: The type of Document that will be referenced
|
||||||
:param dbref: Store the reference as :class:`~pymongo.dbref.DBRef`
|
:param dbref: Store the reference as :class:`~pymongo.dbref.DBRef`
|
||||||
or as the :class:`~pymongo.objectid.ObjectId`.id .
|
or as the :class:`~pymongo.objectid.ObjectId`.
|
||||||
:param reverse_delete_rule: Determines what to do when the referring
|
:param reverse_delete_rule: Determines what to do when the referring
|
||||||
object is deleted
|
object is deleted
|
||||||
:param kwargs: Keyword arguments passed into the parent :class:`~mongoengine.BaseField`
|
:param kwargs: Keyword arguments passed into the parent :class:`~mongoengine.BaseField`
|
||||||
@@ -1311,8 +1301,8 @@ 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, str) and not issubclass(
|
if not isinstance(document_type, str) and not (
|
||||||
document_type, Document
|
inspect.isclass(document_type) and issubclass(document_type, Document)
|
||||||
):
|
):
|
||||||
self.error(
|
self.error(
|
||||||
"Argument to CachedReferenceField constructor must be a"
|
"Argument to CachedReferenceField constructor must be a"
|
||||||
@@ -1642,7 +1632,7 @@ class EnumField(BaseField):
|
|||||||
"'choices' can't be set on EnumField, "
|
"'choices' can't be set on EnumField, "
|
||||||
"it is implicitly set as the enum class"
|
"it is implicitly set as the enum class"
|
||||||
)
|
)
|
||||||
kwargs["choices"] = list(self._enum_cls)
|
kwargs["choices"] = list(self._enum_cls) # Implicit validator
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def __set__(self, instance, value):
|
def __set__(self, instance, value):
|
||||||
@@ -1659,13 +1649,6 @@ class EnumField(BaseField):
|
|||||||
return value.value
|
return value.value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
if value and not isinstance(value, self._enum_cls):
|
|
||||||
try:
|
|
||||||
self._enum_cls(value)
|
|
||||||
except Exception as e:
|
|
||||||
self.error(str(e))
|
|
||||||
|
|
||||||
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
|
||||||
@@ -2431,7 +2414,7 @@ class LazyReferenceField(BaseField):
|
|||||||
object is deleted
|
object is deleted
|
||||||
:param passthrough: When trying to access unknown fields, the
|
:param passthrough: When trying to access unknown fields, the
|
||||||
:class:`~mongoengine.base.datastructure.LazyReference` instance will
|
:class:`~mongoengine.base.datastructure.LazyReference` instance will
|
||||||
automatically call `fetch()` and try to retrive the field on the fetched
|
automatically call `fetch()` and try to retrieve the field on the fetched
|
||||||
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.
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ from mongoengine.connection import get_connection
|
|||||||
# get_mongodb_version()
|
# get_mongodb_version()
|
||||||
MONGODB_34 = (3, 4)
|
MONGODB_34 = (3, 4)
|
||||||
MONGODB_36 = (3, 6)
|
MONGODB_36 = (3, 6)
|
||||||
|
MONGODB_42 = (4, 2)
|
||||||
|
MONGODB_44 = (4, 4)
|
||||||
|
|
||||||
|
|
||||||
def get_mongodb_version():
|
def get_mongodb_version():
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class BaseQuerySet:
|
|||||||
self._ordering = None
|
self._ordering = None
|
||||||
self._snapshot = False
|
self._snapshot = False
|
||||||
self._timeout = True
|
self._timeout = True
|
||||||
|
self._allow_disk_use = False
|
||||||
self._read_preference = None
|
self._read_preference = None
|
||||||
self._read_concern = None
|
self._read_concern = None
|
||||||
self._iter = False
|
self._iter = False
|
||||||
@@ -799,6 +800,7 @@ class BaseQuerySet:
|
|||||||
"_ordering",
|
"_ordering",
|
||||||
"_snapshot",
|
"_snapshot",
|
||||||
"_timeout",
|
"_timeout",
|
||||||
|
"_allow_disk_use",
|
||||||
"_read_preference",
|
"_read_preference",
|
||||||
"_read_concern",
|
"_read_concern",
|
||||||
"_iter",
|
"_iter",
|
||||||
@@ -1165,6 +1167,16 @@ class BaseQuerySet:
|
|||||||
queryset._snapshot = enabled
|
queryset._snapshot = enabled
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
def allow_disk_use(self, enabled):
|
||||||
|
"""Enable or disable the use of temporary files on disk while processing a blocking sort operation.
|
||||||
|
(To store data exceeding the 100 megabyte system memory limit)
|
||||||
|
|
||||||
|
:param enabled: whether or not temporary files on disk are used
|
||||||
|
"""
|
||||||
|
queryset = self.clone()
|
||||||
|
queryset._allow_disk_use = enabled
|
||||||
|
return queryset
|
||||||
|
|
||||||
def timeout(self, enabled):
|
def timeout(self, enabled):
|
||||||
"""Enable or disable the default mongod timeout when querying. (no_cursor_timeout option)
|
"""Enable or disable the default mongod timeout when querying. (no_cursor_timeout option)
|
||||||
|
|
||||||
@@ -1343,21 +1355,18 @@ class BaseQuerySet:
|
|||||||
|
|
||||||
MapReduceDocument = _import_class("MapReduceDocument")
|
MapReduceDocument = _import_class("MapReduceDocument")
|
||||||
|
|
||||||
if not hasattr(self._collection, "map_reduce"):
|
|
||||||
raise NotImplementedError("Requires MongoDB >= 1.7.1")
|
|
||||||
|
|
||||||
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 = str(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 or None)
|
||||||
|
|
||||||
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 = str(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 or None)
|
||||||
|
|
||||||
mr_args = {"query": queryset._query}
|
mr_args = {"query": queryset._query}
|
||||||
|
|
||||||
@@ -1367,7 +1376,7 @@ class BaseQuerySet:
|
|||||||
finalize_f_scope = finalize_f.scope
|
finalize_f_scope = finalize_f.scope
|
||||||
finalize_f = str(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 or None)
|
||||||
mr_args["finalize"] = finalize_f
|
mr_args["finalize"] = finalize_f
|
||||||
|
|
||||||
if scope:
|
if scope:
|
||||||
@@ -1604,6 +1613,9 @@ class BaseQuerySet:
|
|||||||
if not self._timeout:
|
if not self._timeout:
|
||||||
cursor_args["no_cursor_timeout"] = True
|
cursor_args["no_cursor_timeout"] = True
|
||||||
|
|
||||||
|
if self._allow_disk_use:
|
||||||
|
cursor_args["allow_disk_use"] = 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()
|
||||||
|
|
||||||
|
|||||||
5
setup.py
5
setup.py
@@ -98,7 +98,6 @@ CLASSIFIERS = [
|
|||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.5",
|
|
||||||
"Programming Language :: Python :: 3.6",
|
"Programming Language :: Python :: 3.6",
|
||||||
"Programming Language :: Python :: 3.7",
|
"Programming Language :: Python :: 3.7",
|
||||||
"Programming Language :: Python :: 3.8",
|
"Programming Language :: Python :: 3.8",
|
||||||
@@ -113,7 +112,7 @@ extra_opts = {
|
|||||||
"tests_require": [
|
"tests_require": [
|
||||||
"pytest<5.0",
|
"pytest<5.0",
|
||||||
"pytest-cov",
|
"pytest-cov",
|
||||||
"coverage<5.0", # recent coverage switched to sqlite format for the .coverage file which isn't handled properly by coveralls
|
"coverage",
|
||||||
"blinker",
|
"blinker",
|
||||||
"Pillow>=7.0.0",
|
"Pillow>=7.0.0",
|
||||||
],
|
],
|
||||||
@@ -140,7 +139,7 @@ setup(
|
|||||||
long_description=LONG_DESCRIPTION,
|
long_description=LONG_DESCRIPTION,
|
||||||
platforms=["any"],
|
platforms=["any"],
|
||||||
classifiers=CLASSIFIERS,
|
classifiers=CLASSIFIERS,
|
||||||
python_requires=">=3.5",
|
python_requires=">=3.6",
|
||||||
install_requires=["pymongo>=3.4, <4.0"],
|
install_requires=["pymongo>=3.4, <4.0"],
|
||||||
cmdclass={"test": PyTest},
|
cmdclass={"test": PyTest},
|
||||||
**extra_opts
|
**extra_opts
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import pytest
|
|||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.connection import get_db
|
from mongoengine.connection import get_db
|
||||||
|
from mongoengine.mongodb_support import MONGODB_42, get_mongodb_version
|
||||||
|
|
||||||
|
|
||||||
class TestIndexes(unittest.TestCase):
|
class TestIndexes(unittest.TestCase):
|
||||||
@@ -452,9 +453,11 @@ class TestIndexes(unittest.TestCase):
|
|||||||
.get("stage")
|
.get("stage")
|
||||||
== "IXSCAN"
|
== "IXSCAN"
|
||||||
)
|
)
|
||||||
|
mongo_db = get_mongodb_version()
|
||||||
|
PROJECTION_STR = "PROJECTION" if mongo_db < MONGODB_42 else "PROJECTION_COVERED"
|
||||||
assert (
|
assert (
|
||||||
query_plan.get("queryPlanner").get("winningPlan").get("stage")
|
query_plan.get("queryPlanner").get("winningPlan").get("stage")
|
||||||
== "PROJECTION"
|
== PROJECTION_STR
|
||||||
)
|
)
|
||||||
|
|
||||||
query_plan = Test.objects(a=1).explain()
|
query_plan = Test.objects(a=1).explain()
|
||||||
|
|||||||
@@ -13,6 +13,17 @@ class TestBooleanField(MongoDBTestCase):
|
|||||||
person.save()
|
person.save()
|
||||||
assert get_as_pymongo(person) == {"_id": person.id, "admin": True}
|
assert get_as_pymongo(person) == {"_id": person.id, "admin": True}
|
||||||
|
|
||||||
|
def test_construction_does_not_fail_uncastable_value(self):
|
||||||
|
class BoolFail:
|
||||||
|
def __bool__(self):
|
||||||
|
return "bogus"
|
||||||
|
|
||||||
|
class Person(Document):
|
||||||
|
admin = BooleanField()
|
||||||
|
|
||||||
|
person = Person(admin=BoolFail())
|
||||||
|
person.admin == "bogus"
|
||||||
|
|
||||||
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
|
||||||
fields.
|
fields.
|
||||||
|
|||||||
@@ -2,11 +2,28 @@ from decimal import Decimal
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import (
|
||||||
|
CachedReferenceField,
|
||||||
|
DecimalField,
|
||||||
|
Document,
|
||||||
|
EmbeddedDocument,
|
||||||
|
EmbeddedDocumentField,
|
||||||
|
InvalidDocumentError,
|
||||||
|
ListField,
|
||||||
|
ReferenceField,
|
||||||
|
StringField,
|
||||||
|
ValidationError,
|
||||||
|
)
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestCachedReferenceField(MongoDBTestCase):
|
class TestCachedReferenceField(MongoDBTestCase):
|
||||||
|
def test_constructor_fail_bad_document_type(self):
|
||||||
|
with pytest.raises(
|
||||||
|
ValidationError, match="must be a document class or a string"
|
||||||
|
):
|
||||||
|
CachedReferenceField(document_type=0)
|
||||||
|
|
||||||
def test_get_and_save(self):
|
def test_get_and_save(self):
|
||||||
"""
|
"""
|
||||||
Tests #1047: CachedReferenceField creates DBRefs on to_python,
|
Tests #1047: CachedReferenceField creates DBRefs on to_python,
|
||||||
|
|||||||
@@ -2,59 +2,11 @@ from decimal import Decimal
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import DecimalField, Document, ValidationError
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestDecimalField(MongoDBTestCase):
|
class TestDecimalField(MongoDBTestCase):
|
||||||
def test_validation(self):
|
|
||||||
"""Ensure that invalid values cannot be assigned to decimal fields."""
|
|
||||||
|
|
||||||
class Person(Document):
|
|
||||||
height = DecimalField(min_value=Decimal("0.1"), max_value=Decimal("3.5"))
|
|
||||||
|
|
||||||
Person.drop_collection()
|
|
||||||
|
|
||||||
Person(height=Decimal("1.89")).save()
|
|
||||||
person = Person.objects.first()
|
|
||||||
assert person.height == Decimal("1.89")
|
|
||||||
|
|
||||||
person.height = "2.0"
|
|
||||||
person.save()
|
|
||||||
person.height = 0.01
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
person.validate()
|
|
||||||
person.height = Decimal("0.01")
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
person.validate()
|
|
||||||
person.height = Decimal("4.0")
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
person.validate()
|
|
||||||
person.height = "something invalid"
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
person.validate()
|
|
||||||
|
|
||||||
person_2 = Person(height="something invalid")
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
person_2.validate()
|
|
||||||
|
|
||||||
def test_comparison(self):
|
|
||||||
class Person(Document):
|
|
||||||
money = DecimalField()
|
|
||||||
|
|
||||||
Person.drop_collection()
|
|
||||||
|
|
||||||
Person(money=6).save()
|
|
||||||
Person(money=7).save()
|
|
||||||
Person(money=8).save()
|
|
||||||
Person(money=10).save()
|
|
||||||
|
|
||||||
assert 2 == Person.objects(money__gt=Decimal("7")).count()
|
|
||||||
assert 2 == Person.objects(money__gt=7).count()
|
|
||||||
assert 2 == Person.objects(money__gt="7").count()
|
|
||||||
|
|
||||||
assert 3 == Person.objects(money__gte="7").count()
|
|
||||||
|
|
||||||
def test_storage(self):
|
def test_storage(self):
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
float_value = DecimalField(precision=4)
|
float_value = DecimalField(precision=4)
|
||||||
@@ -106,3 +58,63 @@ class TestDecimalField(MongoDBTestCase):
|
|||||||
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))
|
||||||
assert expected == actual
|
assert expected == actual
|
||||||
|
|
||||||
|
def test_save_none(self):
|
||||||
|
class Person(Document):
|
||||||
|
value = DecimalField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
person = Person(value=None)
|
||||||
|
assert person.value is None
|
||||||
|
person.save()
|
||||||
|
fetched_person = Person.objects.first()
|
||||||
|
fetched_person.value is None
|
||||||
|
|
||||||
|
def test_validation(self):
|
||||||
|
"""Ensure that invalid values cannot be assigned to decimal fields."""
|
||||||
|
|
||||||
|
class Person(Document):
|
||||||
|
height = DecimalField(min_value=Decimal("0.1"), max_value=Decimal("3.5"))
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
Person(height=Decimal("1.89")).save()
|
||||||
|
person = Person.objects.first()
|
||||||
|
assert person.height == Decimal("1.89")
|
||||||
|
|
||||||
|
person.height = "2.0"
|
||||||
|
person.save()
|
||||||
|
person.height = 0.01
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
person.height = Decimal("0.01")
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
person.height = Decimal("4.0")
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
person.height = "something invalid"
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
|
person_2 = Person(height="something invalid")
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
person_2.validate()
|
||||||
|
|
||||||
|
def test_comparison(self):
|
||||||
|
class Person(Document):
|
||||||
|
money = DecimalField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
Person(money=6).save()
|
||||||
|
Person(money=7).save()
|
||||||
|
Person(money=8).save()
|
||||||
|
Person(money=10).save()
|
||||||
|
|
||||||
|
assert 2 == Person.objects(money__gt=Decimal("7")).count()
|
||||||
|
assert 2 == Person.objects(money__gt=7).count()
|
||||||
|
assert 2 == Person.objects(money__gt="7").count()
|
||||||
|
|
||||||
|
assert 3 == Person.objects(money__gte="7").count()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from enum import Enum
|
|||||||
from bson import InvalidDocument
|
from bson import InvalidDocument
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from mongoengine import *
|
from mongoengine import Document, EnumField, ValidationError
|
||||||
from tests.utils import MongoDBTestCase, get_as_pymongo
|
from tests.utils import MongoDBTestCase, get_as_pymongo
|
||||||
|
|
||||||
|
|
||||||
@@ -45,6 +45,11 @@ class TestStringEnumField(MongoDBTestCase):
|
|||||||
m.save()
|
m.save()
|
||||||
assert m.status == Status.DONE
|
assert m.status == Status.DONE
|
||||||
|
|
||||||
|
m.status = "wrong"
|
||||||
|
assert m.status == "wrong"
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
m.validate()
|
||||||
|
|
||||||
def test_set_default(self):
|
def test_set_default(self):
|
||||||
class ModelWithDefault(Document):
|
class ModelWithDefault(Document):
|
||||||
status = EnumField(Status, default=Status.DONE)
|
status = EnumField(Status, default=Status.DONE)
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ class TestField(MongoDBTestCase):
|
|||||||
)
|
)
|
||||||
assert res == 1
|
assert res == 1
|
||||||
|
|
||||||
# Retrive data from db and verify it.
|
# Retrieve data from db and verify it.
|
||||||
ret = HandleNoneFields.objects.all()[0]
|
ret = HandleNoneFields.objects.all()[0]
|
||||||
assert ret.str_fld is None
|
assert ret.str_fld is None
|
||||||
assert ret.int_fld is None
|
assert ret.int_fld is None
|
||||||
@@ -340,7 +340,7 @@ class TestField(MongoDBTestCase):
|
|||||||
{"$unset": {"str_fld": 1, "int_fld": 1, "flt_fld": 1, "comp_dt_fld": 1}},
|
{"$unset": {"str_fld": 1, "int_fld": 1, "flt_fld": 1, "comp_dt_fld": 1}},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Retrive data from db and verify it.
|
# Retrieve data from db and verify it.
|
||||||
ret = HandleNoneFields.objects.first()
|
ret = HandleNoneFields.objects.first()
|
||||||
assert ret.str_fld is None
|
assert ret.str_fld is None
|
||||||
assert ret.int_fld is None
|
assert ret.int_fld is None
|
||||||
@@ -374,34 +374,6 @@ class TestField(MongoDBTestCase):
|
|||||||
person.id = str(ObjectId())
|
person.id = str(ObjectId())
|
||||||
person.validate()
|
person.validate()
|
||||||
|
|
||||||
def test_string_validation(self):
|
|
||||||
"""Ensure that invalid values cannot be assigned to string fields."""
|
|
||||||
|
|
||||||
class Person(Document):
|
|
||||||
name = StringField(max_length=20)
|
|
||||||
userid = StringField(r"[0-9a-z_]+$")
|
|
||||||
|
|
||||||
person = Person(name=34)
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
person.validate()
|
|
||||||
|
|
||||||
# Test regex validation on userid
|
|
||||||
person = Person(userid="test.User")
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
person.validate()
|
|
||||||
|
|
||||||
person.userid = "test_user"
|
|
||||||
assert person.userid == "test_user"
|
|
||||||
person.validate()
|
|
||||||
|
|
||||||
# Test max length validation on name
|
|
||||||
person = Person(name="Name that is more than twenty characters")
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
person.validate()
|
|
||||||
|
|
||||||
person.name = "Shorter name"
|
|
||||||
person.validate()
|
|
||||||
|
|
||||||
def test_db_field_validation(self):
|
def test_db_field_validation(self):
|
||||||
"""Ensure that db_field doesn't accept invalid values."""
|
"""Ensure that db_field doesn't accept invalid values."""
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,26 @@ import pytest
|
|||||||
from mongoengine import *
|
from mongoengine import *
|
||||||
from mongoengine.connection import get_db
|
from mongoengine.connection import get_db
|
||||||
|
|
||||||
from tests.utils import MongoDBTestCase
|
from tests.utils import MongoDBTestCase, get_as_pymongo
|
||||||
|
|
||||||
|
|
||||||
class TestLongField(MongoDBTestCase):
|
class TestLongField(MongoDBTestCase):
|
||||||
|
def test_storage(self):
|
||||||
|
class Person(Document):
|
||||||
|
value = LongField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
person = Person(value=5000)
|
||||||
|
person.save()
|
||||||
|
assert get_as_pymongo(person) == {"_id": person.id, "value": 5000}
|
||||||
|
|
||||||
|
def test_construction_does_not_fail_with_invalid_value(self):
|
||||||
|
class Person(Document):
|
||||||
|
value = LongField()
|
||||||
|
|
||||||
|
person = Person(value="not_an_int")
|
||||||
|
assert person.value == "not_an_int"
|
||||||
|
|
||||||
def test_long_field_is_considered_as_int64(self):
|
def test_long_field_is_considered_as_int64(self):
|
||||||
"""
|
"""
|
||||||
Tests that long fields are stored as long in mongo, even if long
|
Tests that long fields are stored as long in mongo, even if long
|
||||||
@@ -30,19 +46,16 @@ class TestLongField(MongoDBTestCase):
|
|||||||
class TestDocument(Document):
|
class TestDocument(Document):
|
||||||
value = LongField(min_value=0, max_value=110)
|
value = LongField(min_value=0, max_value=110)
|
||||||
|
|
||||||
doc = TestDocument()
|
TestDocument(value=50).validate()
|
||||||
doc.value = 50
|
|
||||||
doc.validate()
|
|
||||||
|
|
||||||
doc.value = -1
|
|
||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
doc.validate()
|
TestDocument(value=-1).validate()
|
||||||
doc.value = 120
|
|
||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
doc.validate()
|
TestDocument(value=120).validate()
|
||||||
doc.value = "ten"
|
|
||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
doc.validate()
|
TestDocument(value="ten").validate()
|
||||||
|
|
||||||
def test_long_ne_operator(self):
|
def test_long_ne_operator(self):
|
||||||
class TestDocument(Document):
|
class TestDocument(Document):
|
||||||
@@ -53,4 +66,5 @@ class TestLongField(MongoDBTestCase):
|
|||||||
TestDocument(long_fld=None).save()
|
TestDocument(long_fld=None).save()
|
||||||
TestDocument(long_fld=1).save()
|
TestDocument(long_fld=1).save()
|
||||||
|
|
||||||
assert 1 == TestDocument.objects(long_fld__ne=None).count()
|
assert TestDocument.objects(long_fld__ne=None).count() == 1
|
||||||
|
assert TestDocument.objects(long_fld__ne=1).count() == 1
|
||||||
|
|||||||
@@ -274,3 +274,25 @@ class TestSequenceField(MongoDBTestCase):
|
|||||||
assert foo.counter == bar.counter
|
assert foo.counter == bar.counter
|
||||||
assert foo._fields["counter"].owner_document == Foo
|
assert foo._fields["counter"].owner_document == Foo
|
||||||
assert bar._fields["counter"].owner_document == Bar
|
assert bar._fields["counter"].owner_document == Bar
|
||||||
|
|
||||||
|
def test_sequence_setattr_not_incrementing_counter(self):
|
||||||
|
class Person(DynamicDocument):
|
||||||
|
id = SequenceField(primary_key=True)
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
self.db["mongoengine.counters"].drop()
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
for x in range(10):
|
||||||
|
Person(name="Person %s" % x).save()
|
||||||
|
|
||||||
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
|
assert c["next"] == 10
|
||||||
|
|
||||||
|
# Setting SequenceField field value should not increment counter:
|
||||||
|
new_person = Person()
|
||||||
|
new_person.id = 1100
|
||||||
|
|
||||||
|
# Counter should still be at 10
|
||||||
|
c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
|
||||||
|
assert c["next"] == 10
|
||||||
|
|||||||
43
tests/fields/test_string_field.py
Normal file
43
tests/fields/test_string_field.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
|
from tests.utils import MongoDBTestCase, get_as_pymongo
|
||||||
|
|
||||||
|
|
||||||
|
class TestStringField(MongoDBTestCase):
|
||||||
|
def test_storage(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
person = Person(name="test123")
|
||||||
|
person.save()
|
||||||
|
assert get_as_pymongo(person) == {"_id": person.id, "name": "test123"}
|
||||||
|
|
||||||
|
def test_validation(self):
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField(max_length=20, min_length=2)
|
||||||
|
userid = StringField(r"[0-9a-z_]+$")
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError, match="only accepts string values"):
|
||||||
|
Person(name=34).validate()
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError, match="value is too short"):
|
||||||
|
Person(name="s").validate()
|
||||||
|
|
||||||
|
# Test regex validation on userid
|
||||||
|
person = Person(userid="test.User")
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
|
person.userid = "test_user"
|
||||||
|
assert person.userid == "test_user"
|
||||||
|
person.validate()
|
||||||
|
|
||||||
|
# Test max length validation on name
|
||||||
|
person = Person(name="Name that is more than twenty characters")
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
person.validate()
|
||||||
|
|
||||||
|
person = Person(name="a friendl name", userid="7a757668sqjdkqlsdkq")
|
||||||
|
person.validate()
|
||||||
@@ -21,6 +21,10 @@ from mongoengine.queryset import (
|
|||||||
QuerySetManager,
|
QuerySetManager,
|
||||||
queryset_manager,
|
queryset_manager,
|
||||||
)
|
)
|
||||||
|
from tests.utils import (
|
||||||
|
requires_mongodb_gte_44,
|
||||||
|
requires_mongodb_lt_42,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class db_ops_tracker(query_counter):
|
class db_ops_tracker(query_counter):
|
||||||
@@ -1489,6 +1493,7 @@ class TestQueryset(unittest.TestCase):
|
|||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
@requires_mongodb_lt_42
|
||||||
def test_exec_js_query(self):
|
def test_exec_js_query(self):
|
||||||
"""Ensure that queries are properly formed for use in exec_js."""
|
"""Ensure that queries are properly formed for use in exec_js."""
|
||||||
|
|
||||||
@@ -1526,6 +1531,7 @@ class TestQueryset(unittest.TestCase):
|
|||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
@requires_mongodb_lt_42
|
||||||
def test_exec_js_field_sub(self):
|
def test_exec_js_field_sub(self):
|
||||||
"""Ensure that field substitutions occur properly in exec_js functions."""
|
"""Ensure that field substitutions occur properly in exec_js functions."""
|
||||||
|
|
||||||
@@ -2659,6 +2665,8 @@ class TestQueryset(unittest.TestCase):
|
|||||||
title = StringField(primary_key=True)
|
title = StringField(primary_key=True)
|
||||||
tags = ListField(StringField())
|
tags = ListField(StringField())
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
post1 = BlogPost(title="Post #1", tags=["mongodb", "mongoengine"])
|
post1 = BlogPost(title="Post #1", tags=["mongodb", "mongoengine"])
|
||||||
post2 = BlogPost(title="Post #2", tags=["django", "mongodb"])
|
post2 = BlogPost(title="Post #2", tags=["django", "mongodb"])
|
||||||
post3 = BlogPost(title="Post #3", tags=["hitchcock films"])
|
post3 = BlogPost(title="Post #3", tags=["hitchcock films"])
|
||||||
@@ -2687,12 +2695,15 @@ class TestQueryset(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
results = BlogPost.objects.map_reduce(map_f, reduce_f, "myresults")
|
results = BlogPost.objects.order_by("_id").map_reduce(
|
||||||
|
map_f, reduce_f, "myresults2"
|
||||||
|
)
|
||||||
results = list(results)
|
results = list(results)
|
||||||
|
|
||||||
assert results[0].object == post1
|
assert len(results) == 3
|
||||||
assert results[1].object == post2
|
assert results[0].object.id == post1.id
|
||||||
assert results[2].object == post3
|
assert results[1].object.id == post2.id
|
||||||
|
assert results[2].object.id == post3.id
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
@@ -2700,7 +2711,6 @@ class TestQueryset(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
Test map/reduce custom output
|
Test map/reduce custom output
|
||||||
"""
|
"""
|
||||||
register_connection("test2", "mongoenginetest2")
|
|
||||||
|
|
||||||
class Family(Document):
|
class Family(Document):
|
||||||
id = IntField(primary_key=True)
|
id = IntField(primary_key=True)
|
||||||
@@ -2773,6 +2783,7 @@ class TestQueryset(unittest.TestCase):
|
|||||||
family.persons.push(person);
|
family.persons.push(person);
|
||||||
family.totalAge += person.age;
|
family.totalAge += person.age;
|
||||||
});
|
});
|
||||||
|
family.persons.sort((a, b) => (a.age > b.age))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2801,10 +2812,10 @@ class TestQueryset(unittest.TestCase):
|
|||||||
"_id": 1,
|
"_id": 1,
|
||||||
"value": {
|
"value": {
|
||||||
"persons": [
|
"persons": [
|
||||||
{"age": 21, "name": "Wilson Jr"},
|
|
||||||
{"age": 45, "name": "Wilson Father"},
|
|
||||||
{"age": 40, "name": "Eliana Costa"},
|
|
||||||
{"age": 17, "name": "Tayza Mariana"},
|
{"age": 17, "name": "Tayza Mariana"},
|
||||||
|
{"age": 21, "name": "Wilson Jr"},
|
||||||
|
{"age": 40, "name": "Eliana Costa"},
|
||||||
|
{"age": 45, "name": "Wilson Father"},
|
||||||
],
|
],
|
||||||
"totalAge": 123,
|
"totalAge": 123,
|
||||||
},
|
},
|
||||||
@@ -2814,9 +2825,9 @@ class TestQueryset(unittest.TestCase):
|
|||||||
"_id": 2,
|
"_id": 2,
|
||||||
"value": {
|
"value": {
|
||||||
"persons": [
|
"persons": [
|
||||||
|
{"age": 10, "name": "Igor Gabriel"},
|
||||||
{"age": 16, "name": "Isabella Luanna"},
|
{"age": 16, "name": "Isabella Luanna"},
|
||||||
{"age": 36, "name": "Sandra Mara"},
|
{"age": 36, "name": "Sandra Mara"},
|
||||||
{"age": 10, "name": "Igor Gabriel"},
|
|
||||||
],
|
],
|
||||||
"totalAge": 62,
|
"totalAge": 62,
|
||||||
},
|
},
|
||||||
@@ -2826,8 +2837,8 @@ class TestQueryset(unittest.TestCase):
|
|||||||
"_id": 3,
|
"_id": 3,
|
||||||
"value": {
|
"value": {
|
||||||
"persons": [
|
"persons": [
|
||||||
{"age": 30, "name": "Arthur WA"},
|
|
||||||
{"age": 25, "name": "Paula Leonel"},
|
{"age": 25, "name": "Paula Leonel"},
|
||||||
|
{"age": 30, "name": "Arthur WA"},
|
||||||
],
|
],
|
||||||
"totalAge": 55,
|
"totalAge": 55,
|
||||||
},
|
},
|
||||||
@@ -3108,6 +3119,7 @@ class TestQueryset(unittest.TestCase):
|
|||||||
freq = Person.objects.item_frequencies("city", normalize=True, map_reduce=True)
|
freq = Person.objects.item_frequencies("city", normalize=True, map_reduce=True)
|
||||||
assert freq == {"CRB": 0.5, None: 0.5}
|
assert freq == {"CRB": 0.5, None: 0.5}
|
||||||
|
|
||||||
|
@requires_mongodb_lt_42
|
||||||
def test_item_frequencies_with_null_embedded(self):
|
def test_item_frequencies_with_null_embedded(self):
|
||||||
class Data(EmbeddedDocument):
|
class Data(EmbeddedDocument):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
@@ -3136,6 +3148,7 @@ class TestQueryset(unittest.TestCase):
|
|||||||
ot = Person.objects.item_frequencies("extra.tag", map_reduce=True)
|
ot = Person.objects.item_frequencies("extra.tag", map_reduce=True)
|
||||||
assert ot == {None: 1.0, "friend": 1.0}
|
assert ot == {None: 1.0, "friend": 1.0}
|
||||||
|
|
||||||
|
@requires_mongodb_lt_42
|
||||||
def test_item_frequencies_with_0_values(self):
|
def test_item_frequencies_with_0_values(self):
|
||||||
class Test(Document):
|
class Test(Document):
|
||||||
val = IntField()
|
val = IntField()
|
||||||
@@ -3150,6 +3163,7 @@ class TestQueryset(unittest.TestCase):
|
|||||||
ot = Test.objects.item_frequencies("val", map_reduce=False)
|
ot = Test.objects.item_frequencies("val", map_reduce=False)
|
||||||
assert ot == {0: 1}
|
assert ot == {0: 1}
|
||||||
|
|
||||||
|
@requires_mongodb_lt_42
|
||||||
def test_item_frequencies_with_False_values(self):
|
def test_item_frequencies_with_False_values(self):
|
||||||
class Test(Document):
|
class Test(Document):
|
||||||
val = BooleanField()
|
val = BooleanField()
|
||||||
@@ -3164,6 +3178,7 @@ class TestQueryset(unittest.TestCase):
|
|||||||
ot = Test.objects.item_frequencies("val", map_reduce=False)
|
ot = Test.objects.item_frequencies("val", map_reduce=False)
|
||||||
assert ot == {False: 1}
|
assert ot == {False: 1}
|
||||||
|
|
||||||
|
@requires_mongodb_lt_42
|
||||||
def test_item_frequencies_normalize(self):
|
def test_item_frequencies_normalize(self):
|
||||||
class Test(Document):
|
class Test(Document):
|
||||||
val = IntField()
|
val = IntField()
|
||||||
@@ -3550,7 +3565,8 @@ class TestQueryset(unittest.TestCase):
|
|||||||
Book.objects.create(title="The Stories", authors=[mark_twain, john_tolkien])
|
Book.objects.create(title="The Stories", authors=[mark_twain, john_tolkien])
|
||||||
|
|
||||||
authors = Book.objects.distinct("authors")
|
authors = Book.objects.distinct("authors")
|
||||||
assert authors == [mark_twain, john_tolkien]
|
authors_names = {author.name for author in authors}
|
||||||
|
assert authors_names == {mark_twain.name, john_tolkien.name}
|
||||||
|
|
||||||
def test_distinct_ListField_EmbeddedDocumentField_EmbeddedDocumentField(self):
|
def test_distinct_ListField_EmbeddedDocumentField_EmbeddedDocumentField(self):
|
||||||
class Continent(EmbeddedDocument):
|
class Continent(EmbeddedDocument):
|
||||||
@@ -3587,7 +3603,8 @@ class TestQueryset(unittest.TestCase):
|
|||||||
assert country_list == [scotland, tibet]
|
assert country_list == [scotland, tibet]
|
||||||
|
|
||||||
continent_list = Book.objects.distinct("authors.country.continent")
|
continent_list = Book.objects.distinct("authors.country.continent")
|
||||||
assert continent_list == [europe, asia]
|
continent_list_names = {c.continent_name for c in continent_list}
|
||||||
|
assert continent_list_names == {europe.continent_name, asia.continent_name}
|
||||||
|
|
||||||
def test_distinct_ListField_ReferenceField(self):
|
def test_distinct_ListField_ReferenceField(self):
|
||||||
class Bar(Document):
|
class Bar(Document):
|
||||||
@@ -5656,6 +5673,31 @@ class TestQueryset(unittest.TestCase):
|
|||||||
qs = self.Person.objects().timeout(False)
|
qs = self.Person.objects().timeout(False)
|
||||||
assert qs._cursor_args == {"no_cursor_timeout": True}
|
assert qs._cursor_args == {"no_cursor_timeout": True}
|
||||||
|
|
||||||
|
@requires_mongodb_gte_44
|
||||||
|
def test_allow_disk_use(self):
|
||||||
|
qs = self.Person.objects()
|
||||||
|
assert qs._cursor_args == {}
|
||||||
|
|
||||||
|
qs = self.Person.objects().allow_disk_use(False)
|
||||||
|
assert qs._cursor_args == {}
|
||||||
|
|
||||||
|
qs = self.Person.objects().allow_disk_use(True)
|
||||||
|
assert qs._cursor_args == {"allow_disk_use": True}
|
||||||
|
|
||||||
|
# Test if allow_disk_use changes the results
|
||||||
|
self.Person.drop_collection()
|
||||||
|
self.Person.objects.create(name="Foo", age=12)
|
||||||
|
self.Person.objects.create(name="Baz", age=17)
|
||||||
|
self.Person.objects.create(name="Bar", age=13)
|
||||||
|
|
||||||
|
qs_disk = self.Person.objects().order_by("age").allow_disk_use(True)
|
||||||
|
qs = self.Person.objects().order_by("age")
|
||||||
|
|
||||||
|
assert qs_disk.count() == qs.count()
|
||||||
|
|
||||||
|
for index in range(qs_disk.count()):
|
||||||
|
assert qs_disk[index] == qs[index]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -248,6 +248,34 @@ class TestQuerysetAggregate(MongoDBTestCase):
|
|||||||
|
|
||||||
assert list(data) == [{"_id": p1.pk, "name": "ISABELLA LUANNA"}]
|
assert list(data) == [{"_id": p1.pk, "name": "ISABELLA LUANNA"}]
|
||||||
|
|
||||||
|
def test_queryset_aggregation_geonear_aggregation_on_pointfield(self):
|
||||||
|
"""test ensures that $geonear can be used as a 1-stage pipeline and that
|
||||||
|
MongoEngine does not interfer with such pipeline (#2473)
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Aggr(Document):
|
||||||
|
name = StringField()
|
||||||
|
c = PointField()
|
||||||
|
|
||||||
|
Aggr.drop_collection()
|
||||||
|
|
||||||
|
agg1 = Aggr(name="X", c=[10.634584, 35.8245029]).save()
|
||||||
|
agg2 = Aggr(name="Y", c=[10.634584, 35.8245029]).save()
|
||||||
|
|
||||||
|
pipeline = [
|
||||||
|
{
|
||||||
|
"$geoNear": {
|
||||||
|
"near": {"type": "Point", "coordinates": [10.634584, 35.8245029]},
|
||||||
|
"distanceField": "c",
|
||||||
|
"spherical": True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
assert list(Aggr.objects.aggregate(*pipeline)) == [
|
||||||
|
{"_id": agg1.id, "c": 0.0, "name": "X"},
|
||||||
|
{"_id": agg2.id, "c": 0.0, "name": "Y"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import operator
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -33,6 +34,14 @@ def get_as_pymongo(doc):
|
|||||||
return doc.__class__.objects.as_pymongo().get(id=doc.id)
|
return doc.__class__.objects.as_pymongo().get(id=doc.id)
|
||||||
|
|
||||||
|
|
||||||
|
def requires_mongodb_lt_42(func):
|
||||||
|
return _decorated_with_ver_requirement(func, (4, 2), oper=operator.lt)
|
||||||
|
|
||||||
|
|
||||||
|
def requires_mongodb_gte_44(func):
|
||||||
|
return _decorated_with_ver_requirement(func, (4, 4), oper=operator.ge)
|
||||||
|
|
||||||
|
|
||||||
def _decorated_with_ver_requirement(func, mongo_version_req, oper):
|
def _decorated_with_ver_requirement(func, mongo_version_req, oper):
|
||||||
"""Return a MongoDB version requirement decorator.
|
"""Return a MongoDB version requirement decorator.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user