Search code examples
pythonpymongodecorator

In decorator function, Is there a way to apply the session used by decorator wrapper function to the repository code?


First of all, I'm so sorry to write an ambiguous title.

My situation is like below.

I wanted to use Transactional decorator (like @Transactional in Spring)

But in order to use transaction in PyMongo,

it need to use the session of wrapper function like Repository function(mongo_user_repository.save()).

(docs - https://pymongo.readthedocs.io/en/stable/api/pymongo/client_session.html)

So, Is there any way to get session of wrapper function in Service functions or some other way to use transactional decorator in Service functions?

I made decorator,


class MongoDB:
    def __init__(self):
        self.__client = MongoClient(
            DB_URL,
            tlsCAFile=certifi.where()
        )

    def close(self):
        self.__client.close()

    def transactional(self, origin_func):
        def wrapper_func(*args, **kwargs):
            with self.__client.start_session() as session:
                with session.start_transaction():
                    return origin_func(*args, **kwargs)

        return wrapper_func

And wanted to use like this in service code.

class UserService:
    def __init__(
        self,
        mongo_user_repository: MongoUsersRepository,
    ):
        self.__mongo_user_repository: MongoUsersRepository = mongo_user_repository

    @mongodb.transactional
    def save(self, user_create_request: UserCreateRequestDto):
        mongo_user = MongoUsers.from_dto(user_create_request=user_create_request)

        self.__mongo_user_repository.save(
            mongo_user=mongo_user,
            session=session
        )

Solution

  • You can make mongodb.transactional call the wrapped function with session as an additional argument:

    def transactional(self, origin_func):
        def wrapper_func(*args, **kwargs):
            with self.__client.start_session() as session:
                with session.start_transaction():
                    return origin_func(*args, session=session, **kwargs)
    
        return wrapper_func
    

    so that the decorated service function can take session as an argument:

    @mongodb.transactional
    def save(self, user_create_request: UserCreateRequestDto, session):
        mongo_user = MongoUsers.from_dto(user_create_request=user_create_request)
    
        self.__mongo_user_repository.save(
            mongo_user=mongo_user,
            session=session
        )