Skip to content

Commit df4e526

Browse files
committed
PYTHON-821 - Implement insert_one.
1 parent 7416e65 commit df4e526

4 files changed

Lines changed: 91 additions & 9 deletions

File tree

doc/api/pymongo/collection.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
.. autodata:: pymongo.cursor.TAILABLE_AWAIT
1717
.. autodata:: pymongo.cursor.EXHAUST
1818

19+
.. autoclass:: pymongo.collection.InsertOneResult
20+
:members:
21+
1922
.. autoclass:: pymongo.collection.ReturnDocument
2023

2124
.. autoattribute:: Before
@@ -39,6 +42,7 @@
3942
.. autoattribute:: read_preference
4043
.. autoattribute:: write_concern
4144
.. automethod:: with_options
45+
.. automethod:: insert_one
4246
.. automethod:: insert(doc_or_docs[, manipulate=True[, check_keys=True[, continue_on_error=False[, **kwargs]]]])
4347
.. automethod:: save(to_save[, manipulate=True[, check_keys=True[, **kwargs]]])
4448
.. automethod:: update(spec, document[, upsert=False[, manipulate=False[, multi=False[, check_keys=True[, **kwargs]]]]])

pymongo/bulk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ def bulk_api_result(self):
231231

232232
@property
233233
def acknowledged(self):
234-
"""Was this bulk write operation acknowledged?"""
234+
"""Is this the result of an acknowledged bulk write operation?"""
235235
return self.__acknowledged
236236

237237
@property

pymongo/collection.py

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from pymongo.helpers import _check_write_command_response, _command
4040
from pymongo.message import _INSERT, _UPDATE, _DELETE
4141
from pymongo.read_preferences import ReadPreference
42+
from pymongo.write_concern import WriteConcern
4243

4344

4445
try:
@@ -50,6 +51,26 @@
5051
_NO_OBJ_ERROR = "No matching object found"
5152

5253

54+
class InsertOneResult(object):
55+
"""The return type for :meth:`Collection.insert_one`."""
56+
57+
__slots__ = ("__inserted_id", "__acknowledged")
58+
59+
def __init__(self, inserted_id, acknowledged):
60+
self.__inserted_id = inserted_id
61+
self.__acknowledged = acknowledged
62+
63+
@property
64+
def inserted_id(self):
65+
"""The inserted document's _id."""
66+
return self.__inserted_id
67+
68+
@property
69+
def acknowledged(self):
70+
"""Is this the result of an acknowledged write operation?"""
71+
return self.__acknowledged
72+
73+
5374
class ReturnDocument(object):
5475
"""An enum used with :meth:`Collection.find_one_and_replace` and
5576
:meth:`Collection.find_one_and_update`.
@@ -483,8 +504,34 @@ def insert(self, doc_or_docs, manipulate=True,
483504
484505
.. mongodoc:: insert
485506
"""
507+
write_concern = None
508+
if kwargs:
509+
write_concern = WriteConcern(**kwargs)
510+
return self.__insert(doc_or_docs, not continue_on_error,
511+
check_keys, manipulate, write_concern)
512+
513+
def insert_one(self, document):
514+
"""Insert a single document.
515+
516+
:Parameters:
517+
- `document`: The document to insert. Must be a mutable mapping
518+
type. If the document does not have an _id field one will be
519+
added automatically.
520+
521+
:Returns:
522+
- An instance of :class:`InsertOneResult`.
523+
"""
524+
if not isinstance(document, collections.MutableMapping):
525+
raise TypeError("document must be a mutable mapping type")
526+
if "_id" not in document:
527+
document["_id"] = ObjectId()
528+
return InsertOneResult(self.__insert(document),
529+
self.write_concern.acknowledged)
530+
531+
def __insert(self, docs, ordered=True,
532+
check_keys=True, manipulate=False, write_concern=None):
533+
"""Internal insert helper."""
486534
client = self.database.connection
487-
docs = doc_or_docs
488535
return_one = False
489536
if isinstance(docs, collections.MutableMapping):
490537
return_one = True
@@ -512,13 +559,13 @@ def gen():
512559
ids.append(doc.get('_id'))
513560
yield doc
514561

515-
concern = kwargs or self.write_concern.document
562+
concern = (write_concern or self.write_concern).document
516563
safe = concern.get("w") != 0
517564

518565
if client._writable_max_wire_version() > 1 and safe:
519566
# Insert command
520567
command = SON([('insert', self.name),
521-
('ordered', not continue_on_error)])
568+
('ordered', ordered)])
522569

523570
if concern:
524571
command['writeConcern'] = concern
@@ -530,9 +577,8 @@ def gen():
530577
else:
531578
# Legacy batched OP_INSERT
532579
message._do_batched_insert(self.__full_name, gen(), check_keys,
533-
safe, concern, continue_on_error,
580+
safe, concern, not ordered,
534581
self.codec_options, client)
535-
536582
if return_one:
537583
return ids[0]
538584
else:

test/test_collection.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@
3535
from pymongo import (ASCENDING, DESCENDING, GEO2D,
3636
GEOHAYSTACK, GEOSPHERE, HASHED, TEXT)
3737
from pymongo import MongoClient
38-
from pymongo.collection import Collection, ReturnDocument
38+
from pymongo.collection import Collection, ReturnDocument, InsertOneResult
3939
from pymongo.command_cursor import CommandCursor
4040
from pymongo.cursor import EXHAUST
41-
from pymongo.errors import (DocumentTooLarge,
41+
from pymongo.errors import (ConfigurationError,
42+
DocumentTooLarge,
4243
DuplicateKeyError,
4344
InvalidDocument,
4445
InvalidName,
@@ -605,6 +606,37 @@ def remove_insert_find_one(doc):
605606
qcheck.check_unittest(self, remove_insert_find_one,
606607
qcheck.gen_mongo_dict(3))
607608

609+
def test_insert_one(self):
610+
db = self.db
611+
db.test.drop()
612+
613+
document = {"_id": 1000}
614+
result = db.test.insert_one(document)
615+
self.assertTrue(isinstance(result, InsertOneResult))
616+
self.assertTrue(isinstance(result.inserted_id, int))
617+
self.assertEqual(document["_id"], result.inserted_id)
618+
self.assertTrue(result.acknowledged)
619+
self.assertIsNotNone(db.test.find_one({"_id": document["_id"]}))
620+
self.assertEqual(1, db.test.count())
621+
622+
document = {"foo": "bar"}
623+
result = db.test.insert_one(document)
624+
self.assertTrue(isinstance(result, InsertOneResult))
625+
self.assertTrue(isinstance(result.inserted_id, ObjectId))
626+
self.assertEqual(document["_id"], result.inserted_id)
627+
self.assertTrue(result.acknowledged)
628+
self.assertIsNotNone(db.test.find_one({"_id": document["_id"]}))
629+
630+
db = db.connection.get_database(db.name,
631+
write_concern=WriteConcern(w=0))
632+
result = db.test.insert_one(document)
633+
self.assertTrue(isinstance(result, InsertOneResult))
634+
self.assertTrue(isinstance(result.inserted_id, ObjectId))
635+
self.assertEqual(document["_id"], result.inserted_id)
636+
self.assertFalse(result.acknowledged)
637+
# The insert failed duplicate key...
638+
self.assertEqual(2, db.test.count())
639+
608640
def test_generator_insert(self):
609641
db = self.db
610642
db.test.remove({})
@@ -1231,7 +1263,7 @@ def wtimeout_err(f, *args, **kwargs):
12311263
wtimeout_err(coll.remove, {"x": 1}, w=w, wtimeout=1)
12321264

12331265
# can't use fsync and j options together
1234-
self.assertRaises(OperationFailure, self.db.test.insert,
1266+
self.assertRaises(ConfigurationError, self.db.test.insert,
12351267
{"_id": 1}, j=True, fsync=True)
12361268

12371269
def test_manual_last_error(self):

0 commit comments

Comments
 (0)