PYTHON-882 - Backport get_database, get_collection, and with_options.
This change comes with a small backward breaking change in the behavior of client.document_class in exchange for enhanced configuration flexibility. See changelog.rst for details and examples.
This commit is contained in:
parent
4cf64b6170
commit
9b1ac9717f
@ -30,6 +30,7 @@
|
||||
.. autoattribute:: secondary_acceptable_latency_ms
|
||||
.. autoattribute:: write_concern
|
||||
.. autoattribute:: uuid_subtype
|
||||
.. automethod:: with_options
|
||||
.. automethod:: insert(doc_or_docs[, manipulate=True[, safe=None[, check_keys=True[, continue_on_error=False[, **kwargs]]]]])
|
||||
.. automethod:: save(to_save[, manipulate=True[, safe=None[, check_keys=True[, **kwargs]]]])
|
||||
.. automethod:: update(spec, document[, upsert=False[, manipulate=False[, safe=None[, multi=False[, check_keys=True[, **kwargs]]]]]])
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
.. automethod:: drop_database
|
||||
.. automethod:: copy_database(from_name, to_name[, from_host=None[, username=None[, password=None]]])
|
||||
.. automethod:: get_default_database
|
||||
.. automethod:: get_database
|
||||
.. automethod:: server_info
|
||||
.. automethod:: start_request
|
||||
.. automethod:: in_request
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
.. automethod:: drop_database
|
||||
.. automethod:: copy_database(from_name, to_name[, from_host=None[, username=None[, password=None]]])
|
||||
.. automethod:: get_default_database
|
||||
.. automethod:: get_database
|
||||
.. automethod:: server_info
|
||||
.. automethod:: start_request
|
||||
.. automethod:: in_request
|
||||
|
||||
@ -41,4 +41,5 @@
|
||||
.. automethod:: drop_database
|
||||
.. automethod:: copy_database(from_name, to_name[, from_host=None[, username=None[, password=None]]])
|
||||
.. automethod:: get_default_database
|
||||
.. automethod:: get_database
|
||||
.. automethod:: close_cursor
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
.. automethod:: drop_database
|
||||
.. automethod:: copy_database(from_name, to_name[, from_host=None[, username=None[, password=None]]])
|
||||
.. automethod:: get_default_database
|
||||
.. automethod:: get_database
|
||||
.. automethod:: close_cursor
|
||||
.. automethod:: get_lasterror_options
|
||||
.. automethod:: set_lasterror_options
|
||||
|
||||
@ -1,6 +1,36 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Changes in Version 2.9
|
||||
----------------------
|
||||
|
||||
.. warning::
|
||||
In previous versions of PyMongo, changing the value of
|
||||
:attr:`~pymongo.mongo_client.MongoClient.document_class` changed
|
||||
the behavior of all existing instances of
|
||||
:class:`~pymongo.collection.Collection`::
|
||||
|
||||
>>> coll = client.test.test
|
||||
>>> coll.find_one()
|
||||
{u'_id': ObjectId('5579dc7cfba5220cc14d9a18')}
|
||||
>>> from bson.son import SON
|
||||
>>> client.document_class = SON
|
||||
>>> coll.find_one()
|
||||
SON([(u'_id', ObjectId('5579dc7cfba5220cc14d9a18'))])
|
||||
|
||||
The document_class setting is now configurable at the client,
|
||||
database, collection, and per-operation level. This required breaking
|
||||
the existing behavior. To change the document class per operation in a
|
||||
forward compatible way use
|
||||
:meth:`~pymongo.collection.Collection.with_options`::
|
||||
|
||||
>>> coll.find_one()
|
||||
{u'_id': ObjectId('5579dc7cfba5220cc14d9a18')}
|
||||
>>> from bson.codec_options import CodecOptions
|
||||
>>> coll.with_options(CodecOptions(SON)).find_one()
|
||||
SON([(u'_id', ObjectId('5579dc7cfba5220cc14d9a18'))])
|
||||
|
||||
|
||||
Changes in Version 2.8.1
|
||||
------------------------
|
||||
|
||||
|
||||
@ -95,6 +95,7 @@ from pymongo.mongo_client import MongoClient
|
||||
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
|
||||
from pymongo.replica_set_connection import ReplicaSetConnection
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.write_concern import WriteConcern
|
||||
|
||||
def has_c():
|
||||
"""Is the C extension installed?
|
||||
|
||||
@ -24,7 +24,7 @@ from pymongo import (bulk,
|
||||
helpers,
|
||||
message)
|
||||
from pymongo.command_cursor import CommandCursor
|
||||
from pymongo.cursor import Cursor, CursorType
|
||||
from pymongo.cursor import Cursor
|
||||
from pymongo.errors import InvalidName, OperationFailure
|
||||
from pymongo.helpers import _check_write_command_response
|
||||
from pymongo.message import _INSERT, _UPDATE, _DELETE
|
||||
@ -48,7 +48,8 @@ class Collection(common.BaseObject):
|
||||
"""A Mongo collection.
|
||||
"""
|
||||
|
||||
def __init__(self, database, name, create=False, **kwargs):
|
||||
def __init__(self, database, name, create=False, codec_options=None,
|
||||
read_preference=None, write_concern=None, **kwargs):
|
||||
"""Get / create a Mongo collection.
|
||||
|
||||
Raises :class:`TypeError` if `name` is not an instance of
|
||||
@ -69,9 +70,20 @@ class Collection(common.BaseObject):
|
||||
- `name`: the name of the collection to get
|
||||
- `create` (optional): if ``True``, force collection
|
||||
creation even without options being set
|
||||
- `codec_options` (optional): An instance of
|
||||
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
||||
default) database.codec_options is used.
|
||||
- `read_preference` (optional): The read preference to use. If
|
||||
``None`` (the default) database.read_preference is used.
|
||||
- `write_concern` (optional): An instance of
|
||||
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
||||
default) database.write_concern is used.
|
||||
- `**kwargs` (optional): additional keyword arguments will
|
||||
be passed as options for the create collection command
|
||||
|
||||
.. versionchanged:: 2.9
|
||||
Added the codec_options, read_preference, and write_concern options.
|
||||
|
||||
.. versionchanged:: 2.2
|
||||
Removed deprecated argument: options
|
||||
|
||||
@ -86,15 +98,18 @@ class Collection(common.BaseObject):
|
||||
|
||||
.. mongodoc:: collections
|
||||
"""
|
||||
opts, mode, tags, wc_doc = helpers._get_common_options(
|
||||
database, codec_options, read_preference, write_concern)
|
||||
salms = database.secondary_acceptable_latency_ms
|
||||
|
||||
super(Collection, self).__init__(
|
||||
codec_options=opts,
|
||||
read_preference=mode,
|
||||
tag_sets=tags,
|
||||
secondary_acceptable_latency_ms=salms,
|
||||
slave_okay=database.slave_okay,
|
||||
read_preference=database.read_preference,
|
||||
tag_sets=database.tag_sets,
|
||||
secondary_acceptable_latency_ms=(
|
||||
database.secondary_acceptable_latency_ms),
|
||||
safe=database.safe,
|
||||
codec_options=database.codec_options,
|
||||
**database.write_concern)
|
||||
**wc_doc)
|
||||
|
||||
if not isinstance(name, basestring):
|
||||
raise TypeError("name must be an instance "
|
||||
@ -189,6 +204,43 @@ class Collection(common.BaseObject):
|
||||
"""
|
||||
return self.__database
|
||||
|
||||
def with_options(
|
||||
self, codec_options=None, read_preference=None, write_concern=None):
|
||||
"""Get a clone of this collection changing the specified settings.
|
||||
|
||||
>>> from pymongo import ReadPreference
|
||||
>>> coll1.read_preference == ReadPreference.PRIMARY
|
||||
True
|
||||
>>> coll2 = coll1.with_options(read_preference=ReadPreference.SECONDARY)
|
||||
>>> coll1.read_preference == ReadPreference.PRIMARY
|
||||
True
|
||||
>>> coll2.read_preference == ReadPreference.SECONDARY
|
||||
True
|
||||
|
||||
:Parameters:
|
||||
- `codec_options` (optional): An instance of
|
||||
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
||||
default) the :attr:`codec_options` of this :class:`Collection`
|
||||
is used.
|
||||
- `read_preference` (optional): The read preference to use. If
|
||||
``None`` (the default) the :attr:`read_preference` of this
|
||||
:class:`Collection` is used. See :mod:`~pymongo.read_preferences`
|
||||
for options.
|
||||
- `write_concern` (optional): An instance of
|
||||
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
||||
default) the :attr:`write_concern` of this :class:`Collection`
|
||||
is used.
|
||||
|
||||
.. versionadded:: 2.9
|
||||
"""
|
||||
opts, mode, tags, wc_doc = helpers._get_common_options(
|
||||
self, codec_options, read_preference, write_concern)
|
||||
coll = Collection(self.__database, self.__name, False, opts)
|
||||
coll.write_concern = wc_doc
|
||||
coll.read_preference = mode
|
||||
coll.tag_sets = tags
|
||||
return coll
|
||||
|
||||
def initialize_unordered_bulk_op(self):
|
||||
"""Initialize an unordered batch of write operations.
|
||||
|
||||
@ -1537,7 +1589,7 @@ class Collection(common.BaseObject):
|
||||
use_master = not self.slave_okay and not self.read_preference
|
||||
|
||||
return self.__database.command("group", group,
|
||||
uuid_subtype=self.uuid_subtype,
|
||||
codec_options=self.codec_options,
|
||||
read_preference=self.read_preference,
|
||||
tag_sets=self.tag_sets,
|
||||
secondary_acceptable_latency_ms=(
|
||||
@ -1650,7 +1702,7 @@ class Collection(common.BaseObject):
|
||||
must_use_master = True
|
||||
|
||||
response = self.__database.command("mapreduce", self.__name,
|
||||
uuid_subtype=self.uuid_subtype,
|
||||
codec_options=self.codec_options,
|
||||
map=map, reduce=reduce,
|
||||
read_preference=self.read_preference,
|
||||
tag_sets=self.tag_sets,
|
||||
@ -1706,7 +1758,7 @@ class Collection(common.BaseObject):
|
||||
use_master = not self.slave_okay and not self.read_preference
|
||||
|
||||
res = self.__database.command("mapreduce", self.__name,
|
||||
uuid_subtype=self.uuid_subtype,
|
||||
codec_options=self.codec_options,
|
||||
read_preference=self.read_preference,
|
||||
tag_sets=self.tag_sets,
|
||||
secondary_acceptable_latency_ms=(
|
||||
@ -1814,7 +1866,7 @@ class Collection(common.BaseObject):
|
||||
out = self.__database.command("findAndModify", self.__name,
|
||||
allowable_errors=[no_obj_error],
|
||||
read_preference=ReadPreference.PRIMARY,
|
||||
uuid_subtype=self.uuid_subtype,
|
||||
codec_options=self.codec_options,
|
||||
**kwargs)
|
||||
|
||||
if not out['ok']:
|
||||
|
||||
@ -416,7 +416,10 @@ class BaseObject(object):
|
||||
|
||||
def __init__(self, **options):
|
||||
|
||||
self._codec_options = None
|
||||
self._codec_options = options.get('codec_options')
|
||||
if not isinstance(self._codec_options, CodecOptions):
|
||||
raise TypeError("codec_options must be an instance of "
|
||||
"bson.codec_options.CodecOptions")
|
||||
self.__slave_okay = False
|
||||
self.__read_pref = ReadPreference.PRIMARY
|
||||
self.__tag_sets = [{}]
|
||||
@ -465,8 +468,6 @@ class BaseObject(object):
|
||||
self.__read_pref = validate_read_preference(option, value)
|
||||
elif option in ('tag_sets', 'readpreferencetags'):
|
||||
self.__tag_sets = validate_tag_sets(option, value)
|
||||
elif option == 'codec_options':
|
||||
self._codec_options = value
|
||||
elif option in ('secondaryacceptablelatencyms',
|
||||
'secondary_acceptable_latency_ms'):
|
||||
self.__secondary_acceptable_latency_ms = (
|
||||
|
||||
@ -18,6 +18,7 @@ from collections import deque
|
||||
|
||||
from bson import RE_TYPE
|
||||
from bson.code import Code
|
||||
from bson.codec_options import CodecOptions as _CodecOptions
|
||||
from bson.son import SON
|
||||
from pymongo import helpers, message, read_preferences
|
||||
from pymongo.read_preferences import ReadPreference, secondary_ok_commands
|
||||
@ -104,7 +105,7 @@ class Cursor(object):
|
||||
read_preference=ReadPreference.PRIMARY,
|
||||
tag_sets=[{}], secondary_acceptable_latency_ms=None,
|
||||
exhaust=False, compile_re=True, oplog_replay=False,
|
||||
modifiers=None, _must_use_master=False, _uuid_subtype=None,
|
||||
modifiers=None, _must_use_master=False, _codec_options=None,
|
||||
**kwargs):
|
||||
"""Create a new cursor.
|
||||
|
||||
@ -178,9 +179,6 @@ class Cursor(object):
|
||||
if not isinstance(fields, dict):
|
||||
fields = helpers._fields_list_to_dict(fields)
|
||||
|
||||
if as_class is None:
|
||||
as_class = collection.database.connection.document_class
|
||||
|
||||
self.__collection = collection
|
||||
self.__spec = spec
|
||||
self.__fields = fields
|
||||
@ -216,16 +214,19 @@ class Cursor(object):
|
||||
self.__explain = False
|
||||
self.__hint = None
|
||||
self.__comment = None
|
||||
self.__as_class = as_class
|
||||
self.__slave_okay = slave_okay
|
||||
self.__manipulate = manipulate
|
||||
self.__read_preference = read_preference
|
||||
self.__tag_sets = tag_sets
|
||||
self.__secondary_acceptable_latency_ms = secondary_acceptable_latency_ms
|
||||
self.__tz_aware = collection.database.connection.tz_aware
|
||||
self.__compile_re = compile_re
|
||||
self.__must_use_master = _must_use_master
|
||||
self.__uuid_subtype = _uuid_subtype or collection.uuid_subtype
|
||||
|
||||
copts = _codec_options or collection.codec_options
|
||||
if as_class is not None:
|
||||
copts = _CodecOptions(
|
||||
as_class, copts.tz_aware, copts.uuid_representation)
|
||||
self.__codec_options = copts
|
||||
|
||||
self.__data = deque()
|
||||
self.__connection_id = None
|
||||
@ -311,10 +312,10 @@ class Cursor(object):
|
||||
values_to_clone = ("spec", "fields", "skip", "limit", "max_time_ms",
|
||||
"comment", "max", "min",
|
||||
"snapshot", "ordering", "explain", "hint",
|
||||
"batch_size", "max_scan", "as_class", "slave_okay",
|
||||
"batch_size", "max_scan", "slave_okay",
|
||||
"manipulate", "read_preference", "tag_sets",
|
||||
"secondary_acceptable_latency_ms",
|
||||
"must_use_master", "uuid_subtype", "compile_re",
|
||||
"must_use_master", "codec_options", "compile_re",
|
||||
"query_flags", "modifiers", "kwargs")
|
||||
data = dict((k, v) for k, v in self.__dict__.iteritems()
|
||||
if k.startswith('_Cursor__') and k[9:] in values_to_clone)
|
||||
@ -825,7 +826,7 @@ class Cursor(object):
|
||||
database = self.__collection.database
|
||||
r = database.command("count", self.__collection.name,
|
||||
allowable_errors=["ns missing"],
|
||||
uuid_subtype=self.__uuid_subtype,
|
||||
codec_options=self.__codec_options,
|
||||
compile_re=self.__compile_re,
|
||||
**command)
|
||||
if r.get("errmsg", "") == "ns missing":
|
||||
@ -878,7 +879,7 @@ class Cursor(object):
|
||||
database = self.__collection.database
|
||||
return database.command("distinct",
|
||||
self.__collection.name,
|
||||
uuid_subtype=self.__uuid_subtype,
|
||||
codec_options=self.__codec_options,
|
||||
compile_re=self.__compile_re,
|
||||
**options)["values"]
|
||||
|
||||
@ -1013,11 +1014,13 @@ class Cursor(object):
|
||||
raise
|
||||
|
||||
try:
|
||||
response = helpers._unpack_response(response, self.__id,
|
||||
self.__as_class,
|
||||
self.__tz_aware,
|
||||
self.__uuid_subtype,
|
||||
self.__compile_re)
|
||||
response = helpers._unpack_response(
|
||||
response,
|
||||
self.__id,
|
||||
self.__codec_options.document_class,
|
||||
self.__codec_options.tz_aware,
|
||||
self.__codec_options.uuid_representation,
|
||||
self.__compile_re)
|
||||
except OperationFailure:
|
||||
self.__killed = True
|
||||
# Make sure exhaust socket is returned immediately, if necessary.
|
||||
@ -1075,7 +1078,7 @@ class Cursor(object):
|
||||
self.__collection.full_name,
|
||||
self.__skip, ntoreturn,
|
||||
self.__query_spec(), self.__fields,
|
||||
self.__uuid_subtype))
|
||||
self.__codec_options.uuid_representation))
|
||||
if not self.__id:
|
||||
self.__killed = True
|
||||
elif self.__id: # Get More
|
||||
|
||||
@ -18,6 +18,7 @@ import warnings
|
||||
|
||||
from bson.binary import OLD_UUID_SUBTYPE
|
||||
from bson.code import Code
|
||||
from bson.codec_options import CodecOptions as _CodecOptions
|
||||
from bson.dbref import DBRef
|
||||
from bson.son import SON
|
||||
from pymongo import auth, common, helpers
|
||||
@ -28,7 +29,8 @@ from pymongo.errors import (CollectionInvalid,
|
||||
OperationFailure)
|
||||
from pymongo.read_preferences import (modes,
|
||||
secondary_ok_commands,
|
||||
ReadPreference)
|
||||
ReadPreference,
|
||||
_ServerMode)
|
||||
from pymongo.son_manipulator import SONManipulator
|
||||
|
||||
|
||||
@ -36,7 +38,8 @@ class Database(common.BaseObject):
|
||||
"""A Mongo database.
|
||||
"""
|
||||
|
||||
def __init__(self, connection, name):
|
||||
def __init__(self, connection, name, codec_options=None,
|
||||
read_preference=None, write_concern=None):
|
||||
"""Get a database by connection and name.
|
||||
|
||||
Raises :class:`TypeError` if `name` is not an instance of
|
||||
@ -47,18 +50,32 @@ class Database(common.BaseObject):
|
||||
:Parameters:
|
||||
- `connection`: a client instance
|
||||
- `name`: database name
|
||||
- `codec_options` (optional): An instance of
|
||||
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
||||
default) connection.codec_options is used.
|
||||
- `read_preference` (optional): The read preference to use. If
|
||||
``None`` (the default) connection.read_preference is used.
|
||||
- `write_concern` (optional): An instance of
|
||||
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
||||
default) connection.write_concern is used.
|
||||
|
||||
.. mongodoc:: databases
|
||||
|
||||
.. versionchanged:: 2.9
|
||||
Added the codec_options, read_preference, and write_concern options.
|
||||
"""
|
||||
super(Database,
|
||||
self).__init__(slave_okay=connection.slave_okay,
|
||||
read_preference=connection.read_preference,
|
||||
tag_sets=connection.tag_sets,
|
||||
secondary_acceptable_latency_ms=(
|
||||
connection.secondary_acceptable_latency_ms),
|
||||
safe=connection.safe,
|
||||
codec_options=connection.codec_options,
|
||||
**connection.write_concern)
|
||||
opts, mode, tags, wc_doc = helpers._get_common_options(
|
||||
connection, codec_options, read_preference, write_concern)
|
||||
salms = connection.secondary_acceptable_latency_ms
|
||||
|
||||
super(Database, self).__init__(
|
||||
codec_options=opts,
|
||||
read_preference=mode,
|
||||
tag_sets=tags,
|
||||
secondary_acceptable_latency_ms=salms,
|
||||
slave_okay=connection.slave_okay,
|
||||
safe=connection.safe,
|
||||
**wc_doc)
|
||||
|
||||
if not isinstance(name, basestring):
|
||||
raise TypeError("name must be an instance "
|
||||
@ -209,7 +226,48 @@ class Database(common.BaseObject):
|
||||
"""
|
||||
return self.__getattr__(name)
|
||||
|
||||
def create_collection(self, name, **kwargs):
|
||||
def get_collection(self, name, codec_options=None,
|
||||
read_preference=None, write_concern=None):
|
||||
"""Get a :class:`~pymongo.collection.Collection` with the given name
|
||||
and options.
|
||||
|
||||
Useful for creating a :class:`~pymongo.collection.Collection` with
|
||||
different codec options, read preference, and/or write concern from
|
||||
this :class:`Database`.
|
||||
|
||||
>>> from pymongo import ReadPreference
|
||||
>>> db.read_preference == ReadPreference.PRIMARY
|
||||
True
|
||||
>>> coll1 = db.test
|
||||
>>> coll1.read_preference == ReadPreference.PRIMARY
|
||||
True
|
||||
>>> coll2 = db.get_collection(
|
||||
... 'test', read_preference=ReadPreference.SECONDARY)
|
||||
>>> coll2.read_preference == SECONDARY
|
||||
True
|
||||
|
||||
:Parameters:
|
||||
- `name`: The name of the collection - a string.
|
||||
- `codec_options` (optional): An instance of
|
||||
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
||||
default) the :attr:`codec_options` of this :class:`Database` is
|
||||
used.
|
||||
- `read_preference` (optional): The read preference to use. If
|
||||
``None`` (the default) the :attr:`read_preference` of this
|
||||
:class:`Database` is used. See :mod:`~pymongo.read_preferences`
|
||||
for options.
|
||||
- `write_concern` (optional): An instance of
|
||||
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
||||
default) the :attr:`write_concern` of this :class:`Database` is
|
||||
used.
|
||||
|
||||
.. versionadded:: 2.9
|
||||
"""
|
||||
return Collection(
|
||||
self, name, False, codec_options, read_preference, write_concern)
|
||||
|
||||
def create_collection(self, name, codec_options=None,
|
||||
read_preference=None, write_concern=None, **kwargs):
|
||||
"""Create a new :class:`~pymongo.collection.Collection` in this
|
||||
database.
|
||||
|
||||
@ -232,22 +290,34 @@ class Database(common.BaseObject):
|
||||
|
||||
:Parameters:
|
||||
- `name`: the name of the collection to create
|
||||
- `codec_options` (optional): An instance of
|
||||
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
||||
default) the :attr:`codec_options` of this :class:`Database` is
|
||||
used.
|
||||
- `read_preference` (optional): The read preference to use. If
|
||||
``None`` (the default) the :attr:`read_preference` of this
|
||||
:class:`Database` is used.
|
||||
- `write_concern` (optional): An instance of
|
||||
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
||||
default) the :attr:`write_concern` of this :class:`Database` is
|
||||
used.
|
||||
- `**kwargs` (optional): additional keyword arguments will
|
||||
be passed as options for the create collection command
|
||||
|
||||
.. versionchanged:: 2.9
|
||||
Added the codec_options, read_preference, and write_concern options.
|
||||
|
||||
.. versionchanged:: 2.2
|
||||
Removed deprecated argument: options
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
deprecating `options` in favor of kwargs
|
||||
"""
|
||||
opts = {"create": True}
|
||||
opts.update(kwargs)
|
||||
|
||||
if name in self.collection_names():
|
||||
raise CollectionInvalid("collection %s already exists" % name)
|
||||
|
||||
return Collection(self, name, **opts)
|
||||
return Collection(self, name, True, codec_options,
|
||||
read_preference, write_concern, **kwargs)
|
||||
|
||||
def _apply_incoming_manipulators(self, son, collection):
|
||||
for manipulator in self.__incoming_manipulators:
|
||||
@ -285,7 +355,8 @@ class Database(common.BaseObject):
|
||||
|
||||
def _command(self, command, value=1,
|
||||
check=True, allowable_errors=None,
|
||||
uuid_subtype=OLD_UUID_SUBTYPE, compile_re=True, **kwargs):
|
||||
uuid_subtype=OLD_UUID_SUBTYPE, compile_re=True,
|
||||
read_preference=None, codec_options=None, **kwargs):
|
||||
"""Internal command helper.
|
||||
"""
|
||||
|
||||
@ -310,19 +381,34 @@ class Database(common.BaseObject):
|
||||
must_use_master = True
|
||||
break
|
||||
|
||||
if codec_options is None or 'as_class' in kwargs:
|
||||
opts = {}
|
||||
if 'as_class' in kwargs:
|
||||
opts['document_class'] = kwargs.pop('as_class')
|
||||
# 'as_class' must be in kwargs so don't use document_class
|
||||
if codec_options:
|
||||
opts['tz_aware'] = codec_options.tz_aware
|
||||
opts['uuid_representation'] = codec_options.uuid_representation
|
||||
else:
|
||||
opts['uuid_representation'] = uuid_subtype
|
||||
codec_options = _CodecOptions(**opts)
|
||||
|
||||
extra_opts = {
|
||||
'as_class': kwargs.pop('as_class', None),
|
||||
'slave_okay': kwargs.pop('slave_okay', self.slave_okay),
|
||||
'_codec_options': codec_options,
|
||||
'_must_use_master': must_use_master,
|
||||
'_uuid_subtype': uuid_subtype
|
||||
}
|
||||
|
||||
extra_opts['read_preference'] = kwargs.pop(
|
||||
'read_preference',
|
||||
self.read_preference)
|
||||
extra_opts['tag_sets'] = kwargs.pop(
|
||||
'tag_sets',
|
||||
self.tag_sets)
|
||||
if isinstance(read_preference, _ServerMode):
|
||||
extra_opts['read_preference'] = read_preference.mode
|
||||
extra_opts['tag_sets'] = read_preference.tag_sets
|
||||
else:
|
||||
if read_preference is None:
|
||||
read_preference = self.read_preference
|
||||
extra_opts['read_preference'] = read_preference
|
||||
extra_opts['tag_sets'] = kwargs.pop(
|
||||
'tag_sets',
|
||||
self.tag_sets)
|
||||
extra_opts['secondary_acceptable_latency_ms'] = kwargs.pop(
|
||||
'secondary_acceptable_latency_ms',
|
||||
self.secondary_acceptable_latency_ms)
|
||||
@ -357,7 +443,8 @@ class Database(common.BaseObject):
|
||||
|
||||
def command(self, command, value=1,
|
||||
check=True, allowable_errors=[],
|
||||
uuid_subtype=OLD_UUID_SUBTYPE, compile_re=True, **kwargs):
|
||||
uuid_subtype=OLD_UUID_SUBTYPE, compile_re=True,
|
||||
read_preference=None, codec_options=None, **kwargs):
|
||||
"""Issue a MongoDB command.
|
||||
|
||||
Send command `command` to the database and return the
|
||||
@ -445,7 +532,8 @@ class Database(common.BaseObject):
|
||||
.. _localThreshold: http://docs.mongodb.org/manual/reference/mongos/#cmdoption-mongos--localThreshold
|
||||
"""
|
||||
return self._command(command, value, check, allowable_errors,
|
||||
uuid_subtype, compile_re, **kwargs)[0]
|
||||
uuid_subtype, compile_re, read_preference,
|
||||
codec_options, **kwargs)[0]
|
||||
|
||||
def collection_names(self, include_system_collections=True):
|
||||
"""Get a list of all the collection names in this database.
|
||||
|
||||
@ -32,6 +32,37 @@ from pymongo.errors import (AutoReconnect,
|
||||
OperationFailure,
|
||||
ExecutionTimeout,
|
||||
WTimeoutError)
|
||||
from pymongo.read_preferences import _ServerMode
|
||||
from pymongo.write_concern import WriteConcern as _WriteConcern
|
||||
|
||||
|
||||
def _get_common_options(obj, codec_options, read_preference, write_concern):
|
||||
"""Get the codec options, read preference mode and tags, and write concern
|
||||
necessary to create a new Database of Collection instance.
|
||||
"""
|
||||
if codec_options is None:
|
||||
codec_options = obj.codec_options
|
||||
|
||||
if read_preference is None:
|
||||
rp_mode = obj.read_preference
|
||||
rp_tags = obj.tag_sets
|
||||
else:
|
||||
if isinstance(read_preference, _ServerMode):
|
||||
rp_mode = read_preference.mode
|
||||
rp_tags = read_preference.tag_sets
|
||||
else:
|
||||
rp_mode = read_preference
|
||||
rp_tags = [{}]
|
||||
|
||||
if write_concern is None:
|
||||
wc_document = obj.write_concern
|
||||
else:
|
||||
if not isinstance(write_concern, _WriteConcern):
|
||||
raise TypeError("write_concern must be an instance of "
|
||||
"pymongo.write_concern.WriteConcern")
|
||||
wc_document = write_concern.document
|
||||
|
||||
return codec_options, rp_mode, rp_tags, wc_document
|
||||
|
||||
|
||||
def _index_list(key_or_list, direction=None):
|
||||
|
||||
@ -1512,6 +1512,46 @@ class MongoClient(common.BaseObject):
|
||||
|
||||
return self[self.__default_database_name]
|
||||
|
||||
def get_database(self, name, codec_options=None,
|
||||
read_preference=None, write_concern=None):
|
||||
"""Get a :class:`~pymongo.database.Database` with the given name and
|
||||
options.
|
||||
|
||||
Useful for creating a :class:`~pymongo.database.Database` with
|
||||
different codec options, read preference, and/or write concern from
|
||||
this :class:`MongoClient`.
|
||||
|
||||
>>> from pymongo import ReadPreference
|
||||
>>> client.read_preference == ReadPreference.PRIMARY
|
||||
True
|
||||
>>> db1 = client.test
|
||||
>>> db1.read_preference == ReadPreference.PRIMARY
|
||||
True
|
||||
>>> db2 = client.get_database(
|
||||
... 'test', read_preference=ReadPreference.SECONDARY)
|
||||
>>> db2.read_preference == ReadPreference.SECONDARY
|
||||
True
|
||||
|
||||
:Parameters:
|
||||
- `name`: The name of the database - a string.
|
||||
- `codec_options` (optional): An instance of
|
||||
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
||||
default) the :attr:`codec_options` of this :class:`MongoClient` is
|
||||
used.
|
||||
- `read_preference` (optional): The read preference to use. If
|
||||
``None`` (the default) the :attr:`read_preference` of this
|
||||
:class:`MongoClient` is used. See :mod:`~pymongo.read_preferences`
|
||||
for options.
|
||||
- `write_concern` (optional): An instance of
|
||||
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
||||
default) the :attr:`write_concern` of this :class:`MongoClient` is
|
||||
used.
|
||||
|
||||
.. versionadded:: 2.9
|
||||
"""
|
||||
return database.Database(
|
||||
self, name, codec_options, read_preference, write_concern)
|
||||
|
||||
@property
|
||||
def is_locked(self):
|
||||
"""Is this server locked? While locked, all write operations
|
||||
|
||||
@ -1977,3 +1977,43 @@ class MongoReplicaSetClient(common.BaseObject):
|
||||
raise ConfigurationError('No default database defined')
|
||||
|
||||
return self[self.__default_database_name]
|
||||
|
||||
def get_database(self, name, codec_options=None,
|
||||
read_preference=None, write_concern=None):
|
||||
"""Get a :class:`~pymongo.database.Database` with the given name and
|
||||
options.
|
||||
|
||||
Useful for creating a :class:`~pymongo.database.Database` with
|
||||
different codec options, read preference, and/or write concern from
|
||||
this :class:`MongoClient`.
|
||||
|
||||
>>> from pymongo import ReadPreference
|
||||
>>> client.read_preference == ReadPreference.PRIMARY
|
||||
True
|
||||
>>> db1 = client.test
|
||||
>>> db1.read_preference == ReadPreference.PRIMARY
|
||||
True
|
||||
>>> db2 = client.get_database(
|
||||
... 'test', read_preference=ReadPreference.SECONDARY)
|
||||
>>> db2.read_preference == ReadPreference.SECONDARY
|
||||
True
|
||||
|
||||
:Parameters:
|
||||
- `name`: The name of the database - a string.
|
||||
- `codec_options` (optional): An instance of
|
||||
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
||||
default) the :attr:`codec_options` of this :class:`MongoClient` is
|
||||
used.
|
||||
- `read_preference` (optional): The read preference to use. If
|
||||
``None`` (the default) the :attr:`read_preference` of this
|
||||
:class:`MongoClient` is used. See :mod:`~pymongo.read_preferences`
|
||||
for options.
|
||||
- `write_concern` (optional): An instance of
|
||||
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
||||
default) the :attr:`write_concern` of this :class:`MongoClient` is
|
||||
used.
|
||||
|
||||
.. versionadded:: 2.9
|
||||
"""
|
||||
return database.Database(
|
||||
self, name, codec_options, read_preference, write_concern)
|
||||
|
||||
@ -29,6 +29,8 @@ sys.path[0:0] = [""]
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from bson.binary import JAVA_LEGACY, PYTHON_LEGACY
|
||||
from bson.codec_options import CodecOptions
|
||||
from bson.son import SON
|
||||
from bson.tz_util import utc
|
||||
from pymongo.mongo_client import MongoClient
|
||||
@ -40,7 +42,8 @@ from pymongo.errors import (AutoReconnect,
|
||||
ConnectionFailure,
|
||||
InvalidName,
|
||||
OperationFailure)
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.read_preferences import ReadPreference, Secondary
|
||||
from pymongo.write_concern import WriteConcern
|
||||
from test import version, host, port, pair, skip_restricted_localhost
|
||||
from test.pymongo_mocks import MockClient
|
||||
from test.utils import (assertRaisesExactly,
|
||||
@ -226,6 +229,28 @@ class TestClient(unittest.TestCase, TestRequestMixin):
|
||||
self.assertEqual(client.test, client["test"])
|
||||
self.assertEqual(client.test, Database(client, "test"))
|
||||
|
||||
def test_get_database(self):
|
||||
client = MongoClient(host, port, _connect=False)
|
||||
codec_options = CodecOptions(
|
||||
tz_aware=True, uuid_representation=JAVA_LEGACY)
|
||||
write_concern = WriteConcern(w=2, j=True)
|
||||
db = client.get_database(
|
||||
'foo', codec_options, ReadPreference.SECONDARY, write_concern)
|
||||
self.assertEqual('foo', db.name)
|
||||
self.assertEqual(codec_options, db.codec_options)
|
||||
self.assertEqual(JAVA_LEGACY, db.uuid_subtype)
|
||||
self.assertEqual(ReadPreference.SECONDARY, db.read_preference)
|
||||
self.assertEqual([{}], db.tag_sets)
|
||||
self.assertEqual(write_concern.document, db.write_concern)
|
||||
|
||||
pref = Secondary([{"dc": "sf"}])
|
||||
db = client.get_database('foo', read_preference=pref)
|
||||
self.assertEqual(pref.mode, db.read_preference)
|
||||
self.assertEqual(pref.tag_sets, db.tag_sets)
|
||||
self.assertEqual({}, db.write_concern)
|
||||
self.assertEqual(CodecOptions(), db.codec_options)
|
||||
self.assertEqual(PYTHON_LEGACY, db.uuid_subtype)
|
||||
|
||||
def test_database_names(self):
|
||||
client = MongoClient(host, port)
|
||||
|
||||
@ -526,6 +551,7 @@ class TestClient(unittest.TestCase, TestRequestMixin):
|
||||
self.assertFalse(isinstance(db.test.find_one(), SON))
|
||||
|
||||
c.document_class = SON
|
||||
db = c.pymongo_test
|
||||
|
||||
self.assertEqual(SON, c.document_class)
|
||||
self.assertTrue(isinstance(db.test.find_one(), SON))
|
||||
@ -539,6 +565,7 @@ class TestClient(unittest.TestCase, TestRequestMixin):
|
||||
self.assertFalse(isinstance(db.test.find_one(as_class=dict), SON))
|
||||
|
||||
c.document_class = dict
|
||||
db = c.pymongo_test
|
||||
|
||||
self.assertEqual(dict, c.document_class)
|
||||
self.assertTrue(isinstance(db.test.find_one(), dict))
|
||||
|
||||
@ -28,9 +28,10 @@ from nose.plugins.skip import SkipTest
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from bson.binary import Binary
|
||||
from bson.binary import Binary, JAVA_LEGACY
|
||||
from bson.regex import Regex
|
||||
from bson.code import Code
|
||||
from bson.codec_options import CodecOptions
|
||||
from bson.dbref import DBRef
|
||||
from bson.objectid import ObjectId
|
||||
from bson.py3compat import b
|
||||
@ -40,9 +41,6 @@ from pymongo import (ASCENDING, DESCENDING, GEO2D,
|
||||
from pymongo import message as message_module
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.command_cursor import CommandCursor
|
||||
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.son_manipulator import SONManipulator
|
||||
from pymongo.errors import (DocumentTooLarge,
|
||||
DuplicateKeyError,
|
||||
InvalidDocument,
|
||||
@ -50,6 +48,10 @@ from pymongo.errors import (DocumentTooLarge,
|
||||
InvalidOperation,
|
||||
OperationFailure,
|
||||
WTimeoutError)
|
||||
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
|
||||
from pymongo.read_preferences import ReadPreference, Secondary
|
||||
from pymongo.son_manipulator import SONManipulator
|
||||
from pymongo.write_concern import WriteConcern
|
||||
from test.test_client import get_client
|
||||
from test.utils import (catch_warnings, enable_text_search,
|
||||
get_pool, is_mongos, joinall, oid_generated_on_client)
|
||||
@ -109,6 +111,26 @@ class TestCollection(unittest.TestCase):
|
||||
# No exception
|
||||
self.db.drop_collection('test')
|
||||
|
||||
def test_with_options(self):
|
||||
coll = self.db.test
|
||||
codec_options = CodecOptions(
|
||||
tz_aware=True, uuid_representation=JAVA_LEGACY)
|
||||
write_concern = WriteConcern(w=2, j=True)
|
||||
coll2 = coll.with_options(
|
||||
codec_options, ReadPreference.SECONDARY, write_concern)
|
||||
self.assertEqual(codec_options, coll2.codec_options)
|
||||
self.assertEqual(JAVA_LEGACY, coll2.uuid_subtype)
|
||||
self.assertEqual(ReadPreference.SECONDARY, coll2.read_preference)
|
||||
self.assertEqual(write_concern.document, coll2.write_concern)
|
||||
|
||||
pref = Secondary([{"dc": "sf"}])
|
||||
coll2 = coll.with_options(read_preference=pref)
|
||||
self.assertEqual(pref.mode, coll2.read_preference)
|
||||
self.assertEqual(pref.tag_sets, coll2.tag_sets)
|
||||
self.assertEqual(coll.codec_options, coll2.codec_options)
|
||||
self.assertEqual(coll.uuid_subtype, coll2.uuid_subtype)
|
||||
self.assertEqual(coll.write_concern, coll2.write_concern)
|
||||
|
||||
def test_create_index(self):
|
||||
db = self.db
|
||||
|
||||
|
||||
@ -686,8 +686,8 @@ class TestCursor(unittest.TestCase):
|
||||
self.assertEqual(cursor._Cursor__skip, cursor2._Cursor__skip)
|
||||
self.assertEqual(cursor._Cursor__limit, cursor2._Cursor__limit)
|
||||
self.assertEqual(cursor._Cursor__snapshot, cursor2._Cursor__snapshot)
|
||||
self.assertEqual(type(cursor._Cursor__as_class),
|
||||
type(cursor2._Cursor__as_class))
|
||||
self.assertEqual(type(cursor._Cursor__codec_options),
|
||||
type(cursor2._Cursor__codec_options))
|
||||
self.assertEqual(cursor._Cursor__slave_okay,
|
||||
cursor2._Cursor__slave_okay)
|
||||
self.assertEqual(cursor._Cursor__manipulate,
|
||||
|
||||
@ -24,7 +24,9 @@ import unittest
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from bson.binary import JAVA_LEGACY
|
||||
from bson.code import Code
|
||||
from bson.codec_options import CodecOptions
|
||||
from bson.regex import Regex
|
||||
from bson.dbref import DBRef
|
||||
from bson.objectid import ObjectId
|
||||
@ -33,18 +35,19 @@ from pymongo import (ALL,
|
||||
auth,
|
||||
OFF,
|
||||
SLOW_ONLY,
|
||||
helpers,
|
||||
ReadPreference)
|
||||
helpers)
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.database import Database
|
||||
from pymongo.errors import (CollectionInvalid,
|
||||
ExecutionTimeout,
|
||||
InvalidName,
|
||||
OperationFailure)
|
||||
from pymongo.read_preferences import ReadPreference, Secondary
|
||||
from pymongo.son_manipulator import (AutoReference,
|
||||
NamespaceInjector,
|
||||
SONManipulator,
|
||||
ObjectIdShuffler)
|
||||
from pymongo.write_concern import WriteConcern
|
||||
from test import version, skip_restricted_localhost
|
||||
from test.utils import (catch_warnings, get_command_line,
|
||||
is_mongos, server_started_with_auth)
|
||||
@ -92,6 +95,27 @@ class TestDatabase(unittest.TestCase):
|
||||
self.assertNotEqual(db.test, Collection(db, "mike"))
|
||||
self.assertEqual(db.test.mike, db["test.mike"])
|
||||
|
||||
def test_get_collection(self):
|
||||
db = Database(self.client, "pymongo_test")
|
||||
codec_options = CodecOptions(
|
||||
tz_aware=True, uuid_representation=JAVA_LEGACY)
|
||||
write_concern = WriteConcern(w=2, j=True)
|
||||
coll = db.get_collection(
|
||||
'foo', codec_options, ReadPreference.SECONDARY, write_concern)
|
||||
self.assertEqual('foo', coll.name)
|
||||
self.assertEqual(codec_options, coll.codec_options)
|
||||
self.assertEqual(JAVA_LEGACY, coll.uuid_subtype)
|
||||
self.assertEqual(ReadPreference.SECONDARY, coll.read_preference)
|
||||
self.assertEqual(write_concern.document, coll.write_concern)
|
||||
|
||||
pref = Secondary([{"dc": "sf"}])
|
||||
coll = db.get_collection('foo', read_preference=pref)
|
||||
self.assertEqual(pref.mode, coll.read_preference)
|
||||
self.assertEqual(pref.tag_sets, coll.tag_sets)
|
||||
self.assertEqual(db.codec_options, coll.codec_options)
|
||||
self.assertEqual(db.uuid_subtype, coll.uuid_subtype)
|
||||
self.assertEqual(db.write_concern, coll.write_concern)
|
||||
|
||||
def test_create_collection(self):
|
||||
db = Database(self.client, "pymongo_test")
|
||||
|
||||
|
||||
@ -471,6 +471,7 @@ class TestMasterSlaveConnection(unittest.TestCase, TestRequestMixin):
|
||||
self.assertFalse(isinstance(db.test.find_one(), SON))
|
||||
|
||||
c.document_class = SON
|
||||
db = c.pymongo_test
|
||||
|
||||
self.assertEqual(SON, c.document_class)
|
||||
self.assertTrue(isinstance(db.test.find_one(), SON))
|
||||
@ -484,6 +485,7 @@ class TestMasterSlaveConnection(unittest.TestCase, TestRequestMixin):
|
||||
self.assertFalse(isinstance(db.test.find_one(as_class=dict), SON))
|
||||
|
||||
c.document_class = dict
|
||||
db = c.pymongo_test
|
||||
|
||||
self.assertEqual(dict, c.document_class)
|
||||
self.assertTrue(isinstance(db.test.find_one(), dict))
|
||||
|
||||
@ -565,6 +565,7 @@ class TestReplicaSetClient(TestReplicaSetClientBase, TestRequestMixin):
|
||||
self.assertFalse(isinstance(db.test.find_one(), SON))
|
||||
|
||||
c.document_class = SON
|
||||
db = c.pymongo_test
|
||||
|
||||
self.assertEqual(SON, c.document_class)
|
||||
self.assertTrue(isinstance(db.test.find_one(), SON))
|
||||
@ -579,6 +580,7 @@ class TestReplicaSetClient(TestReplicaSetClientBase, TestRequestMixin):
|
||||
self.assertFalse(isinstance(db.test.find_one(as_class=dict), SON))
|
||||
|
||||
c.document_class = dict
|
||||
db = c.pymongo_test
|
||||
|
||||
self.assertEqual(dict, c.document_class)
|
||||
self.assertTrue(isinstance(db.test.find_one(), dict))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user