Closed 0x1618 closed 2 weeks ago
Ok, so i created a decorator, create_transaction()
, which wraps the entire route in a with
block, but it feels a bit nested...
def create_transaction():
def decorator(func):
@wraps(func)
async def wrapped(self, req, resp, *args, **kwargs):
async with in_transaction() as connection:
req.context.connection = connection
return await func(self, req, resp, *args, **kwargs)
return wrapped
return decorator
class VerifyEmailResource:
@create_transaction()
@before(authorize_user, is_logged=True, is_email_verified=False) # uses req.context.connection
async def on_get(self, req: Request, resp: Response):
entity_user: UserType = req.context.entity_user
await entity_user.verification().start_verification()
resp.status = 200
resp.media = {
"title": VERIFICATION_STARTED_TITLE,
"description": VERIFICATION_STARTED_MSG
}
@create_transaction()
@before(authorize_user, is_logged=True, is_email_verified=False) # uses req.context.connection
@before(
validate_request,
token={
'type': str,
'validators': {
'exact_length': VERIFICATION_TOKEN_LENGTH
}
}
)
async def on_post(self, req: Request, resp: Response):
entity_user: UserType = req.context.entity_user
await entity_user.verification().end_verification(
token=req.context.data['token']
)
resp.status = 200
resp.media = {
"title": VERIFICATION_ENDED_TITLE,
"description": VERIFICATION_ENDED_MSG
}
A much better option would be to do something like this under. However, i'm not sure how to implement the decorator properly to map all routes, like @before
do.
@create_transaction()
@before(authorize_user, is_logged=True, is_email_verified=False) # uses req.context.connection
class VerifyEmailResource:
async def on_get(self, req: Request, resp: Response):
entity_user: UserType = req.context.entity_user
await entity_user.verification().start_verification()
resp.status = 200
resp.media = {
"title": VERIFICATION_STARTED_TITLE,
"description": VERIFICATION_STARTED_MSG
}
@before(
validate_request,
token={
'type': str,
'validators': {
'exact_length': VERIFICATION_TOKEN_LENGTH
}
}
)
async def on_post(self, req: Request, resp: Response):
entity_user: UserType = req.context.entity_user
await entity_user.verification().end_verification(
token=req.context.data['token']
)
resp.status = 200
resp.media = {
"title": VERIFICATION_ENDED_TITLE,
"description": VERIFICATION_ENDED_MSG
}
Hi,
This looks like a discussion, so I will move it there. Regarding your question, falcon has middleware methods that run before and after a responder method so you could use them to start a transaction then commit/rollback.
The method are documented here process_resource and process_response. See the docs here https://falcon.readthedocs.io/en/stable/api/middleware.html You could do something like the following:
class Middleware:
async def process_resource(
self,
req: Request,
resp: Response,
resource: object,
params: dict[str, Any],
) -> None:
req.context.conn = await new_transaction() # create a transaction
async def process_response(
self,
req: Request,
resp: Response,
resource: object,
req_succeeded: bool
) -> None:
if req_succeded:
await req.context.conn.commit()
else:
await req.context.conn.rollback()
Hi, i'm wondering how to integrate falcon with Tortoise ORM. As far as i know, Tortoise ORM manages transactions on its own, but i'm not sure if it creates a transaction for each request (although i don't see why it would). I already managed to create
process_startup
andprocess_shutdown
, and i think that to create a new transaction (where exceptions trigger a rollback), you need to useasync with in_transaction()
wherever you run database queries.Here you can see an example. There’s one problem:
entity_user
is fetched in a separate transaction (which can lead to data mismatch), but it should be fetched within a single transaction usingin_transaction()
. As you can see, it's challenging to manage this without proper middleware. Withoutin_transaction()
, if an exception occurs inverification()
, the database will save progress up to that point, which could lead to bugs.I'm thinking about creating a decorator to wrap
on_get
so it would runon_get
within aasync with in_transaction()
block. However, i'm not sure how to do it properly to make it work well with Falcon and Tortoise ORM, also there may be a better solution.Current middleware:
I would really appreciate help with the integration. Also if you have a good solution for other ORM integration, please feel free to share it.