Package: service-repository
Abstract service layer built into the repository pattern for SQLAlchemy or MongoDB (asynchronous)Asynchronous abstract methods for service layer built into the repository pattern for SQLAlchemy or MongoDB with the same service layer.Inherit the usual service methods:pagination,create,get,update,delete,countInstallation$pipinstallservice-repository---> 100%With SQLAlchemyCreate the model, as in the examplesong/models.pyCreate and extend ofBaseRepositorySqlalchemyin you repository, as in the examplesong/repositories.pyCreate and extend you service, as in the examplesong/services.pyThe following is an example with the connection session, but pass the session as needed.importpytestfromsqlalchemy.ext.asyncioimportAsyncSession,create_async_enginefromsqlalchemy.ormimportsessionmakerfromsqlmodelimportSQLModel# Get you connection session of SQLAlchemy, example next, but make as you@pytest.fixture()asyncdefsqlalchemy():SQLALCHEMY_DATABASE_URI="sqlite+aiosqlite:///./tests/testing.db"engine=create_async_engine(SQLALCHEMY_DATABASE_URI,future=True,echo=True)async_session=sessionmaker(bind=engine,expire_on_commit=False,class_=AsyncSession)asyncwithengine.begin()asconn:awaitconn.run_sync(SQLModel.metadata.create_all)yieldasync_sessionawaitconn.run_sync(SQLModel.metadata.drop_all)The following is an example with register created.importpytestfromtests.song.servicesimportSongServicefromtests.song.modelsimportSongCreate@pytest.fixtureasyncdefsong_one(sqlalchemy):song_create={"title":"Song title 1","is_active":True,}asyncwithsqlalchemy()assession:song=awaitSongService(db=session).create(schema_in=SongCreate(**song_create))returnsongPagination with dynamic filter and sorting in the service.importpytestfromtests.song.servicesimportSongService@pytest.mark.asyncioasyncdeftest_song_service_with_or_and_asc_filter_paginate_song(sqlalchemy):criteria=[{"field":"foo","op":"ilike","value":"%bar%"},]# Or with criteria in format orcriteria=[{"or":[{"field":"title","op":"==","value":"Song title 2"},{"field":"title","op":"==","value":"Song title 3"},]}]sort=[{"field":"title","direction":"asc"},]asyncwithsqlalchemy()assession:pagination=awaitSongService(db=session).paginate(page=1,per_page=5,criteria=criteria,sort=sort)Operator options for filtering are:is_null, is_not_null, eq, ne, gt, lt, ge, le, like, ilike, not_ilike, in, not_in, any, not_anyUse the create extended method on serviceimportpytestfromtests.song.servicesimportSongServicefromtests.song.modelsimportSongCreate@pytest.mark.asyncioasyncdeftest_song_service_create_song(sqlalchemy):song_data_one={"title":"Song title 1","is_active":True,}asyncwithsqlalchemy()assession:song=awaitSongService(db=session).create(schema_in=SongCreate(**song_data_one))Update method on serviceimportpytestfromtests.song.servicesimportSongServicefromtests.song.modelsimportSongUpdate@pytest.mark.asyncioasyncdeftest_song_service_update_song(sqlalchemy,song_one):song_data={"title":"Song title 2","is_active":False,}asyncwithsqlalchemy()assession:song=awaitSongService(db=session).update(instance=song_one,schema_in=SongUpdate(**song_data))Get method on service to get one registerimportpytestfromtests.song.servicesimportSongService@pytest.mark.asyncioasyncdeftest_song_service_get_song(sqlalchemy,song_one):asyncwithsqlalchemy()assession:song=awaitSongService(db=session).get(id=song_one.id)Delete method on serviceimportpytestfromtests.song.servicesimportSongService@pytest.mark.asyncioasyncdeftest_song_service_delete_song(sqlalchemy,song_one):asyncwithsqlalchemy()assession:awaitSongService(db=session).delete(id=song_one.id)Count method on serviceimportpytestfromtests.song.servicesimportSongService@pytest.mark.asyncioasyncdeftest_song_service_count_song(sqlalchemy):asyncwithsqlalchemy()assession:total=awaitSongService(db=session).count()With MongoDBCreate the model, as in the exampleproduct/models.pyCreate and extend ofBaseRepositoryMotorin you repository, as in the exampleproduct/repositories.pyCreate and extend you service, as in the exampleproduct/services.pyThe following is an example with the connection session, but pass the session as needed.importpytestfrommotor.motor_asyncioimportAsyncIOMotorClient# Get you connection session of Motor, example next, but make as you@pytest.fixture()asyncdefmotor():client=AsyncIOMotorClient("localhost",maxPoolSize=10,minPoolSize=10,tz_aware=True,)awaitclient.drop_database("testing")session=client.get_database("testing")yieldsessionThe following is an example with register created.importpytestfromtests.product.modelsimportProductCreatefromtests.product.servicesimportProductService@pytest.fixtureasyncdefproduct_one(motor,product_data_one):product_data={"title":"Product title 1","is_active":True,}product=awaitProductService(db=motor).create(schema_in=ProductCreate(**product_data))returnproductPagination with dynamic filter and sorting in the service.importpytestfromtests.product.servicesimportProductService@pytest.mark.asyncioasyncdeftest_product_service_with_or_and_asc_filter_paginate_product(app,motor,product_data_one):criteria={"title":"Product title 1"}sort=[("title",-1)]pagination=awaitProductService(db=motor).paginate(page=1,per_page=5,criteria=criteria,sort=sort)Use the create extended method on serviceimportpytestfromtests.product.modelsimportProductCreatefromtests.product.servicesimportProductService@pytest.mark.asyncioasyncdeftest_product_service_create_product(motor):product_data={"title":"Product title 1","is_active":True,}product=awaitProductService(db=motor).create(schema_in=ProductCreate(**product_data))Update method on serviceimportpytestfromtests.product.modelsimportProductUpdatefromtests.product.servicesimportProductService@pytest.mark.asyncioasyncdeftest_product_service_update_product(motor,product_one):product_data={"title":"Product title 2","is_active":False,}product=awaitProductService(db=motor).update(instance=product_one,schema_in=ProductUpdate(**product_data))Get method on service to get one registerimportpytestfromtests.product.servicesimportProductService@pytest.mark.asyncioasyncdeftest_product_service_get_product(motor,product_one):product=awaitProductService(db=motor).get(_id=product_one.id)Delete method on serviceimportpytestfromtests.product.servicesimportProductService@pytest.mark.asyncioasyncdeftest_product_service_delete_product(motor,product_one):awaitProductService(db=motor).delete(_id=product_one.id)Count method on serviceimportpytestfromtests.product.servicesimportProductService@pytest.mark.asyncioasyncdeftest_product_service_count_product(motor):total=awaitProductService(db=motor).count()Inherited base service layer with FastApiYou can create an inherited base service layer and add its methods,
as in the example in FastApi with convenience methods for 404:fromfastapiimportHTTPException,statusfrompydanticimportBaseModelfromservice_repository.servicesimportBaseServiceasBaseclassBaseService(Base):"""Class representing the base service."""def__init__(self,db)->None:self.db=dbasyncdefget_or_404(self,**kwargs):"""Get one instance by filter or 404 if not exists."""instance=awaitself.get(**kwargs)ifnotinstance:raiseHTTPException(status_code=status.HTTP_404_NOT_FOUND)returninstanceasyncdefupdate_or_404(self,schema_in:BaseModel,**kwargs):"""Update instance or 404 if not exists."""instance=awaitself.get_or_404(**kwargs)instance=awaitself.update(instance=instance,schema_in=schema_in)returninstanceasyncdefdelete_or_404(self,**kwargs):"""Delete one instance by filter or 404 if not exists."""awaitself.get_or_404(**kwargs)awaitself.delete(**kwargs)Inherited base service layer with FlaskYou can create an inherited base service layer and add its methods,
as in the example in Flask with convenience methods for 404:fromflaskimportabortfrompydanticimportBaseModelfromservice_repository.servicesimportBaseServiceasBaseclassBaseService(Base):"""Class representing the base service."""def__init__(self,db)->None:self.db=dbasyncdefget_or_404(self,**kwargs):"""Get one instance by filter or 404 if not exists."""instance=awaitself.get(**kwargs)ifnotinstance:raiseabort(404)returninstanceasyncdefupdate_or_404(self,schema_in:BaseModel,**kwargs):"""Update instance or 404 if not exists."""instance=awaitself.get_or_404(**kwargs)instance=awaitself.update(instance=instance,schema_in=schema_in)returninstanceasyncdefdelete_or_404(self,**kwargs):"""Delete one instance by filter or 404 if not exists."""awaitself.get_or_404(**kwargs)awaitself.delete(**kwargs)SecurityIf you discover any security related issues, please emailfndmiranda@gmail.cominstead of using the issue tracker.LicenseThe MIT License (MIT). Please seeLicense Filefor more information.