Package: aio-mongo-dm
aio-mongo-dmasynchronous lightweight ODM for MongoDB based onmotorSuitable Application EnvironmentThe goal of this package is to create an asynchronous, simple and intuitive ODM, which can be easily applied to the python asynchronous framework system.
If you happen to like asynchronous framework very much and use mongodb as your data storage system.[Motor documentation][https://motor.readthedocs.io/en/stable/index.html]Installationpipinstallaio-mongo-dmQuick Startimportasynciofromdatetimeimportdatetimefromaio_mongo_dmimportDocumentclassUser(Document):# customize document field with schema data__schema__={'name':{'type':str,'default':'my_default_name','index':1},'age':{'type':int,'default':20,'index':-1},'sex':{'type':bool,'default':True},'createdAt':{'type':datetime,'index':-1},'updatedAt':{'type':datetime}}# customize class method@classmethodasyncdeffind_one_by_name(cls,user_name):returnawaitcls.find_one({'name':user_name})# hook method, after user.save()asyncdefafter_save(self):print('after_save User hook method')_db_url='mongodb://localhost:27000'asyncdefmain():# init whole document/collection with mongodb url and db_nameawaitDocument.init_db(url=_db_url,db_name='mytest')# create User instanceuser=User()assertuser.name=='my_default_name'assertuser.sexisTrue# _id not existassert'_id'notinusercount1=awaitUser.count({'age':{'$lg':10}})# save user object to db， then return _id (mongodb's unique index )awaituser.save()# _id existassert'_id'inusercount2=awaitUser.count({})assertcount2==count1+1print(f'User count={count2}')user_with_name=awaitUser.find_one_by_name('my_default_name')print(user_with_name,user_with_name.updatedAt)assertisinstance(user_with_name,User)cursor=User.find({'age':{'$gt':10}}).sort('age')fordocumentinawaitcursor.to_list(length=100):print(document)if__name__=='__main__':asyncio.run(main())Create Indeximportasynciofromdatetimeimportdatetimefromaio_mongo_dmimportDocumentclassUser(Document):# set DB name of this document__db_name__='mytest'# customize class with schema data__schema__={'name':{'type':str,'default':'my_default_name','index':1},'age':{'type':int,'default':20,'index':-1},'sex':{'type':bool,'default':True},'createdAt':{'type':datetime,'index':-1},'updatedAt':{'type':datetime}}# customize class method@classmethodasyncdeffind_one_by_name(cls,user_name):returnawaitcls.find_one({'name':user_name})# hook method, after user.save()asyncdefafter_save(self):print('after_save User hook method')_db_url='mongodb://localhost:27000'asyncdefmain():# init whole document/collection with mongodb url，with default db_name=testawaitDocument.init_db(url=_db_url)# 1、create single index respectively according to User.__schema__ index value, e.g. name 1, createdAt -1single_index_results=awaitUser.create_index()print('single_index_results',single_index_results)# 2、 create compound indexcompound_index_result=awaitUser.create_compound_index([('name',1),('createdAt',-1)])print('compound_index_result',compound_index_result)# 3、 get all index informationindex_information=awaitUser.get_index_infor()print('index_information',index_information)if__name__=='__main__':asyncio.run(main())TranscationNotice: transaction need replication set env, e.g. one Primary Server, one Secondary Server, one Arbiter Server. [Detail Configuration][https://www.mongodb.com/docs/v5.0/reference/configuration-options/]frombson.objectidimportObjectIdfromdatetimeimportdatetimeimportasynciofromaio_mongo_dmimportDocument,AioClient_db_url='mongodb://localhost:27000'classUser(Document):# customize class with schema data__schema__={'name':{'type':str,'default':'my_default_name','index':1},'age':{'type':int,'default':20,'index':-1},'sex':{'type':bool,'default':True},'createdAt':{'type':datetime,'index':-1},'updatedAt':{'type':datetime}}classPayOrder(Document):# customize class with schema data__schema__={'user_id':{'type':ObjectId},'total_fee':{'type':int},'status':{'type':str,'default':'Normal'},'createdAt':{'type':datetime,'index':-1},'updatedAt':{'type':datetime}}asyncdeftrancation_test(is_raise_exception):client=AioClient(client_name='__local__')asyncwithawaitclient.start_session()ass:asyncwiths.start_transaction():user=User()user.name='kavin'new_user=awaituser.save(session=s)pay_order=PayOrder()pay_order.user_id=user['_id']pay_order.total_fee=100pay_order.status='payed'new_order=awaitpay_order.save(session=s)assertnew_user['_id']==new_order['user_id']ifis_raise_exception:raiseException('trancation_test exception')asyncdefmain():# init whole document/collection with mongodb url and db_nameawaitDocument.init_db(url=_db_url,client_name='__local__',db_name='mytest')user_count1=awaitUser.count()new_order_count1=awaitPayOrder.count()# successful transactionawaittrancation_test(False)user_count2=awaitUser.count()new_order_count2=awaitPayOrder.count()# count +1assertuser_count2==user_count1+1assertnew_order_count2==new_order_count1+1try:# failed transactionawaittrancation_test(True)exceptExceptionase:assertstr(e)=='trancation_test exception'user_count3=awaitUser.count()new_order_count3=awaitPayOrder.count()# count nor changeassertuser_count3==user_count2assertnew_order_count3==new_order_count2print('trancation test ok.')if__name__=='__main__':asyncio.run(main())More ExampleFor more examples, please query the example folder.API ReferenceDocument__db_url__set db url of this document. you can set in sub-class Document or use Document class method init_db(url='mongodb://localhost:27017')default: 'mongodb://localhost:27017'__db_name__optional. Attribute for setting up the database. you can set in sub-class Document or use Document class method init_db(db_name='mytest')default: 'test'__collection_name__optional. Attribute for setting up the collection name. e.g. the class name is 'User', so default collection name is 'users' if not set.default:  '{class name}.lower() + s'__schema__Set the initializing data for all objects in the collection when the object is initialized. Defined field default value and type will be checked .save(session=None)Coroutine. It saves the object in the database, attribute '_id' will be generated if successsession:  ClientSession instance for transaction operationdelete()Coroutine. It remove an object from the database. If the object does not exist in the database,
then theAioMongoDocumentDoesNotExistexception will be raised.refresh(session=None)Coroutine. Refresh the current object from the same object from the database.
If the object does not exist in the database, then theAioMongoDocumentDoesNotExistexception will be raised.session:  ClientSession instance for transaction operationpre_save()Hook Method. This method is called before the save() method. You can override this method in a subclass.
If this method is not overridden, 'updatedAt' and 'createdAt' will be updated with datetime.now() by default if the field key defined inschema.after_save()Hook Method. This method is called after the save() method. You can override this method in a subclass.init_db(url: str = None, db_name: str = 'test', client_name: str = '__local__', io_loop: _UnixSelectorEventLoop = None, **kwargs) -> AioClientCoroutine Class Method. init mongodb method, create AioClient instance and set class attributeaio_clienturl:  mongodb urldb_name:  database nameclient_name:  name for cache aio clientio_loop:  asyncio event loopawaitDocument.init_db(url='mongodb://localhost:27000',db_name='mytest')create_instance(obj: object) -> Optional[_Document]Class Method. Create a document instance through an object, e.g.  User.create_instance({'name': 'kavin', 'age': 30})obj:  an object instancefind_by_id(oid: Union[str, ObjectId], session=None) -> Optional[_Document]Coroutine Class Method. document query based on document ID. e.g. User.find_by_id('xxxxxxx')oid:  Document ID, str or ObjectIdsession:  ClientSession instance for transaction operationdelete_by_id(oid: Union[str, ObjectId], session=None)Coroutine Class Method. delete document instance according to document ID. e.g. User.delete_by_id('xxxxxxx')oid:  Document ID, str or ObjectIdsession:  ClientSession instance for transaction operationfind(*args, session: Optional[ClientSession] = None,**kwargs) -> AsyncIOMotorCursorClass Method. Querying for More Than One Document, create a AsyncIOMotorCursor.cursor=User.find({'age':{'$gt':10}}).sort('age')fordocumentinawaitcursor.to_list(length=100):print(document)find_one(*args, session: Optional[ClientSession] = None, **kwargs) -> Optional[_Document]Coroutine Class Method. Getting a Single Document, return None if no matching document is found.doc=awaitUser.find_one({'age':{'$gt':10}}).sort('age')count(*filters: Mapping[str, Any], session: Optional[ClientSession] = None, **kwargs) -> intCoroutine Class Method. Count the number of documents in this collection.filters:  A query document that selects which documents to count in the collection.session:  ClientSession instance for transaction operationusers_num=awaitUser.count()users_name_num=awaitUser.count({'name':'kavin'})users_age_num=awaitUser.count({'age':{$gt:30}}})get_collection(db_name: str = None) -> AioCollectionClass Method. get aio collection through the specified db name, if db_name is not None.
use attributedb_nameif db_name is None.db_name:  database namecreate_index(session: Optional[ClientSession] = None) -> listCoroutine Class Method. create index on this collection. When defining document subclasses, index can be defined in schema，
In this function, we will create all the previously defined default indexes one by one
return index str listsession:  ClientSession instance for transaction operationclassUser(Document):__schema__={'name':{'type':str,'default':'my_default_name','index':-1},'sex':{'type':bool},'age':{'type':int,'default':20,'index':1},'createdAt':{'type':datetime,'index':-1},'updatedAt':{'type':datetime}}res=awaitUser.create_index()assertres==['index_-1','age_1','createdAt_-1']create_compound_index(keys: Union[str, Sequence[Tuple[str, Union[int, str, Mapping[str, Any]]]]], session: Optional[ClientSession] = None) -> strCoroutine Class Method. create compound index on this collection.keys:  list of index key and index valuesession:  ClientSession instance for transaction operationclassUser(Document):__schema__={'name':{'type':str,'default':'my_default_name','index':-1},'sex':{'type':bool},'age':{'type':int,'default':20,'index':1},'createdAt':{'type':datetime,'index':-1},'updatedAt':{'type':datetime}}keys=[('name',1),('createdAt',-1)]res=awaitUser.create_compound_index(keys)assertres=='name_1_createdAt_-1'get_index_infor(session: Optional[ClientSession] = None) -> strCoroutine Class Method. Get information on this collection’s indexes.session:  ClientSession instance for transaction operation