'How to work with Sqlalchemy session that is not explicitly passed to function(s)
Reading SQLAlchemy about Sessions we can read this:
https://docs.sqlalchemy.org/en/14/orm/contextual.html#session-lifespan
Both paragraphs are simple to understand but follow a top-down perspective.
My use case is reversed, I am building an internal library that will be pip-installed by REST API as a dependency, and to perform my operation I need access to the database via sqlalchemy. My goal is to preserve the separation of concerns and we lack specific knowledge of which function(s) will be used in which order and if those functions are calling each other or don't.
So let's assume we have the following functions:
def pick_mortgage(user):
mortgages = pick_chepeast_mortgages(user, 3)
fees = Session.query(MortgageFee).all ()
# ... reads from database ...
# ... saves to database ...
return mortgages, fees
def pick_chepeast_mortgages(user, number):
mortgages = find_possible_mortgages(user)
# ... only calculate some stuff, no db involved ...
return mortgages[:3]
def find_possible_mortgages(user):
possible_mortgages = Session.query(models.Mortgage).all()
# ... reads from database ...
# ... saves to database ...
return possible_mortgages
And it might be used in the following, Django Rest Framework-like style:
from mortgage_calulator import pick_mortgage
class ListMortgagesView(mixins.ListModelMixin, viewsets.GenericViewSet):
def retrieve(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
mortgages, fees = mortgages.pick_mortgage(request.user)
# ... do more stuff ...
return Response(
data,
status=status.HTTP_200_OK,
)
My Session is defined following way:
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from config import DATABASE_URL
engine = create_engine(DATABASE_URL, future=True, echo=True)
session_factory = sessionmaker(engine, expire_on_commit=False)
Session = scoped_session(session_factory)
My go-to strategy would be to create a decorator called managed_session and decorate all mortgage functions that need access to database with it:
def managed_session(func):
@functools.wraps(func)
def inner(*args, **kwargs):
with Session.begin_nested() as session:
return func(*args, **kwargs)
return inner
My confusion stems from the fact that documentation talks about session life-cycle to be separate and external and all the examples instantiate it and pass explicitly to the functions that may need it.
In my case, while session is somewhat external and separate from the function itself, it's in a python global scope. Am I using sqlalchemy incorrectly? Or is my approach valid and just one way of doing it?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
