33import re
44import warnings
55
6+ from collections .abc import Mapping
7+
68from bson import SON , json_util
79from bson .code import Code
810import pymongo
911import pymongo .errors
1012from pymongo .collection import ReturnDocument
1113from pymongo .common import validate_read_preference
14+ from pymongo .read_concern import ReadConcern
1215
1316from mongoengine import signals
1417from mongoengine .base import get_document
1518from mongoengine .common import _import_class
1619from mongoengine .connection import get_db
17- from mongoengine .context_managers import set_write_concern , switch_db
20+ from mongoengine .context_managers import (
21+ set_read_write_concern ,
22+ set_write_concern ,
23+ switch_db ,
24+ )
1825from mongoengine .errors import (
1926 BulkWriteError ,
2027 InvalidQueryError ,
@@ -57,6 +64,7 @@ def __init__(self, document, collection):
5764 self ._snapshot = False
5865 self ._timeout = True
5966 self ._read_preference = None
67+ self ._read_concern = None
6068 self ._iter = False
6169 self ._scalar = []
6270 self ._none = False
@@ -484,7 +492,13 @@ def delete(self, write_concern=None, _from_doc_delete=False, cascade_refs=None):
484492 return result .deleted_count
485493
486494 def update (
487- self , upsert = False , multi = True , write_concern = None , full_result = False , ** update
495+ self ,
496+ upsert = False ,
497+ multi = True ,
498+ write_concern = None ,
499+ read_concern = None ,
500+ full_result = False ,
501+ ** update
488502 ):
489503 """Perform an atomic update on the fields matched by the query.
490504
@@ -496,6 +510,7 @@ def update(
496510 ``save(..., write_concern={w: 2, fsync: True}, ...)`` will
497511 wait until at least two servers have recorded the write and
498512 will force an fsync on the primary server.
513+ :param read_concern: Override the read concern for the operation
499514 :param full_result: Return the associated ``pymongo.UpdateResult`` rather than just the number
500515 updated items
501516 :param update: Django-style update keyword arguments
@@ -522,7 +537,9 @@ def update(
522537 else :
523538 update ["$set" ] = {"_cls" : queryset ._document ._class_name }
524539 try :
525- with set_write_concern (queryset ._collection , write_concern ) as collection :
540+ with set_read_write_concern (
541+ queryset ._collection , write_concern , read_concern
542+ ) as collection :
526543 update_func = collection .update_one
527544 if multi :
528545 update_func = collection .update_many
@@ -539,7 +556,7 @@ def update(
539556 raise OperationError (message )
540557 raise OperationError ("Update failed (%s)" % err )
541558
542- def upsert_one (self , write_concern = None , ** update ):
559+ def upsert_one (self , write_concern = None , read_concern = None , ** update ):
543560 """Overwrite or add the first document matched by the query.
544561
545562 :param write_concern: Extra keyword arguments are passed down which
@@ -548,6 +565,7 @@ def upsert_one(self, write_concern=None, **update):
548565 ``save(..., write_concern={w: 2, fsync: True}, ...)`` will
549566 wait until at least two servers have recorded the write and
550567 will force an fsync on the primary server.
568+ :param read_concern: Override the read concern for the operation
551569 :param update: Django-style update keyword arguments
552570
553571 :returns the new or overwritten document
@@ -559,6 +577,7 @@ def upsert_one(self, write_concern=None, **update):
559577 multi = False ,
560578 upsert = True ,
561579 write_concern = write_concern ,
580+ read_concern = read_concern ,
562581 full_result = True ,
563582 ** update
564583 )
@@ -1177,6 +1196,22 @@ def read_preference(self, read_preference):
11771196 queryset ._cursor_obj = None # we need to re-create the cursor object whenever we apply read_preference
11781197 return queryset
11791198
1199+ def read_concern (self , read_concern ):
1200+ """Change the read_concern when querying.
1201+
1202+ :param read_concern: override ReplicaSetConnection-level
1203+ preference.
1204+ """
1205+ if read_concern is not None and not isinstance (read_concern , Mapping ):
1206+ raise TypeError ("%r is not a valid read concern." % (read_concern ,))
1207+
1208+ queryset = self .clone ()
1209+ queryset ._read_concern = (
1210+ ReadConcern (** read_concern ) if read_concern is not None else None
1211+ )
1212+ queryset ._cursor_obj = None # we need to re-create the cursor object whenever we apply read_concern
1213+ return queryset
1214+
11801215 def scalar (self , * fields ):
11811216 """Instead of returning Document instances, return either a specific
11821217 value or a tuple of values in order.
@@ -1623,9 +1658,9 @@ def _cursor(self):
16231658 # XXX In PyMongo 3+, we define the read preference on a collection
16241659 # level, not a cursor level. Thus, we need to get a cloned collection
16251660 # object using `with_options` first.
1626- if self ._read_preference is not None :
1661+ if self ._read_preference is not None or self . _read_concern is not None :
16271662 self ._cursor_obj = self ._collection .with_options (
1628- read_preference = self ._read_preference
1663+ read_preference = self ._read_preference , read_concern = self . _read_concern
16291664 ).find (self ._query , ** self ._cursor_args )
16301665 else :
16311666 self ._cursor_obj = self ._collection .find (self ._query , ** self ._cursor_args )
0 commit comments