Package: python-mongodm
MongODMMongODM is a simple, lightweight, and asynchronous Object Document Mapper for MongoDB.
It uses Pydantic and Motor.Installationpipinstallpython-mongodmUsageimportmongodmfrommotor.motor_asyncioimportAsyncIOMotorClientmongodm.set_config(AsyncIOMotorClient('mongodb://localhost:27017'),'test')classEntity(mongodm.MongoODMBase):__collection_name__='my_entity'__protected_attributes__={'protected'}# Writable on first creation, but not on updatestitle:strdescription:strprotected:stritem=Entity(title='title',description='description',protected='protected')awaititem.save()# Commit in DBitem.dict()# {'title': 'title', 'description': 'description', 'protected': 'protected', created_at: datetime.datetime(), updated_at: None, deleted_at: None}db_items=awaitEntity.get_all()# List of instances from dbdb_items[0].title='modification'awaitdb_items[0].save()awaitdb_items[0].delete()# To change multiples attributes simultaneously, pydantic constructor stylenew_attributes_dict={'title':'edited','description':'edited'}item.set_attributes(**new_attributes_dict)The default _id field constructor isuuid.uuid4marshalled to a string. It is possible to change it by setting__id_constructor__to a callable that returns the desired type.It will still be casted to a string before being written in the database. If you want this field to be of another type than a string in Mongo, you can set__id_marshaller__to any type accepted by MongoDB.The configuration for the database can be set withset_config()You can enable soft delete by using set_config withsoft_delete=True.mongodm.set_config(AsyncIOMotorClient("mongodb://localhost:27017"),"my_database",soft_delete=True)Since the whole thing is based on Pydantic, you can use all the features of Pydantic.frompydanticimportField,validatorclassEntity(mongodm.MongoODMBase):__collection_name__='my_entity'title:strdescription:strprotected:str=Field(default='protected',description='This is a protected field')@validator('title')deftitle_must_contain_space(cls,v):if' 'notinv:raiseValueError('must contain a space')returnv.title()You can even modify the pydantic config by creating a new class that inherits from MongoODMBase and override the pydantic Config subclass.classMyBase(mongodm.MongoODMBase):classConfig(mongodm.MongoODMBase.Config):allow_population_by_field_name=TrueclassEntity(MyBase):...Encrypted fieldsIt is possible to encrypt string fields in the database by using theEncryptedStrclass. It uses rsa to encrypt the data and store it in the database as a string.
MongODM will automatically decrypt the data when it is retrieved from the database.
You will need to configure the encryption by usingset_encryption_config().This approach is very opinionated, and is proposed as an alternative if you don't want to use the MongoDB encryption features.importmongodmmongodm.set_encryption_config(public_key='YOUR_RSA_PUBLIC_KEY_VALUE',private_key='YOUR_RSA_PRIVATE_KEY_VALUE')HooksYou can use hooks to execute code before or after a variety of events. The following hooks are available:before_create(): Before the object is created in the database.after_create(): After the object is created in the database.before_save(): Before the object is saved in the database, called both during creation and update operations.after_save(): After the object is saved in the database, called both during creation and update operations.before_update(payload): Before the object is updated in the database. You can access the payload with thepayloadargument, and you MUST return it.after_update(): After the object is updated in the databaseafter_find(): After the object is found in the databasebefore_delete(): Before the object is deleted in the databaseafter_delete(): After the object is deleted in the databaseafter_soft_delete()(only if soft delete is enabled)after_hard_delete()(only if soft delete is disabled)All the hooks must be declared as async functions. To define one you have to override the method on your class with the same signature as the hook you want to use.defbefore_create(self):self.created_at=datetime.now().isoformat()# You must use the same method signature and return it at the enddefbefore_update(self,payload):payload['updated_at']=datetime.now().isoformat()returnpayloadContributingContributions are welcome! Please open an issue or a pull request.
We are looking for reviews and feedback on the basic design of the library.LicenseMIT