Compare commits

..

35 Commits

Author SHA1 Message Date
Stefan Wojcik
2626eca58e more readme tweaks [ci skip] 2017-02-26 13:38:39 -05:00
Stefan Wojcik
d5b1914158 include supported mongodb ver info in the readme [ci skip] 2017-02-26 13:35:03 -05:00
Stefan Wojcik
e20a34857d fix py3 2017-02-26 12:11:21 -05:00
Stefan Wojcik
7d0b533920 nicer ver decorators + only test $minDistance in v3.0 2017-02-26 11:59:22 -05:00
Stefan Wojcik
6a9e02635d refactor poorly isolated geo unit tests 2017-02-26 11:23:27 -05:00
Stefan Wojcik
5a1fdadf8b pymongo v2.7 doesn't support MongoDB v3.0 2017-02-26 03:13:58 -05:00
Stefan Wojcik
70871f671a fix version comparison 2017-02-26 02:48:35 -05:00
Stefan Wojcik
e6821d8656 flake8 debug 2017-02-26 02:45:37 -05:00
Stefan Wojcik
4f85547333 fix package name 2017-02-26 02:35:50 -05:00
Stefan Wojcik
deeb6d026d fix the $max operator 2017-02-26 02:29:59 -05:00
Stefan Wojcik
97d9a7cf43 different approach to running flake8 2017-02-26 02:29:21 -05:00
Stefan Wojcik
7850e47bf7 fix the $max operator 2017-02-26 02:24:48 -05:00
Stefan Wojcik
a94ec06275 fix authSource test 2017-02-26 02:21:25 -05:00
Stefan Wojcik
4acd7a5b75 skip older mongodb decorator 2017-02-26 02:14:34 -05:00
Stefan Wojcik
e6dcb39036 revert aggregate change 2017-02-25 22:54:19 -05:00
Stefan Wojcik
4ed65f8589 skip $minDistance for older MongoDB vers 2017-02-25 22:52:35 -05:00
Stefan Wojcik
39586e83cd pymongo v2.7 doesn't support MongoDB v3.0 2017-02-25 22:11:15 -05:00
Stefan Wojcik
5992fdd408 remove debug statement 2017-02-25 20:58:04 -05:00
Stefan Wojcik
70faaf445e fix queryset tests in mongodb v2.4 2017-02-25 20:55:59 -05:00
Stefan Wojcik
71ccfeac3c get_mongodb_version tests helper + fix indexing tests in mongodb v2.4 2017-02-25 20:21:20 -05:00
Stefan Wojcik
d39f5197cb tox.init cleanup 2017-02-25 19:10:00 -05:00
Stefan Wojcik
5b4eb02683 shuffle some jobs, focus on testing py27 and py35 2017-02-25 19:07:32 -05:00
Stefan Wojcik
d0442d7578 mongo v2.4 needs explicit service start 2017-02-25 18:48:59 -05:00
Stefan Wojcik
098a90ee96 no need for service start 2017-02-25 18:32:08 -05:00
Stefan Wojcik
d7d48e4c21 fix .travis.yml 2017-02-25 18:25:26 -05:00
Stefan Wojcik
cb62b457bc fix mongo install script 2017-02-25 18:21:04 -05:00
Stefan Wojcik
d29c1c1094 missing file 2017-02-25 18:17:53 -05:00
Stefan Wojcik
d6af8cb157 fix .travis.yml 2017-02-25 18:15:28 -05:00
Stefan Wojcik
8e2e5f506d customize the matrix + separate script to install mongodb 2017-02-25 18:11:21 -05:00
Stefan Wojcik
0ea1dfddaa merge latest master + test pymongo v2.8 instead of v2.9 + drop dev pymongo + smarter pypi deployment 2017-02-25 16:54:46 -05:00
Omer Katz
ce7142eb4f Fix the repo entry for MongoDB 3. 2015-07-17 21:53:06 +03:00
Omer Katz
5e17fc6d47 Only start mongodb if it's not started already after installation. 2015-07-17 21:46:31 +03:00
Omer Katz
ee7bd1bdae Start MongoDB if it's stopped. 2015-07-17 21:32:55 +03:00
Omer Katz
d75dc02614 Fixed the repos entry. 2015-07-17 21:25:12 +03:00
Omer Katz
c08ee77041 Test against multiple MongoDB versions. 2015-07-17 21:21:09 +03:00
18 changed files with 968 additions and 1079 deletions

1
.gitignore vendored
View File

@@ -17,4 +17,3 @@ tests/test_bugfix.py
htmlcov/
venv
venv3
scratchpad

View File

@@ -5,21 +5,11 @@ Changelog
Development
===========
- (Fill this out as you fix issues and develop your features).
Changes in 0.12.0
=================
- Fixed using sets in field choices #1481
- POTENTIAL BREAKING CHANGE: Fixed limit/skip/hint/batch_size chaining #1476
- POTENTIAL BREAKING CHANGE: Changed a public `QuerySet.clone_into` method to a private `QuerySet._clone_into` #1476
- Fixed connecting to a replica set with PyMongo 2.x #1436
- Fixed using sets in field choices #1481
- Fixed deleting items from a `ListField` #1318
- Fixed an obscure error message when filtering by `field__in=non_iterable`. #1237
- Fixed behavior of a `dec` update operator #1450
- Added a `rename` update operator #1454
- Added validation for the `db_field` parameter #1448
- Fixed the error message displayed when querying an `EmbeddedDocumentField` by an invalid value #1440
- Fixed the error message displayed when validating unicode URLs #1486
- Raise an error when trying to save an abstract document #1449
Changes in 0.11.0
=================

View File

@@ -42,18 +42,13 @@ the :attr:`host` to
will establish connection to ``production`` database using
``admin`` username and ``qwerty`` password.
Replica Sets
============
ReplicaSets
===========
MongoEngine supports connecting to replica sets::
from mongoengine import connect
# Regular connect
connect('dbname', replicaset='rs-name')
# MongoDB URI-style connect
connect(host='mongodb://localhost/dbname?replicaSet=rs-name')
MongoEngine supports
:class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`. To use them,
please use an URI style connection and provide the ``replicaSet`` name
in the connection kwargs.
Read preferences are supported through the connection or via individual
queries by passing the read_preference ::
@@ -64,74 +59,76 @@ queries by passing the read_preference ::
Multiple Databases
==================
To use multiple databases you can use :func:`~mongoengine.connect` and provide
an `alias` name for the connection - if no `alias` is provided then "default"
is used.
Multiple database support was added in MongoEngine 0.6. To use multiple
databases you can use :func:`~mongoengine.connect` and provide an `alias` name
for the connection - if no `alias` is provided then "default" is used.
In the background this uses :func:`~mongoengine.register_connection` to
store the data and you can register all aliases up front if required.
Individual documents can also support multiple databases by providing a
`db_alias` in their meta data. This allows :class:`~pymongo.dbref.DBRef`
objects to point across databases and collections. Below is an example schema,
using 3 different databases to store data::
`db_alias` in their meta data. This allows :class:`~pymongo.dbref.DBRef` objects
to point across databases and collections. Below is an example schema, using
3 different databases to store data::
class User(Document):
name = StringField()
meta = {'db_alias': 'user-db'}
meta = {"db_alias": "user-db"}
class Book(Document):
name = StringField()
meta = {'db_alias': 'book-db'}
meta = {"db_alias": "book-db"}
class AuthorBooks(Document):
author = ReferenceField(User)
book = ReferenceField(Book)
meta = {'db_alias': 'users-books-db'}
meta = {"db_alias": "users-books-db"}
Context Managers
================
Sometimes you may want to switch the database or collection to query against.
Sometimes you may want to switch the database or collection to query against
for a class.
For example, archiving older data into a separate database for performance
reasons or writing functions that dynamically choose collections to write
a document to.
document to.
Switch Database
---------------
The :class:`~mongoengine.context_managers.switch_db` context manager allows
you to change the database alias for a given class allowing quick and easy
access to the same User document across databases::
access the same User document across databases::
from mongoengine.context_managers import switch_db
class User(Document):
name = StringField()
meta = {'db_alias': 'user-db'}
meta = {"db_alias": "user-db"}
with switch_db(User, 'archive-user-db') as User:
User(name='Ross').save() # Saves the 'archive-user-db'
User(name="Ross").save() # Saves the 'archive-user-db'
Switch Collection
-----------------
The :class:`~mongoengine.context_managers.switch_collection` context manager
allows you to change the collection for a given class allowing quick and easy
access to the same Group document across collection::
access the same Group document across collection::
from mongoengine.context_managers import switch_collection
class Group(Document):
name = StringField()
Group(name='test').save() # Saves in the default db
Group(name="test").save() # Saves in the default db
with switch_collection(Group, 'group2000') as Group:
Group(name='hello Group 2000 collection!').save() # Saves in group2000 collection
Group(name="hello Group 2000 collection!").save() # Saves in group2000 collection
.. note:: Make sure any aliases have been registered with

View File

@@ -2,13 +2,13 @@
Installing MongoEngine
======================
To use MongoEngine, you will need to download `MongoDB <http://mongodb.com/>`_
To use MongoEngine, you will need to download `MongoDB <http://mongodb.org/>`_
and ensure it is running in an accessible location. You will also need
`PyMongo <http://api.mongodb.org/python>`_ to use MongoEngine, but if you
install MongoEngine using setuptools, then the dependencies will be handled for
you.
MongoEngine is available on PyPI, so you can use :program:`pip`:
MongoEngine is available on PyPI, so to use it you can use :program:`pip`:
.. code-block:: console

View File

@@ -340,19 +340,14 @@ Javascript code that is executed on the database server.
Counting results
----------------
Just as with limiting and skipping results, there is a method on a
:class:`~mongoengine.queryset.QuerySet` object --
:meth:`~mongoengine.queryset.QuerySet.count`::
Just as with limiting and skipping results, there is a method on
:class:`~mongoengine.queryset.QuerySet` objects --
:meth:`~mongoengine.queryset.QuerySet.count`, but there is also a more Pythonic
way of achieving this::
num_users = User.objects.count()
num_users = len(User.objects)
You could technically use ``len(User.objects)`` to get the same result, but it
would be significantly slower than :meth:`~mongoengine.queryset.QuerySet.count`.
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()
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
than :meth:`~mongoengine.queryset.QuerySet.count`.
Even if len() is the Pythonic way of counting results, keep in mind that if you concerned about performance, :meth:`~mongoengine.queryset.QuerySet.count` is the way to go since it only execute a server side count query, while len() retrieves the results, places them in cache, and finally counts them. If we compare the performance of the two operations, len() is much slower than :meth:`~mongoengine.queryset.QuerySet.count`.
Further aggregation
-------------------

View File

@@ -3,10 +3,11 @@ Tutorial
========
This tutorial introduces **MongoEngine** by means of example --- we will walk
through how to create a simple **Tumblelog** application. A tumblelog is a
blog that supports mixed media content, including text, images, links, video,
audio, etc. For simplicity's sake, we'll stick to text, image, and link
entries. As the purpose of this tutorial is to introduce MongoEngine, we'll
through how to create a simple **Tumblelog** application. A Tumblelog is a type
of blog where posts are not constrained to being conventional text-based posts.
As well as text-based entries, users may post images, links, videos, etc. For
simplicity's sake, we'll stick to text, image and link entries in our
application. As the purpose of this tutorial is to introduce MongoEngine, we'll
focus on the data-modelling side of the application, leaving out a user
interface.
@@ -15,14 +16,14 @@ Getting started
Before we start, make sure that a copy of MongoDB is running in an accessible
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::
$ pip install mongoengine
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`
function. If running locally, the only argument we need to provide is the name
function. If running locally the only argument we need to provide is the name
of the MongoDB database to use::
from mongoengine import *
@@ -38,18 +39,18 @@ Defining our documents
MongoDB is *schemaless*, which means that no schema is enforced by the database
--- we may add and remove fields however we want and MongoDB won't complain.
This makes life a lot easier in many regards, especially when there is a change
to the data model. However, defining schemas for our documents can help to iron
out bugs involving incorrect types or missing fields, and also allow us to
to the data model. However, defining schemata for our documents can help to
iron out bugs involving incorrect types or missing fields, and also allow us to
define utility methods on our documents in the same way that traditional
:abbr:`ORMs (Object-Relational Mappers)` do.
In our Tumblelog application we need to store several different types of
information. We will need to have a collection of **users**, so that we may
information. We will need to have a collection of **users**, so that we may
link posts to an individual. We also need to store our different types of
**posts** (eg: text, image and link) in the database. To aid navigation of our
Tumblelog, posts may have **tags** associated with them, so that the list of
posts shown to the user may be limited to posts that have been assigned a
specific tag. Finally, it would be nice if **comments** could be added to
specific tag. Finally, it would be nice if **comments** could be added to
posts. We'll start with **users**, as the other document models are slightly
more involved.
@@ -77,7 +78,7 @@ Now we'll think about how to store the rest of the information. If we were
using a relational database, we would most likely have a table of **posts**, a
table of **comments** and a table of **tags**. To associate the comments with
individual posts, we would put a column in the comments table that contained a
foreign key to the posts table. We'd also need a link table to provide the
foreign key to the posts table. We'd also need a link table to provide the
many-to-many relationship between posts and tags. Then we'd need to address the
problem of storing the specialised post-types (text, image and link). There are
several ways we can achieve this, but each of them have their problems --- none
@@ -95,7 +96,7 @@ using* the new fields we need to support video posts. This fits with the
Object-Oriented principle of *inheritance* nicely. We can think of
:class:`Post` as a base class, and :class:`TextPost`, :class:`ImagePost` and
:class:`LinkPost` as subclasses of :class:`Post`. In fact, MongoEngine supports
this kind of modeling out of the box --- all you need do is turn on inheritance
this kind of modelling out of the box --- all you need do is turn on inheritance
by setting :attr:`allow_inheritance` to True in the :attr:`meta`::
class Post(Document):
@@ -127,8 +128,8 @@ link table, we can just store a list of tags in each post. So, for both
efficiency and simplicity's sake, we'll store the tags as strings directly
within the post, rather than storing references to tags in a separate
collection. Especially as tags are generally very short (often even shorter
than a document's id), this denormalization won't impact the size of the
database very strongly. Let's take a look at the code of our modified
than a document's id), this denormalisation won't impact very strongly on the
size of our database. So let's take a look that the code our modified
:class:`Post` class::
class Post(Document):
@@ -140,7 +141,7 @@ The :class:`~mongoengine.fields.ListField` object that is used to define a Post'
takes a field object as its first argument --- this means that you can have
lists of any type of field (including lists).
.. note:: We don't need to modify the specialized post types as they all
.. note:: We don't need to modify the specialised post types as they all
inherit from :class:`Post`.
Comments
@@ -148,7 +149,7 @@ Comments
A comment is typically associated with *one* post. In a relational database, to
display a post with its comments, we would have to retrieve the post from the
database and then query the database again for the comments associated with the
database, then query the database again for the comments associated with the
post. This works, but there is no real reason to be storing the comments
separately from their associated posts, other than to work around the
relational model. Using MongoDB we can store the comments as a list of
@@ -218,8 +219,8 @@ Now that we've got our user in the database, let's add a couple of posts::
post2.tags = ['mongoengine']
post2.save()
.. note:: If you change a field on an object that has already been saved and
then call :meth:`save` again, the document will be updated.
.. note:: If you change a field on a object that has already been saved, then
call :meth:`save` again, the document will be updated.
Accessing our data
==================
@@ -231,17 +232,17 @@ used to access the documents in the database collection associated with that
class. So let's see how we can get our posts' titles::
for post in Post.objects:
print(post.title)
print post.title
Retrieving type-specific information
------------------------------------
This will print the titles of our posts, one on each line. But what if we want
This will print the titles of our posts, one on each line. But What if we want
to access the type-specific data (link_url, content, etc.)? One way is simply
to use the :attr:`objects` attribute of a subclass of :class:`Post`::
for post in TextPost.objects:
print(post.content)
print post.content
Using TextPost's :attr:`objects` attribute only returns documents that were
created using :class:`TextPost`. Actually, there is a more general rule here:
@@ -258,14 +259,16 @@ instances of :class:`Post` --- they were instances of the subclass of
practice::
for post in Post.objects:
print(post.title)
print('=' * len(post.title))
print post.title
print '=' * len(post.title)
if isinstance(post, TextPost):
print(post.content)
print post.content
if isinstance(post, LinkPost):
print('Link: {}'.format(post.link_url))
print 'Link:', post.link_url
print
This would print the title of each post, followed by the content if it was a
text post, and "Link: <url>" if it was a link post.
@@ -280,7 +283,7 @@ your query. Let's adjust our query so that only posts with the tag "mongodb"
are returned::
for post in Post.objects(tags='mongodb'):
print(post.title)
print post.title
There are also methods available on :class:`~mongoengine.queryset.QuerySet`
objects that allow different results to be returned, for example, calling
@@ -289,11 +292,11 @@ the first matched by the query you provide. Aggregation functions may also be
used on :class:`~mongoengine.queryset.QuerySet` objects::
num_posts = Post.objects(tags='mongodb').count()
print('Found {} posts with tag "mongodb"'.format(num_posts))
print 'Found %d posts with tag "mongodb"' % num_posts
Learning more about MongoEngine
Learning more about mongoengine
-------------------------------
If you got this far you've made a great start, so well done! The next step on
your MongoEngine journey is the `full user guide <guide/index.html>`_, where
you can learn in-depth about how to use MongoEngine and MongoDB.
If you got this far you've made a great start, so well done! The next step on
your mongoengine journey is the `full user guide <guide/index.html>`_, where you
can learn indepth about how to use mongoengine and mongodb.

View File

@@ -6,9 +6,6 @@ Development
***********
(Fill this out whenever you introduce breaking changes to MongoEngine)
0.12.0
******
This release includes various fixes for the `BaseQuerySet` methods and how they
are chained together. Since version 0.10.1 applying limit/skip/hint/batch_size
to an already-existing queryset wouldn't modify the underlying PyMongo cursor.

View File

@@ -23,7 +23,7 @@ __all__ = (list(document.__all__) + list(fields.__all__) +
list(signals.__all__) + list(errors.__all__))
VERSION = (0, 12, 0)
VERSION = (0, 11, 0)
def get_version():

View File

@@ -684,13 +684,8 @@ class BaseDocument(object):
# class if unavailable
class_name = son.get('_cls', cls._class_name)
# Convert SON to a data dict, making sure each key is a string and
# corresponds to the right db field.
data = {}
for key, value in son.iteritems():
key = str(key)
key = cls._db_field_map.get(key, key)
data[key] = value
# Convert SON to a dict, making sure each key is a string
data = {str(key): value for key, value in son.iteritems()}
# Return correct subclass for document type
if class_name != cls._class_name:

View File

@@ -1,4 +1,3 @@
from collections import OrderedDict
from bson import DBRef, SON
import six
@@ -202,10 +201,6 @@ class DeReference(object):
as_tuple = isinstance(items, tuple)
iterator = enumerate(items)
data = []
elif isinstance(items, OrderedDict):
is_list = False
iterator = items.iteritems()
data = OrderedDict()
else:
is_list = False
iterator = items.iteritems()

View File

@@ -5,7 +5,6 @@ import re
import time
import uuid
import warnings
from collections import Mapping
from operator import itemgetter
from bson import Binary, DBRef, ObjectId, SON
@@ -620,14 +619,6 @@ class DynamicField(BaseField):
Used by :class:`~mongoengine.DynamicDocument` to handle dynamic data"""
def __init__(self, container_class=dict, *args, **kwargs):
self._container_cls = container_class
if not issubclass(self._container_cls, Mapping):
self.error('The class that is specified in `container_class` parameter '
'must be a subclass of `dict`.')
super(DynamicField, self).__init__(*args, **kwargs)
def to_mongo(self, value, use_db_field=True, fields=None):
"""Convert a Python type to a MongoDB compatible type.
"""
@@ -653,7 +644,7 @@ class DynamicField(BaseField):
is_list = True
value = {k: v for k, v in enumerate(value)}
data = self._container_cls()
data = {}
for k, v in value.iteritems():
data[k] = self.to_mongo(v, use_db_field, fields)
@@ -1007,8 +998,8 @@ class ReferenceField(BaseField):
def validate(self, value):
if not isinstance(value, (self.document_type, DBRef, ObjectId)):
self.error('A ReferenceField only accepts DBRef, ObjectId or documents')
if not isinstance(value, (self.document_type, DBRef)):
self.error('A ReferenceField only accepts DBRef or documents')
if isinstance(value, Document) and value.id is None:
self.error('You can only reference documents once they have been '

View File

@@ -158,49 +158,44 @@ class BaseQuerySet(object):
# self._cursor
def __getitem__(self, key):
"""Return a document instance corresponding to a given index if
the key is an integer. If the key is a slice, translate its
bounds into a skip and a limit, and return a cloned queryset
with that skip/limit applied. For example:
>>> User.objects[0]
<User: User object>
>>> User.objects[1:3]
[<User: User object>, <User: User object>]
"""
"""Support skip and limit using getitem and slicing syntax."""
queryset = self.clone()
# Handle a slice
# Slice provided
if isinstance(key, slice):
queryset._cursor_obj = queryset._cursor[key]
queryset._skip, queryset._limit = key.start, key.stop
if key.start and key.stop:
queryset._limit = key.stop - key.start
try:
queryset._cursor_obj = queryset._cursor[key]
queryset._skip, queryset._limit = key.start, key.stop
if key.start and key.stop:
queryset._limit = key.stop - key.start
except IndexError as err:
# PyMongo raises an error if key.start == key.stop, catch it,
# bin it, kill it.
start = key.start or 0
if start >= 0 and key.stop >= 0 and key.step is None:
if start == key.stop:
queryset.limit(0)
queryset._skip = key.start
queryset._limit = key.stop - start
return queryset
raise err
# Allow further QuerySet modifications to be performed
return queryset
# Handle an index
# Integer index provided
elif isinstance(key, int):
if queryset._scalar:
return queryset._get_scalar(
queryset._document._from_son(
queryset._cursor[key],
_auto_dereference=self._auto_dereference,
only_fields=self.only_fields
)
)
queryset._document._from_son(queryset._cursor[key],
_auto_dereference=self._auto_dereference,
only_fields=self.only_fields))
if queryset._as_pymongo:
return queryset._get_as_pymongo(queryset._cursor[key])
return queryset._document._from_son(queryset._cursor[key],
_auto_dereference=self._auto_dereference,
only_fields=self.only_fields)
return queryset._document._from_son(
queryset._cursor[key],
_auto_dereference=self._auto_dereference,
only_fields=self.only_fields
)
raise AttributeError('Provide a slice or an integer index')
raise AttributeError
def __iter__(self):
raise NotImplementedError
@@ -291,7 +286,7 @@ class BaseQuerySet(object):
.. versionadded:: 0.4
"""
return self._document(**kwargs).save(force_insert=True)
return self._document(**kwargs).save()
def first(self):
"""Retrieve the first object matching the query."""

View File

@@ -412,6 +412,7 @@ class IndexesTest(unittest.TestCase):
User.ensure_indexes()
info = User.objects._collection.index_information()
self.assertEqual(sorted(info.keys()), ['_cls_1_user_guid_1', '_id_'])
User.drop_collection()
def test_embedded_document_index(self):
"""Tests settings an index on an embedded document
@@ -433,6 +434,7 @@ class IndexesTest(unittest.TestCase):
info = BlogPost.objects._collection.index_information()
self.assertEqual(sorted(info.keys()), ['_id_', 'date.yr_-1'])
BlogPost.drop_collection()
def test_list_embedded_document_index(self):
"""Ensure list embedded documents can be indexed
@@ -459,6 +461,7 @@ class IndexesTest(unittest.TestCase):
post1 = BlogPost(title="Embedded Indexes tests in place",
tags=[Tag(name="about"), Tag(name="time")])
post1.save()
BlogPost.drop_collection()
def test_recursive_embedded_objects_dont_break_indexes(self):
@@ -619,6 +622,8 @@ class IndexesTest(unittest.TestCase):
post3 = BlogPost(title='test3', date=Date(year=2010), slug='test')
self.assertRaises(OperationError, post3.save)
BlogPost.drop_collection()
def test_unique_embedded_document(self):
"""Ensure that uniqueness constraints are applied to fields on embedded documents.
"""
@@ -646,6 +651,8 @@ class IndexesTest(unittest.TestCase):
sub=SubDocument(year=2010, slug='test'))
self.assertRaises(NotUniqueError, post3.save)
BlogPost.drop_collection()
def test_unique_embedded_document_in_list(self):
"""
Ensure that the uniqueness constraints are applied to fields in
@@ -676,6 +683,8 @@ class IndexesTest(unittest.TestCase):
self.assertRaises(NotUniqueError, post2.save)
BlogPost.drop_collection()
def test_unique_with_embedded_document_and_embedded_unique(self):
"""Ensure that uniqueness constraints are applied to fields on
embedded documents. And work with unique_with as well.
@@ -709,6 +718,8 @@ class IndexesTest(unittest.TestCase):
sub=SubDocument(year=2009, slug='test-1'))
self.assertRaises(NotUniqueError, post3.save)
BlogPost.drop_collection()
def test_ttl_indexes(self):
class Log(Document):
@@ -748,11 +759,13 @@ class IndexesTest(unittest.TestCase):
raise AssertionError("We saved a dupe!")
except NotUniqueError:
pass
Customer.drop_collection()
def test_unique_and_primary(self):
"""If you set a field as primary, then unexpected behaviour can occur.
You won't create a duplicate but you will update an existing document.
"""
class User(Document):
name = StringField(primary_key=True, unique=True)
password = StringField()
@@ -768,23 +781,8 @@ class IndexesTest(unittest.TestCase):
self.assertEqual(User.objects.count(), 1)
self.assertEqual(User.objects.get().password, 'secret2')
def test_unique_and_primary_create(self):
"""Create a new record with a duplicate primary key
throws an exception
"""
class User(Document):
name = StringField(primary_key=True)
password = StringField()
User.drop_collection()
User.objects.create(name='huangz', password='secret')
with self.assertRaises(NotUniqueError):
User.objects.create(name='huangz', password='secret2')
self.assertEqual(User.objects.count(), 1)
self.assertEqual(User.objects.get().password, 'secret')
def test_index_with_pk(self):
"""Ensure you can use `pk` as part of a query"""

View File

@@ -28,6 +28,8 @@ TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__),
__all__ = ("InstanceTest",)
class InstanceTest(unittest.TestCase):
def setUp(self):
@@ -70,7 +72,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(field._instance, instance)
def test_capped_collection(self):
"""Ensure that capped collections work properly."""
"""Ensure that capped collections work properly.
"""
class Log(Document):
date = DateTimeField(default=datetime.now)
meta = {
@@ -178,7 +181,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual('<Article: привет мир>', repr(doc))
def test_repr_none(self):
"""Ensure None values are handled correctly."""
"""Ensure None values handled correctly
"""
class Article(Document):
title = StringField()
@@ -186,23 +190,25 @@ class InstanceTest(unittest.TestCase):
return None
doc = Article(title=u'привет мир')
self.assertEqual('<Article: None>', repr(doc))
def test_queryset_resurrects_dropped_collection(self):
self.Person.drop_collection()
self.assertEqual([], list(self.Person.objects()))
# Ensure works correctly with inhertited classes
class Actor(self.Person):
pass
# Ensure works correctly with inhertited classes
Actor.objects()
self.Person.drop_collection()
self.assertEqual([], list(Actor.objects()))
def test_polymorphic_references(self):
"""Ensure that the correct subclasses are returned from a query
when using references / generic references
"""Ensure that the correct subclasses are returned from a query when
using references / generic references
"""
class Animal(Document):
meta = {'allow_inheritance': True}
@@ -252,6 +258,9 @@ class InstanceTest(unittest.TestCase):
classes = [a.__class__ for a in Zoo.objects.first().animals]
self.assertEqual(classes, [Animal, Fish, Mammal, Dog, Human])
Zoo.drop_collection()
Animal.drop_collection()
def test_reference_inheritance(self):
class Stats(Document):
created = DateTimeField(default=datetime.now)
@@ -278,7 +287,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(list_stats, CompareStats.objects.first().stats)
def test_db_field_load(self):
"""Ensure we load data correctly from the right db field."""
"""Ensure we load data correctly
"""
class Person(Document):
name = StringField(required=True)
_rank = StringField(required=False, db_field="rank")
@@ -297,7 +307,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Person.objects.get(name="Fred").rank, "Private")
def test_db_embedded_doc_field_load(self):
"""Ensure we load embedded document data correctly."""
"""Ensure we load embedded document data correctly
"""
class Rank(EmbeddedDocument):
title = StringField(required=True)
@@ -322,7 +333,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Person.objects.get(name="Fred").rank, "Private")
def test_custom_id_field(self):
"""Ensure that documents may be created with custom primary keys."""
"""Ensure that documents may be created with custom primary keys.
"""
class User(Document):
username = StringField(primary_key=True)
name = StringField()
@@ -370,7 +382,10 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(user_son['_id'], 'mongo')
self.assertTrue('username' not in user_son['_id'])
User.drop_collection()
def test_document_not_registered(self):
class Place(Document):
name = StringField()
@@ -392,6 +407,7 @@ class InstanceTest(unittest.TestCase):
list(Place.objects.all())
def test_document_registry_regressions(self):
class Location(Document):
name = StringField()
meta = {'allow_inheritance': True}
@@ -405,16 +421,18 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Area, get_document("Location.Area"))
def test_creation(self):
"""Ensure that document may be created using keyword arguments."""
"""Ensure that document may be created using keyword arguments.
"""
person = self.Person(name="Test User", age=30)
self.assertEqual(person.name, "Test User")
self.assertEqual(person.age, 30)
def test_to_dbref(self):
"""Ensure that you can get a dbref of a document."""
"""Ensure that you can get a dbref of a document"""
person = self.Person(name="Test User", age=30)
self.assertRaises(OperationError, person.to_dbref)
person.save()
person.to_dbref()
def test_save_abstract_document(self):
@@ -427,7 +445,8 @@ class InstanceTest(unittest.TestCase):
Doc(name='aaa').save()
def test_reload(self):
"""Ensure that attributes may be reloaded."""
"""Ensure that attributes may be reloaded.
"""
person = self.Person(name="Test User", age=20)
person.save()
@@ -460,6 +479,7 @@ class InstanceTest(unittest.TestCase):
doc = Animal(superphylum='Deuterostomia')
doc.save()
doc.reload()
Animal.drop_collection()
def test_reload_sharded_nested(self):
class SuperPhylum(EmbeddedDocument):
@@ -473,9 +493,11 @@ class InstanceTest(unittest.TestCase):
doc = Animal(superphylum=SuperPhylum(name='Deuterostomia'))
doc.save()
doc.reload()
Animal.drop_collection()
def test_reload_referencing(self):
"""Ensures reloading updates weakrefs correctly."""
"""Ensures reloading updates weakrefs correctly
"""
class Embedded(EmbeddedDocument):
dict_field = DictField()
list_field = ListField()
@@ -547,7 +569,8 @@ class InstanceTest(unittest.TestCase):
self.assertFalse("Threw wrong exception")
def test_reload_of_non_strict_with_special_field_name(self):
"""Ensures reloading works for documents with meta strict == False."""
"""Ensures reloading works for documents with meta strict == False
"""
class Post(Document):
meta = {
'strict': False
@@ -568,7 +591,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(post.items, ["more lorem", "even more ipsum"])
def test_dictionary_access(self):
"""Ensure that dictionary-style field access works properly."""
"""Ensure that dictionary-style field access works properly.
"""
person = self.Person(name='Test User', age=30, job=self.Job())
self.assertEqual(person['name'], 'Test User')
@@ -610,7 +634,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(sub_doc.to_mongo().keys(), ['id'])
def test_embedded_document(self):
"""Ensure that embedded documents are set up correctly."""
"""Ensure that embedded documents are set up correctly.
"""
class Comment(EmbeddedDocument):
content = StringField()
@@ -618,7 +643,8 @@ class InstanceTest(unittest.TestCase):
self.assertFalse('id' in Comment._fields)
def test_embedded_document_instance(self):
"""Ensure that embedded documents can reference parent instance."""
"""Ensure that embedded documents can reference parent instance
"""
class Embedded(EmbeddedDocument):
string = StringField()
@@ -626,7 +652,6 @@ class InstanceTest(unittest.TestCase):
embedded_field = EmbeddedDocumentField(Embedded)
Doc.drop_collection()
doc = Doc(embedded_field=Embedded(string="Hi"))
self.assertHasInstance(doc.embedded_field, doc)
@@ -636,8 +661,7 @@ class InstanceTest(unittest.TestCase):
def test_embedded_document_complex_instance(self):
"""Ensure that embedded documents in complex fields can reference
parent instance.
"""
parent instance"""
class Embedded(EmbeddedDocument):
string = StringField()
@@ -653,7 +677,8 @@ class InstanceTest(unittest.TestCase):
self.assertHasInstance(doc.embedded_field[0], doc)
def test_embedded_document_complex_instance_no_use_db_field(self):
"""Ensure that use_db_field is propagated to list of Emb Docs."""
"""Ensure that use_db_field is propagated to list of Emb Docs
"""
class Embedded(EmbeddedDocument):
string = StringField(db_field='s')
@@ -665,6 +690,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(d['embedded_field'], [{'string': 'Hi'}])
def test_instance_is_set_on_setattr(self):
class Email(EmbeddedDocument):
email = EmailField()
@@ -672,7 +698,6 @@ class InstanceTest(unittest.TestCase):
email = EmbeddedDocumentField(Email)
Account.drop_collection()
acc = Account()
acc.email = Email(email='test@example.com')
self.assertHasInstance(acc._data["email"], acc)
@@ -682,6 +707,7 @@ class InstanceTest(unittest.TestCase):
self.assertHasInstance(acc1._data["email"], acc1)
def test_instance_is_set_on_setattr_on_embedded_document_list(self):
class Email(EmbeddedDocument):
email = EmailField()
@@ -827,28 +853,32 @@ class InstanceTest(unittest.TestCase):
self.assertDbEqual([dict(other_doc.to_mongo()), dict(doc.to_mongo())])
def test_save(self):
"""Ensure that a document may be saved in the database."""
"""Ensure that a document may be saved in the database.
"""
# Create person object and save it to the database
person = self.Person(name='Test User', age=30)
person.save()
# Ensure that the object is in the database
collection = self.db[self.Person._get_collection_name()]
person_obj = collection.find_one({'name': 'Test User'})
self.assertEqual(person_obj['name'], 'Test User')
self.assertEqual(person_obj['age'], 30)
self.assertEqual(person_obj['_id'], person.id)
# Test skipping validation on save
class Recipient(Document):
email = EmailField(required=True)
recipient = Recipient(email='root@localhost')
self.assertRaises(ValidationError, recipient.save)
recipient.save(validate=False)
try:
recipient.save(validate=False)
except ValidationError:
self.fail()
def test_save_to_a_value_that_equates_to_false(self):
class Thing(EmbeddedDocument):
count = IntField()
@@ -868,6 +898,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(user.thing.count, 0)
def test_save_max_recursion_not_hit(self):
class Person(Document):
name = StringField()
parent = ReferenceField('self')
@@ -893,6 +924,7 @@ class InstanceTest(unittest.TestCase):
p0.save()
def test_save_max_recursion_not_hit_with_file_field(self):
class Foo(Document):
name = StringField()
picture = FileField()
@@ -916,6 +948,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(b.picture, b.bar.picture, b.bar.bar.picture)
def test_save_cascades(self):
class Person(Document):
name = StringField()
parent = ReferenceField('self')
@@ -938,6 +971,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(p1.name, p.parent.name)
def test_save_cascade_kwargs(self):
class Person(Document):
name = StringField()
parent = ReferenceField('self')
@@ -958,6 +992,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(p1.name, p2.parent.name)
def test_save_cascade_meta_false(self):
class Person(Document):
name = StringField()
parent = ReferenceField('self')
@@ -986,6 +1021,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(p1.name, p.parent.name)
def test_save_cascade_meta_true(self):
class Person(Document):
name = StringField()
parent = ReferenceField('self')
@@ -1010,6 +1046,7 @@ class InstanceTest(unittest.TestCase):
self.assertNotEqual(p1.name, p.parent.name)
def test_save_cascades_generically(self):
class Person(Document):
name = StringField()
parent = GenericReferenceField()
@@ -1035,6 +1072,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(p1.name, p.parent.name)
def test_save_atomicity_condition(self):
class Widget(Document):
toggle = BooleanField(default=False)
count = IntField(default=0)
@@ -1112,8 +1150,7 @@ class InstanceTest(unittest.TestCase):
def test_update(self):
"""Ensure that an existing document is updated instead of be
overwritten.
"""
overwritten."""
# Create person object and save it to the database
person = self.Person(name='Test User', age=30)
person.save()
@@ -1217,6 +1254,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(2, self.Person.objects.count())
def test_can_save_if_not_included(self):
class EmbeddedDoc(EmbeddedDocument):
pass
@@ -1303,7 +1341,10 @@ class InstanceTest(unittest.TestCase):
doc2.update(set__name=doc1.name)
def test_embedded_update(self):
"""Test update on `EmbeddedDocumentField` fields."""
"""
Test update on `EmbeddedDocumentField` fields
"""
class Page(EmbeddedDocument):
log_message = StringField(verbose_name="Log message",
required=True)
@@ -1324,9 +1365,11 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(site.page.log_message, "Error: Dummy message")
def test_embedded_update_db_field(self):
"""Test update on `EmbeddedDocumentField` fields when db_field
is other than default.
"""
Test update on `EmbeddedDocumentField` fields when db_field is other
than default.
"""
class Page(EmbeddedDocument):
log_message = StringField(verbose_name="Log message",
db_field="page_log_message",
@@ -1349,7 +1392,9 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(site.page.log_message, "Error: Dummy message")
def test_save_only_changed_fields(self):
"""Ensure save only sets / unsets changed fields."""
"""Ensure save only sets / unsets changed fields
"""
class User(self.Person):
active = BooleanField(default=True)
@@ -1469,8 +1514,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(q, 3)
def test_set_unset_one_operation(self):
"""Ensure that $set and $unset actions are performed in the
same operation.
"""Ensure that $set and $unset actions are performed in the same
operation.
"""
class FooBar(Document):
foo = StringField(default=None)
@@ -1491,7 +1536,9 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(1, q)
def test_save_only_changed_fields_recursive(self):
"""Ensure save only sets / unsets changed fields."""
"""Ensure save only sets / unsets changed fields
"""
class Comment(EmbeddedDocument):
published = BooleanField(default=True)
@@ -1531,7 +1578,8 @@ class InstanceTest(unittest.TestCase):
self.assertFalse(person.comments_dict['first_post'].published)
def test_delete(self):
"""Ensure that document may be deleted using the delete method."""
"""Ensure that document may be deleted using the delete method.
"""
person = self.Person(name="Test User", age=30)
person.save()
self.assertEqual(self.Person.objects.count(), 1)
@@ -1539,34 +1587,33 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(self.Person.objects.count(), 0)
def test_save_custom_id(self):
"""Ensure that a document may be saved with a custom _id."""
"""Ensure that a document may be saved with a custom _id.
"""
# Create person object and save it to the database
person = self.Person(name='Test User', age=30,
id='497ce96f395f2f052a494fd4')
person.save()
# Ensure that the object is in the database with the correct _id
collection = self.db[self.Person._get_collection_name()]
person_obj = collection.find_one({'name': 'Test User'})
self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4')
def test_save_custom_pk(self):
"""Ensure that a document may be saved with a custom _id using
pk alias.
"""
Ensure that a document may be saved with a custom _id using pk alias.
"""
# Create person object and save it to the database
person = self.Person(name='Test User', age=30,
pk='497ce96f395f2f052a494fd4')
person.save()
# Ensure that the object is in the database with the correct _id
collection = self.db[self.Person._get_collection_name()]
person_obj = collection.find_one({'name': 'Test User'})
self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4')
def test_save_list(self):
"""Ensure that a list field may be properly saved."""
"""Ensure that a list field may be properly saved.
"""
class Comment(EmbeddedDocument):
content = StringField()
@@ -1589,6 +1636,8 @@ class InstanceTest(unittest.TestCase):
for comment_obj, comment in zip(post_obj['comments'], comments):
self.assertEqual(comment_obj['content'], comment['content'])
BlogPost.drop_collection()
def test_list_search_by_embedded(self):
class User(Document):
username = StringField(required=True)
@@ -1648,8 +1697,8 @@ class InstanceTest(unittest.TestCase):
list(Page.objects.filter(comments__user=u3)))
def test_save_embedded_document(self):
"""Ensure that a document with an embedded document field may
be saved in the database.
"""Ensure that a document with an embedded document field may be
saved in the database.
"""
class EmployeeDetails(EmbeddedDocument):
position = StringField()
@@ -1668,13 +1717,13 @@ class InstanceTest(unittest.TestCase):
employee_obj = collection.find_one({'name': 'Test Employee'})
self.assertEqual(employee_obj['name'], 'Test Employee')
self.assertEqual(employee_obj['age'], 50)
# Ensure that the 'details' embedded object saved correctly
self.assertEqual(employee_obj['details']['position'], 'Developer')
def test_embedded_update_after_save(self):
"""Test update of `EmbeddedDocumentField` attached to a newly
saved document.
"""
Test update of `EmbeddedDocumentField` attached to a newly saved
document.
"""
class Page(EmbeddedDocument):
log_message = StringField(verbose_name="Log message",
@@ -1695,8 +1744,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(site.page.log_message, "Error: Dummy message")
def test_updating_an_embedded_document(self):
"""Ensure that a document with an embedded document field may
be saved in the database.
"""Ensure that a document with an embedded document field may be
saved in the database.
"""
class EmployeeDetails(EmbeddedDocument):
position = StringField()
@@ -1731,6 +1780,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(promoted_employee.details, None)
def test_object_mixins(self):
class NameMixin(object):
name = StringField()
@@ -1769,9 +1819,9 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(t.count, 12)
def test_save_reference(self):
"""Ensure that a document reference field may be saved in the
database.
"""Ensure that a document reference field may be saved in the database.
"""
class BlogPost(Document):
meta = {'collection': 'blogpost_1'}
content = StringField()
@@ -1802,6 +1852,8 @@ class InstanceTest(unittest.TestCase):
author = list(self.Person.objects(name='Test User'))[-1]
self.assertEqual(author.age, 25)
BlogPost.drop_collection()
def test_duplicate_db_fields_raise_invalid_document_error(self):
"""Ensure a InvalidDocumentError is thrown if duplicate fields
declare the same db_field.
@@ -1812,7 +1864,7 @@ class InstanceTest(unittest.TestCase):
name2 = StringField(db_field='name')
def test_invalid_son(self):
"""Raise an error if loading invalid data."""
"""Raise an error if loading invalid data"""
class Occurrence(EmbeddedDocument):
number = IntField()
@@ -1835,9 +1887,9 @@ class InstanceTest(unittest.TestCase):
Word._from_son('this is not a valid SON dict')
def test_reverse_delete_rule_cascade_and_nullify(self):
"""Ensure that a referenced document is also deleted upon
deletion.
"""Ensure that a referenced document is also deleted upon deletion.
"""
class BlogPost(Document):
content = StringField()
author = ReferenceField(self.Person, reverse_delete_rule=CASCADE)
@@ -1892,8 +1944,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Book.objects.count(), 0)
def test_reverse_delete_rule_with_shared_id_among_collections(self):
"""Ensure that cascade delete rule doesn't mix id among
collections.
"""Ensure that cascade delete rule doesn't mix id among collections.
"""
class User(Document):
id = IntField(primary_key=True)
@@ -1924,9 +1975,10 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Book.objects.get(), book_2)
def test_reverse_delete_rule_with_document_inheritance(self):
"""Ensure that a referenced document is also deleted upon
deletion of a child document.
"""Ensure that a referenced document is also deleted upon deletion
of a child document.
"""
class Writer(self.Person):
pass
@@ -1958,9 +2010,10 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(BlogPost.objects.count(), 0)
def test_reverse_delete_rule_cascade_and_nullify_complex_field(self):
"""Ensure that a referenced document is also deleted upon
deletion for complex fields.
"""Ensure that a referenced document is also deleted upon deletion for
complex fields.
"""
class BlogPost(Document):
content = StringField()
authors = ListField(ReferenceField(
@@ -1969,6 +2022,7 @@ class InstanceTest(unittest.TestCase):
self.Person, reverse_delete_rule=NULLIFY))
self.Person.drop_collection()
BlogPost.drop_collection()
author = self.Person(name='Test User')
@@ -1992,10 +2046,10 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(BlogPost.objects.count(), 0)
def test_reverse_delete_rule_cascade_triggers_pre_delete_signal(self):
"""Ensure the pre_delete signal is triggered upon a cascading
deletion setup a blog post with content, an author and editor
delete the author which triggers deletion of blogpost via
cascade blog post's pre_delete signal alters an editor attribute.
""" ensure the pre_delete signal is triggered upon a cascading deletion
setup a blog post with content, an author and editor
delete the author which triggers deletion of blogpost via cascade
blog post's pre_delete signal alters an editor attribute
"""
class Editor(self.Person):
review_queue = IntField(default=0)
@@ -2023,7 +2077,6 @@ class InstanceTest(unittest.TestCase):
# delete the author, the post is also deleted due to the CASCADE rule
author.delete()
# the pre-delete signal should have decremented the editor's queue
editor = Editor.objects(name='Max P.').get()
self.assertEqual(editor.review_queue, 0)
@@ -2032,6 +2085,7 @@ class InstanceTest(unittest.TestCase):
"""Ensure that Bi-Directional relationships work with
reverse_delete_rule
"""
class Bar(Document):
content = StringField()
foo = ReferenceField('Foo')
@@ -2077,8 +2131,8 @@ class InstanceTest(unittest.TestCase):
mother = ReferenceField('Person', reverse_delete_rule=DENY)
def test_reverse_delete_rule_cascade_recurs(self):
"""Ensure that a chain of documents is also deleted upon
cascaded deletion.
"""Ensure that a chain of documents is also deleted upon cascaded
deletion.
"""
class BlogPost(Document):
content = StringField()
@@ -2108,10 +2162,15 @@ class InstanceTest(unittest.TestCase):
author.delete()
self.assertEqual(Comment.objects.count(), 0)
self.Person.drop_collection()
BlogPost.drop_collection()
Comment.drop_collection()
def test_reverse_delete_rule_deny(self):
"""Ensure that a document cannot be referenced if there are
still documents referring to it.
"""Ensure that a document cannot be referenced if there are still
documents referring to it.
"""
class BlogPost(Document):
content = StringField()
author = ReferenceField(self.Person, reverse_delete_rule=DENY)
@@ -2139,7 +2198,11 @@ class InstanceTest(unittest.TestCase):
author.delete()
self.assertEqual(self.Person.objects.count(), 1)
self.Person.drop_collection()
BlogPost.drop_collection()
def subclasses_and_unique_keys_works(self):
class A(Document):
pass
@@ -2155,9 +2218,12 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(A.objects.count(), 2)
self.assertEqual(B.objects.count(), 1)
A.drop_collection()
B.drop_collection()
def test_document_hash(self):
"""Test document in list, dict, set."""
"""Test document in list, dict, set
"""
class User(Document):
pass
@@ -2200,9 +2266,11 @@ class InstanceTest(unittest.TestCase):
# in Set
all_user_set = set(User.objects.all())
self.assertTrue(u1 in all_user_set)
def test_picklable(self):
pickle_doc = PickleTest(number=1, string="One", lists=['1', '2'])
pickle_doc.embedded = PickleEmbedded()
pickled_doc = pickle.dumps(pickle_doc) # make sure pickling works even before the doc is saved
@@ -2228,6 +2296,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(pickle_doc.lists, ["1", "2", "3"])
def test_regular_document_pickle(self):
pickle_doc = PickleTest(number=1, string="One", lists=['1', '2'])
pickled_doc = pickle.dumps(pickle_doc) # make sure pickling works even before the doc is saved
pickle_doc.save()
@@ -2250,6 +2319,7 @@ class InstanceTest(unittest.TestCase):
fixtures.PickleTest = PickleTest
def test_dynamic_document_pickle(self):
pickle_doc = PickleDynamicTest(
name="test", number=1, string="One", lists=['1', '2'])
pickle_doc.embedded = PickleDynamicEmbedded(foo="Bar")
@@ -2288,6 +2358,7 @@ class InstanceTest(unittest.TestCase):
validate = DictField()
def test_mutating_documents(self):
class B(EmbeddedDocument):
field1 = StringField(default='field1')
@@ -2295,7 +2366,6 @@ class InstanceTest(unittest.TestCase):
b = EmbeddedDocumentField(B, default=lambda: B())
A.drop_collection()
a = A()
a.save()
a.reload()
@@ -2319,13 +2389,12 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(a.b.field2.c_field, 'new value')
def test_can_save_false_values(self):
"""Ensures you can save False values on save."""
"""Ensures you can save False values on save"""
class Doc(Document):
foo = StringField()
archived = BooleanField(default=False, required=True)
Doc.drop_collection()
d = Doc()
d.save()
d.archived = False
@@ -2334,12 +2403,11 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Doc.objects(archived=False).count(), 1)
def test_can_save_false_values_dynamic(self):
"""Ensures you can save False values on dynamic docs."""
"""Ensures you can save False values on dynamic docs"""
class Doc(DynamicDocument):
foo = StringField()
Doc.drop_collection()
d = Doc()
d.save()
d.archived = False
@@ -2379,7 +2447,7 @@ class InstanceTest(unittest.TestCase):
Collection.update = orig_update
def test_db_alias_tests(self):
"""DB Alias tests."""
""" DB Alias tests """
# mongoenginetest - Is default connection alias from setUp()
# Register Aliases
register_connection('testdb-1', 'mongoenginetest2')
@@ -2441,7 +2509,8 @@ class InstanceTest(unittest.TestCase):
get_db("testdb-3")[AuthorBooks._get_collection_name()])
def test_db_alias_overrides(self):
"""Test db_alias can be overriden."""
"""db_alias can be overriden
"""
# Register a connection with db_alias testdb-2
register_connection('testdb-2', 'mongoenginetest2')
@@ -2465,7 +2534,8 @@ class InstanceTest(unittest.TestCase):
B._get_collection().database.name)
def test_db_alias_propagates(self):
"""db_alias propagates?"""
"""db_alias propagates?
"""
register_connection('testdb-1', 'mongoenginetest2')
class A(Document):
@@ -2478,7 +2548,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual('testdb-1', B._meta.get('db_alias'))
def test_db_ref_usage(self):
"""DB Ref usage in dict_fields."""
""" DB Ref usage in dict_fields"""
class User(Document):
name = StringField()
@@ -2713,6 +2784,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(user.thing._data['data'], [1, 2, 3])
def test_spaces_in_keys(self):
class Embedded(DynamicEmbeddedDocument):
pass
@@ -2801,6 +2873,7 @@ class InstanceTest(unittest.TestCase):
log.machine = "127.0.0.1"
def test_kwargs_simple(self):
class Embedded(EmbeddedDocument):
name = StringField()
@@ -2820,6 +2893,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(classic_doc._data, dict_doc._data)
def test_kwargs_complex(self):
class Embedded(EmbeddedDocument):
name = StringField()
@@ -2842,35 +2916,36 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(classic_doc._data, dict_doc._data)
def test_positional_creation(self):
"""Ensure that document may be created using positional arguments."""
"""Ensure that document may be created using positional arguments.
"""
person = self.Person("Test User", 42)
self.assertEqual(person.name, "Test User")
self.assertEqual(person.age, 42)
def test_mixed_creation(self):
"""Ensure that document may be created using mixed arguments."""
"""Ensure that document may be created using mixed arguments.
"""
person = self.Person("Test User", age=42)
self.assertEqual(person.name, "Test User")
self.assertEqual(person.age, 42)
def test_positional_creation_embedded(self):
"""Ensure that embedded document may be created using positional
arguments.
"""Ensure that embedded document may be created using positional arguments.
"""
job = self.Job("Test Job", 4)
self.assertEqual(job.name, "Test Job")
self.assertEqual(job.years, 4)
def test_mixed_creation_embedded(self):
"""Ensure that embedded document may be created using mixed
arguments.
"""Ensure that embedded document may be created using mixed arguments.
"""
job = self.Job("Test Job", years=4)
self.assertEqual(job.name, "Test Job")
self.assertEqual(job.years, 4)
def test_mixed_creation_dynamic(self):
"""Ensure that document may be created using mixed arguments."""
"""Ensure that document may be created using mixed arguments.
"""
class Person(DynamicDocument):
name = StringField()
@@ -2879,14 +2954,14 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(person.age, 42)
def test_bad_mixed_creation(self):
"""Ensure that document gives correct error when duplicating
arguments.
"""Ensure that document gives correct error when duplicating arguments
"""
with self.assertRaises(TypeError):
return self.Person("Test User", 42, name="Bad User")
def test_data_contains_id_field(self):
"""Ensure that asking for _data returns 'id'."""
"""Ensure that asking for _data returns 'id'
"""
class Person(Document):
name = StringField()
@@ -2898,6 +2973,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(person._data.get('id'), person.id)
def test_complex_nesting_document_and_embedded_document(self):
class Macro(EmbeddedDocument):
value = DynamicField(default="UNDEFINED")
@@ -2940,6 +3016,7 @@ class InstanceTest(unittest.TestCase):
system.nodes["node"].parameters["param"].macros["test"].value)
def test_embedded_document_equality(self):
class Test(Document):
field = StringField(required=True)
@@ -3125,7 +3202,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(idx, 2)
def test_falsey_pk(self):
"""Ensure that we can create and update a document with Falsey PK."""
"""Ensure that we can create and update a document with Falsey PK.
"""
class Person(Document):
age = IntField(primary_key=True)
height = FloatField()

File diff suppressed because it is too large Load Diff

View File

@@ -4962,6 +4962,20 @@ class QuerySetTest(unittest.TestCase):
for p in Person.objects():
self.assertEqual(p.name, 'a')
def test_last_field_name_like_operator(self):
class EmbeddedItem(EmbeddedDocument):
type = StringField()
class Doc(Document):
item = EmbeddedDocumentField(EmbeddedItem)
Doc.drop_collection()
doc = Doc(item=EmbeddedItem(type="axe"))
doc.save()
self.assertEqual(1, Doc.objects(item__type__="axe").count())
def test_len_during_iteration(self):
"""Tests that calling len on a queyset during iteration doesn't
stop paging.

View File

@@ -198,6 +198,19 @@ class ConnectionTest(unittest.TestCase):
self.assertTrue(isinstance(db, pymongo.database.Database))
self.assertEqual(db.name, 'test')
def test_connect_uri_with_replicaset(self):
"""Ensure connect() works when specifying a replicaSet."""
if IS_PYMONGO_3:
c = connect(host='mongodb://localhost/test?replicaSet=local-rs')
db = get_db()
self.assertTrue(isinstance(db, pymongo.database.Database))
self.assertEqual(db.name, 'test')
else:
# PyMongo < v3.x raises an exception:
# "localhost:27017 is not a member of replica set local-rs"
with self.assertRaises(MongoEngineConnectionError):
c = connect(host='mongodb://localhost/test?replicaSet=local-rs')
def test_uri_without_credentials_doesnt_override_conn_settings(self):
"""Ensure connect() uses the username & password params if the URI
doesn't explicitly specify them.
@@ -319,38 +332,6 @@ class ConnectionTest(unittest.TestCase):
self.assertEqual(dict(conn1.write_concern), {'w': 1, 'j': True})
self.assertEqual(dict(conn2.write_concern), {'w': 1, 'j': True})
def test_connect_with_replicaset_via_uri(self):
"""Ensure connect() works when specifying a replicaSet via the
MongoDB URI.
"""
if IS_PYMONGO_3:
c = connect(host='mongodb://localhost/test?replicaSet=local-rs')
db = get_db()
self.assertTrue(isinstance(db, pymongo.database.Database))
self.assertEqual(db.name, 'test')
else:
# PyMongo < v3.x raises an exception:
# "localhost:27017 is not a member of replica set local-rs"
with self.assertRaises(MongoEngineConnectionError):
c = connect(host='mongodb://localhost/test?replicaSet=local-rs')
def test_connect_with_replicaset_via_kwargs(self):
"""Ensure connect() works when specifying a replicaSet via the
connection kwargs
"""
if IS_PYMONGO_3:
c = connect(replicaset='local-rs')
self.assertEqual(c._MongoClient__options.replica_set_name,
'local-rs')
db = get_db()
self.assertTrue(isinstance(db, pymongo.database.Database))
self.assertEqual(db.name, 'test')
else:
# PyMongo < v3.x raises an exception:
# "localhost:27017 is not a member of replica set local-rs"
with self.assertRaises(MongoEngineConnectionError):
c = connect(replicaset='local-rs')
def test_datetime(self):
connect('mongoenginetest', tz_aware=True)
d = datetime.datetime(2010, 5, 5, tzinfo=utc)

View File

@@ -2,15 +2,10 @@
import unittest
from bson import DBRef, ObjectId
from collections import OrderedDict
from mongoengine import *
from mongoengine.connection import get_db
from mongoengine.context_managers import query_counter
from mongoengine.python_support import IS_PYMONGO_3
from mongoengine.base import TopLevelDocumentMetaclass
if IS_PYMONGO_3:
from bson import CodecOptions
class FieldTest(unittest.TestCase):
@@ -1292,70 +1287,5 @@ class FieldTest(unittest.TestCase):
self.assertEqual(q, 2)
def test_dynamic_field_dereference(self):
class Merchandise(Document):
name = StringField()
price = IntField()
class Store(Document):
merchandises = DynamicField()
Merchandise.drop_collection()
Store.drop_collection()
merchandises = {
'#1': Merchandise(name='foo', price=100).save(),
'#2': Merchandise(name='bar', price=120).save(),
'#3': Merchandise(name='baz', price=110).save(),
}
Store(merchandises=merchandises).save()
store = Store.objects().first()
for obj in store.merchandises.values():
self.assertFalse(isinstance(obj, Merchandise))
store.select_related()
for obj in store.merchandises.values():
self.assertTrue(isinstance(obj, Merchandise))
def test_dynamic_field_dereference_with_ordering_guarantee_on_pymongo3(self):
# This is because 'codec_options' is supported on pymongo3 or later
if IS_PYMONGO_3:
class OrderedDocument(Document):
my_metaclass = TopLevelDocumentMetaclass
__metaclass__ = TopLevelDocumentMetaclass
@classmethod
def _get_collection(cls):
collection = super(OrderedDocument, cls)._get_collection()
opts = CodecOptions(document_class=OrderedDict)
return collection.with_options(codec_options=opts)
class Merchandise(Document):
name = StringField()
price = IntField()
class Store(OrderedDocument):
merchandises = DynamicField(container_class=OrderedDict)
Merchandise.drop_collection()
Store.drop_collection()
merchandises = OrderedDict()
merchandises['#1'] = Merchandise(name='foo', price=100).save()
merchandises['#2'] = Merchandise(name='bar', price=120).save()
merchandises['#3'] = Merchandise(name='baz', price=110).save()
Store(merchandises=merchandises).save()
store = Store.objects().first()
store.select_related()
# confirms that the load data order is same with the one at storing
self.assertTrue(type(store.merchandises), OrderedDict)
self.assertEqual(','.join(store.merchandises.keys()), '#1,#2,#3')
if __name__ == '__main__':
unittest.main()