00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 """Manipulators that can edit SON objects as they enter and exit a database.
00016
00017 New manipulators should be defined as subclasses of SONManipulator and can be
00018 installed on a database by calling
00019 `pymongo.database.Database.add_son_manipulator`."""
00020
00021 from bson.dbref import DBRef
00022 from bson.objectid import ObjectId
00023 from bson.son import SON
00024
00025
00026 class SONManipulator(object):
00027 """A base son manipulator.
00028
00029 This manipulator just saves and restores objects without changing them.
00030 """
00031
00032 def will_copy(self):
00033 """Will this SON manipulator make a copy of the incoming document?
00034
00035 Derived classes that do need to make a copy should override this
00036 method, returning True instead of False. All non-copying manipulators
00037 will be applied first (so that the user's document will be updated
00038 appropriately), followed by copying manipulators.
00039 """
00040 return False
00041
00042 def transform_incoming(self, son, collection):
00043 """Manipulate an incoming SON object.
00044
00045 :Parameters:
00046 - `son`: the SON object to be inserted into the database
00047 - `collection`: the collection the object is being inserted into
00048 """
00049 if self.will_copy():
00050 return SON(son)
00051 return son
00052
00053 def transform_outgoing(self, son, collection):
00054 """Manipulate an outgoing SON object.
00055
00056 :Parameters:
00057 - `son`: the SON object being retrieved from the database
00058 - `collection`: the collection this object was stored in
00059 """
00060 if self.will_copy():
00061 return SON(son)
00062 return son
00063
00064
00065 class ObjectIdInjector(SONManipulator):
00066 """A son manipulator that adds the _id field if it is missing.
00067 """
00068
00069 def transform_incoming(self, son, collection):
00070 """Add an _id field if it is missing.
00071 """
00072 if not "_id" in son:
00073 son["_id"] = ObjectId()
00074 return son
00075
00076
00077
00078
00079
00080 class ObjectIdShuffler(SONManipulator):
00081 """A son manipulator that moves _id to the first position.
00082 """
00083
00084 def will_copy(self):
00085 """We need to copy to be sure that we are dealing with SON, not a dict.
00086 """
00087 return True
00088
00089 def transform_incoming(self, son, collection):
00090 """Move _id to the front if it's there.
00091 """
00092 if not "_id" in son:
00093 return son
00094 transformed = SON({"_id": son["_id"]})
00095 transformed.update(son)
00096 return transformed
00097
00098
00099 class NamespaceInjector(SONManipulator):
00100 """A son manipulator that adds the _ns field.
00101 """
00102
00103 def transform_incoming(self, son, collection):
00104 """Add the _ns field to the incoming object
00105 """
00106 son["_ns"] = collection.name
00107 return son
00108
00109
00110 class AutoReference(SONManipulator):
00111 """Transparently reference and de-reference already saved embedded objects.
00112
00113 This manipulator should probably only be used when the NamespaceInjector is
00114 also being used, otherwise it doesn't make too much sense - documents can
00115 only be auto-referenced if they have an *_ns* field.
00116
00117 NOTE: this will behave poorly if you have a circular reference.
00118
00119 TODO: this only works for documents that are in the same database. To fix
00120 this we'll need to add a DatabaseInjector that adds *_db* and then make
00121 use of the optional *database* support for DBRefs.
00122 """
00123
00124 def __init__(self, db):
00125 self.__database = db
00126
00127 def will_copy(self):
00128 """We need to copy so the user's document doesn't get transformed refs.
00129 """
00130 return True
00131
00132 def transform_incoming(self, son, collection):
00133 """Replace embedded documents with DBRefs.
00134 """
00135
00136 def transform_value(value):
00137 if isinstance(value, dict):
00138 if "_id" in value and "_ns" in value:
00139 return DBRef(value["_ns"], transform_value(value["_id"]))
00140 else:
00141 return transform_dict(SON(value))
00142 elif isinstance(value, list):
00143 return [transform_value(v) for v in value]
00144 return value
00145
00146 def transform_dict(object):
00147 for (key, value) in object.items():
00148 object[key] = transform_value(value)
00149 return object
00150
00151 return transform_dict(SON(son))
00152
00153 def transform_outgoing(self, son, collection):
00154 """Replace DBRefs with embedded documents.
00155 """
00156
00157 def transform_value(value):
00158 if isinstance(value, DBRef):
00159 return self.__database.dereference(value)
00160 elif isinstance(value, list):
00161 return [transform_value(v) for v in value]
00162 elif isinstance(value, dict):
00163 return transform_dict(SON(value))
00164 return value
00165
00166 def transform_dict(object):
00167 for (key, value) in object.items():
00168 object[key] = transform_value(value)
00169 return object
00170
00171 return transform_dict(SON(son))
00172
00173
00174
00175
00176
00177