Compare commits
3 Commits
test-repli
...
cleanup-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbf32c6907 | ||
|
|
398964945a | ||
|
|
5f43c032f2 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,3 +17,4 @@ tests/test_bugfix.py
|
|||||||
htmlcov/
|
htmlcov/
|
||||||
venv
|
venv
|
||||||
venv3
|
venv3
|
||||||
|
scratchpad
|
||||||
|
|||||||
@@ -42,13 +42,18 @@ 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 ``qwerty`` password.
|
||||||
|
|
||||||
ReplicaSets
|
Replica Sets
|
||||||
===========
|
============
|
||||||
|
|
||||||
MongoEngine supports
|
MongoEngine supports connecting to replica sets::
|
||||||
:class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`. To use them,
|
|
||||||
please use an URI style connection and provide the ``replicaSet`` name
|
from mongoengine import connect
|
||||||
in the connection kwargs.
|
|
||||||
|
# Regular connect
|
||||||
|
connect('dbname', replicaset='rs-name')
|
||||||
|
|
||||||
|
# MongoDB URI-style connect
|
||||||
|
connect(host='mongodb://localhost/dbname?replicaSet=rs-name')
|
||||||
|
|
||||||
Read preferences are supported through the connection or via individual
|
Read preferences are supported through the connection or via individual
|
||||||
queries by passing the read_preference ::
|
queries by passing the read_preference ::
|
||||||
@@ -59,76 +64,74 @@ queries by passing the read_preference ::
|
|||||||
Multiple Databases
|
Multiple Databases
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Multiple database support was added in MongoEngine 0.6. To use multiple
|
To use multiple databases you can use :func:`~mongoengine.connect` and provide
|
||||||
databases you can use :func:`~mongoengine.connect` and provide an `alias` name
|
an `alias` name for the connection - if no `alias` is provided then "default"
|
||||||
for the connection - if no `alias` is provided then "default" is used.
|
is used.
|
||||||
|
|
||||||
In the background this uses :func:`~mongoengine.register_connection` to
|
In the background this uses :func:`~mongoengine.register_connection` to
|
||||||
store the data and you can register all aliases up front if required.
|
store the data and you can register all aliases up front if required.
|
||||||
|
|
||||||
Individual documents can also support multiple databases by providing a
|
Individual documents can also support multiple databases by providing a
|
||||||
`db_alias` in their meta data. This allows :class:`~pymongo.dbref.DBRef` objects
|
`db_alias` in their meta data. This allows :class:`~pymongo.dbref.DBRef`
|
||||||
to point across databases and collections. Below is an example schema, using
|
objects to point across databases and collections. Below is an example schema,
|
||||||
3 different databases to store data::
|
using 3 different databases to store data::
|
||||||
|
|
||||||
class User(Document):
|
class User(Document):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
meta = {"db_alias": "user-db"}
|
meta = {'db_alias': 'user-db'}
|
||||||
|
|
||||||
class Book(Document):
|
class Book(Document):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
meta = {"db_alias": "book-db"}
|
meta = {'db_alias': 'book-db'}
|
||||||
|
|
||||||
class AuthorBooks(Document):
|
class AuthorBooks(Document):
|
||||||
author = ReferenceField(User)
|
author = ReferenceField(User)
|
||||||
book = ReferenceField(Book)
|
book = ReferenceField(Book)
|
||||||
|
|
||||||
meta = {"db_alias": "users-books-db"}
|
meta = {'db_alias': 'users-books-db'}
|
||||||
|
|
||||||
|
|
||||||
Context Managers
|
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
|
For example, archiving older data into a separate database for performance
|
||||||
reasons or writing functions that dynamically choose collections to write
|
reasons or writing functions that dynamically choose collections to write
|
||||||
document to.
|
a document to.
|
||||||
|
|
||||||
Switch Database
|
Switch Database
|
||||||
---------------
|
---------------
|
||||||
The :class:`~mongoengine.context_managers.switch_db` context manager allows
|
The :class:`~mongoengine.context_managers.switch_db` context manager allows
|
||||||
you to change the database alias for a given class allowing quick and easy
|
you to change the database alias for a given class allowing quick and easy
|
||||||
access the same User document across databases::
|
access to the same User document across databases::
|
||||||
|
|
||||||
from mongoengine.context_managers import switch_db
|
from mongoengine.context_managers import switch_db
|
||||||
|
|
||||||
class User(Document):
|
class User(Document):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
meta = {"db_alias": "user-db"}
|
meta = {'db_alias': 'user-db'}
|
||||||
|
|
||||||
with switch_db(User, 'archive-user-db') as User:
|
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
|
Switch Collection
|
||||||
-----------------
|
-----------------
|
||||||
The :class:`~mongoengine.context_managers.switch_collection` context manager
|
The :class:`~mongoengine.context_managers.switch_collection` context manager
|
||||||
allows you to change the collection for a given class allowing quick and easy
|
allows you to change the collection for a given class allowing quick and easy
|
||||||
access the same Group document across collection::
|
access to the same Group document across collection::
|
||||||
|
|
||||||
from mongoengine.context_managers import switch_collection
|
from mongoengine.context_managers import switch_collection
|
||||||
|
|
||||||
class Group(Document):
|
class Group(Document):
|
||||||
name = StringField()
|
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:
|
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
|
.. note:: Make sure any aliases have been registered with
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
Installing MongoEngine
|
Installing MongoEngine
|
||||||
======================
|
======================
|
||||||
|
|
||||||
To use MongoEngine, you will need to download `MongoDB <http://mongodb.org/>`_
|
To use MongoEngine, you will need to download `MongoDB <http://mongodb.com/>`_
|
||||||
and ensure it is running in an accessible location. You will also need
|
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
|
`PyMongo <http://api.mongodb.org/python>`_ to use MongoEngine, but if you
|
||||||
install MongoEngine using setuptools, then the dependencies will be handled for
|
install MongoEngine using setuptools, then the dependencies will be handled for
|
||||||
you.
|
you.
|
||||||
|
|
||||||
MongoEngine is available on PyPI, so to use it you can use :program:`pip`:
|
MongoEngine is available on PyPI, so you can use :program:`pip`:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ class BaseQuerySet(object):
|
|||||||
|
|
||||||
.. versionadded:: 0.4
|
.. versionadded:: 0.4
|
||||||
"""
|
"""
|
||||||
return self._document(**kwargs).save()
|
return self._document(**kwargs).save(force_insert=True)
|
||||||
|
|
||||||
def first(self):
|
def first(self):
|
||||||
"""Retrieve the first object matching the query."""
|
"""Retrieve the first object matching the query."""
|
||||||
|
|||||||
@@ -412,7 +412,6 @@ class IndexesTest(unittest.TestCase):
|
|||||||
User.ensure_indexes()
|
User.ensure_indexes()
|
||||||
info = User.objects._collection.index_information()
|
info = User.objects._collection.index_information()
|
||||||
self.assertEqual(sorted(info.keys()), ['_cls_1_user_guid_1', '_id_'])
|
self.assertEqual(sorted(info.keys()), ['_cls_1_user_guid_1', '_id_'])
|
||||||
User.drop_collection()
|
|
||||||
|
|
||||||
def test_embedded_document_index(self):
|
def test_embedded_document_index(self):
|
||||||
"""Tests settings an index on an embedded document
|
"""Tests settings an index on an embedded document
|
||||||
@@ -434,7 +433,6 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
info = BlogPost.objects._collection.index_information()
|
info = BlogPost.objects._collection.index_information()
|
||||||
self.assertEqual(sorted(info.keys()), ['_id_', 'date.yr_-1'])
|
self.assertEqual(sorted(info.keys()), ['_id_', 'date.yr_-1'])
|
||||||
BlogPost.drop_collection()
|
|
||||||
|
|
||||||
def test_list_embedded_document_index(self):
|
def test_list_embedded_document_index(self):
|
||||||
"""Ensure list embedded documents can be indexed
|
"""Ensure list embedded documents can be indexed
|
||||||
@@ -461,7 +459,6 @@ class IndexesTest(unittest.TestCase):
|
|||||||
post1 = BlogPost(title="Embedded Indexes tests in place",
|
post1 = BlogPost(title="Embedded Indexes tests in place",
|
||||||
tags=[Tag(name="about"), Tag(name="time")])
|
tags=[Tag(name="about"), Tag(name="time")])
|
||||||
post1.save()
|
post1.save()
|
||||||
BlogPost.drop_collection()
|
|
||||||
|
|
||||||
def test_recursive_embedded_objects_dont_break_indexes(self):
|
def test_recursive_embedded_objects_dont_break_indexes(self):
|
||||||
|
|
||||||
@@ -622,8 +619,6 @@ class IndexesTest(unittest.TestCase):
|
|||||||
post3 = BlogPost(title='test3', date=Date(year=2010), slug='test')
|
post3 = BlogPost(title='test3', date=Date(year=2010), slug='test')
|
||||||
self.assertRaises(OperationError, post3.save)
|
self.assertRaises(OperationError, post3.save)
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
|
||||||
|
|
||||||
def test_unique_embedded_document(self):
|
def test_unique_embedded_document(self):
|
||||||
"""Ensure that uniqueness constraints are applied to fields on embedded documents.
|
"""Ensure that uniqueness constraints are applied to fields on embedded documents.
|
||||||
"""
|
"""
|
||||||
@@ -651,8 +646,6 @@ class IndexesTest(unittest.TestCase):
|
|||||||
sub=SubDocument(year=2010, slug='test'))
|
sub=SubDocument(year=2010, slug='test'))
|
||||||
self.assertRaises(NotUniqueError, post3.save)
|
self.assertRaises(NotUniqueError, post3.save)
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
|
||||||
|
|
||||||
def test_unique_embedded_document_in_list(self):
|
def test_unique_embedded_document_in_list(self):
|
||||||
"""
|
"""
|
||||||
Ensure that the uniqueness constraints are applied to fields in
|
Ensure that the uniqueness constraints are applied to fields in
|
||||||
@@ -683,8 +676,6 @@ class IndexesTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertRaises(NotUniqueError, post2.save)
|
self.assertRaises(NotUniqueError, post2.save)
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
|
||||||
|
|
||||||
def test_unique_with_embedded_document_and_embedded_unique(self):
|
def test_unique_with_embedded_document_and_embedded_unique(self):
|
||||||
"""Ensure that uniqueness constraints are applied to fields on
|
"""Ensure that uniqueness constraints are applied to fields on
|
||||||
embedded documents. And work with unique_with as well.
|
embedded documents. And work with unique_with as well.
|
||||||
@@ -718,8 +709,6 @@ class IndexesTest(unittest.TestCase):
|
|||||||
sub=SubDocument(year=2009, slug='test-1'))
|
sub=SubDocument(year=2009, slug='test-1'))
|
||||||
self.assertRaises(NotUniqueError, post3.save)
|
self.assertRaises(NotUniqueError, post3.save)
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
|
||||||
|
|
||||||
def test_ttl_indexes(self):
|
def test_ttl_indexes(self):
|
||||||
|
|
||||||
class Log(Document):
|
class Log(Document):
|
||||||
@@ -759,13 +748,11 @@ class IndexesTest(unittest.TestCase):
|
|||||||
raise AssertionError("We saved a dupe!")
|
raise AssertionError("We saved a dupe!")
|
||||||
except NotUniqueError:
|
except NotUniqueError:
|
||||||
pass
|
pass
|
||||||
Customer.drop_collection()
|
|
||||||
|
|
||||||
def test_unique_and_primary(self):
|
def test_unique_and_primary(self):
|
||||||
"""If you set a field as primary, then unexpected behaviour can occur.
|
"""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.
|
You won't create a duplicate but you will update an existing document.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class User(Document):
|
class User(Document):
|
||||||
name = StringField(primary_key=True, unique=True)
|
name = StringField(primary_key=True, unique=True)
|
||||||
password = StringField()
|
password = StringField()
|
||||||
@@ -781,8 +768,23 @@ class IndexesTest(unittest.TestCase):
|
|||||||
self.assertEqual(User.objects.count(), 1)
|
self.assertEqual(User.objects.count(), 1)
|
||||||
self.assertEqual(User.objects.get().password, 'secret2')
|
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.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):
|
def test_index_with_pk(self):
|
||||||
"""Ensure you can use `pk` as part of a query"""
|
"""Ensure you can use `pk` as part of a query"""
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -198,19 +198,6 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.assertTrue(isinstance(db, pymongo.database.Database))
|
self.assertTrue(isinstance(db, pymongo.database.Database))
|
||||||
self.assertEqual(db.name, 'test')
|
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):
|
def test_uri_without_credentials_doesnt_override_conn_settings(self):
|
||||||
"""Ensure connect() uses the username & password params if the URI
|
"""Ensure connect() uses the username & password params if the URI
|
||||||
doesn't explicitly specify them.
|
doesn't explicitly specify them.
|
||||||
@@ -332,6 +319,38 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.assertEqual(dict(conn1.write_concern), {'w': 1, 'j': True})
|
self.assertEqual(dict(conn1.write_concern), {'w': 1, 'j': True})
|
||||||
self.assertEqual(dict(conn2.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):
|
def test_datetime(self):
|
||||||
connect('mongoenginetest', tz_aware=True)
|
connect('mongoenginetest', tz_aware=True)
|
||||||
d = datetime.datetime(2010, 5, 5, tzinfo=utc)
|
d = datetime.datetime(2010, 5, 5, tzinfo=utc)
|
||||||
|
|||||||
Reference in New Issue
Block a user