Package: pbumongo
Python Basic Utilities - MongopbumongoAvailable onPyPiTable of ContentsInstallationUsageClassesAbstractMongoStore- abstract class for handling MongoDB collection accessMongoConnection- a helper class to assist with creating multiple store instancesAbstractMongoDocument- abstract class for wrapping MongoDB BSON documentsProgressUpdater- a collection of classes to help with updating job progressInstallationInstall via pip:pipinstallpbumongoUsageIt is good practice associating a sub-class ofAbstractMongoDocumentwith a sub-class ofAbstractMongoStore. This is
done through thedeserialised_classparameter in thesuper()constructor call of the store class. Any method for
querying documents will use that class to deserialise the BSON document into the provided class, which should extendAbstractMongoDocument.Example: let's say we want to implement access to a collection containing user documents. We'll define a classUserthat extendsAbstractMongoDocumentand a classUserStorethat extendsAbstractMongoStore.# main importsfrompbumongoimportAbstractMongoDocument,AbstractMongoStore# supporting importsimportcryptfromtypingimportList,Optionalfromtimeimporttime# this is an example of a minimum viable classclassUser(AbstractMongoDocument):def__init__(self):super().__init__()# define attributes with meaningful defaultsself.username:str=Noneself.password:str=Noneself.permissions:List[str]=[]self.last_login:int=0defget_attribute_mapping(self)->dict:# the values are what is used inside MongoDB documentsreturn{"username":"username","password":"password","permissions":"permissions","last_login":"lastLogin"}@staticmethoddeffrom_json(json:dict):user=User()user.extract_system_fields(json)returnuserclassUserStore(AbstractMongoStore):def__init__(self,mongo_url,mongo_db,collection_name):super().__init__(mongo_url,mongo_db,collection_name,deserialised_class=User,data_model_version=1)deflogin(self,username,password)->Optional[User]:# encrypt the password!pw_encrypted=crypt.crypt(password,crypt.METHOD_MD5)user:Optional[User]=self.query_one({"username":username,"password":pw_encrypted})ifuserisnotNone:# update last_login attribute and save it in database as welluser.last_login=round(time())self.update_one(AbstractMongoStore.id_query(user.id),AbstractMongoStore.set_update("lastLogin",user.last_login))returnuserdefcreate_user(self,username,password)->User:# check if this user already existsexisting=self.query_one({"username":username})ifexistingisnotNone:raiseValueError(f"User with username '{username}' already exists.")# create new user objectuser=User()user.username=usernameuser.password=crypt.crypt(password,crypt.METHOD_MD5)# store in database and return documentuser_id=self.create(user)returnself.get(user_id)MongoConnectionTo use these classes in your application, you can use the MongoConnection helper or create theUserStoreclass
instance directly. TheMongoConnectionhelper is useful, when you have a lot of collections and don't want to repeat
the mongo connection URL and DB name for every constructor.frompbumongoimportMongoConnectionfrommypackageimportUserStore# see implementation abovecon=MongoConnection("mongodb://localhost:27017","myDbName")user_store=con.create_store(store_class=UserStore,collection_name="users")user=user_store.login(username="admin",password="mypassword")ClassesAbstractMongoStoreThis is an abstract class and cannot be instantiated directly. Instead, define a class that extends this class.Constructor__init__(mongo_url, mongo_db, collection_name, deserialised_class, data_model_version=1)mongo_url- this is the Mongo connection URL containing the host, port and optional username, passwordmongo_db- this is the Mongo DB name - the one you provide when usinguse <dbname>on the Mongo shellcollection_name- the name of the collection - e.g.myCollectionfordb.myCollection.find({})on the Mongo shelldeserialised_class- used for all the query methods to deserialise the BSON document into a class with attributes
for easier accessdata_model_version- a number that can be used for database migration as an app develops over timeMethodsget(doc_id: str)- fetches a single document with a matchingdoc_id == document["_id"]get_all()- fetches the entire collection content and deserialises every document. Careful, this is not an iterator,
but returns alistof all the documents and can consume quite a bit of compute and memory.create(document)- creates a new document and returns the_idof the newly created BSON document as string. Thedocumentcan be eitherdictor an instance of thedeserialised_classprovided in thesuper().__init(..)call.Since version 1.0.1 a new parameter is availablecreate(document, return_doc=True)which will return the entire
document/object instead of just the_idof the newly created document.query_one(query: dict)- fetches a single document and deserialises it or returnsNoneif no document can be foundquery(query: dict, sorting, paging)- fetches multiple documents and deserialises them.sortingcan be an
attribute name (as provided in the BSON) or a dictionary with the sort order.pagingis an instance ofpbumongo.PagingInformation.update_one(query: dict, update: dict)- proxies thedb.collection.updateOne(..)function from the Mongo shellupdate(query:, update: dict- same asupdate_one, but will update multiple documents, if the query matchesupdate_full(document)- shortcut for updating the entire document with an updated version, the query will be
constructed from theid/_idprovided by thedocument.delete(doc_id)- deletes a single document with the provided document IDdelete_many(query: dict)- deletes multiple documents matching the query.Static MethodsAbstractMongoStore.id_query(string_id: str)- creates a query{ "_id": ObjectId(string_id) }, which can be used to
query the databaseAbstractMongoStore.set_update(keys, values)- creates a$setupdate statement. If only a single attribute is
updated, you can pass them directly as parameters, e.g. updating a key"checked": True, can be done by.set_update("checked", True). If you update multiple attributes provide them as list in the matching order.AbstractMongoStore.unset_update(keys)- creates an$unsetupdate statement with the attributes listed askeys.
Similarly to.set_update, you can provide a single key without a list for ease of use.AbstractMongoDocumentThis is an abstract class and cannot be instantiated directly. Instead, define a class that extends this class.Constructor__init__(doc_id=None, data_model_version=None)The parameters are entirely optional. Generally it is recommended to use the static methodfrom_json(json: dict)to
create BSON documents you've loaded from the database instead of calling the constructor. For new documents, you would
not provide the_idas the store class handles that.MethodsFor methods and static methods please see the documentation ofJsonDocumentfrompbu.AbstractMongoDocumentextends that class.ProgressUpdaterTheProgressUpdaerclass is part of a set of classes that assist with keeping track of job progress. The other classes
are:ProgressObject: a database object with fields for a status (seepbu>JobStatus), start and end timestamp,
total count, processed count, a list of errors and a main error.ProgressObjectStore: an abstract class that provides store methods to update status, progress and errors of aProgressObjectProgressError: a JSON document containing an error message as well as a dictionary for data related to the error.
These objects will be appeneded to aProgressObject'serrorslist.ProgressUpdater: an object to pass into a processor, which holds references to the progress store and progress
object and provides methods for updating progress and handling errors.Both,ProgressObjectandProgressObjectStoreare abstract classes and should be extended with remaining attributes
of a process / job definition (like a name/label, extra configuration, etc.).ProgressObjectis anAbstractMongoDocumentandProgressUpdateStoreis anAbstractMongoStore.