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:
Bernie Hackett 2015-06-11 14:54:04 -07:00
parent 4cf64b6170
commit 9b1ac9717f
20 changed files with 436 additions and 68 deletions

View File

@ -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]]]]]])

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
------------------------

View File

@ -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?

View File

@ -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']:

View File

@ -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 = (

View File

@ -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

View File

@ -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.

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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))

View File

@ -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

View File

@ -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,

View File

@ -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")

View File

@ -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))

View File

@ -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))