Skip to content

Commit 708906d

Browse files
Refactor CRUD Operations to Utilize SessionContextHolder for Session Management
- Updated CRUD methods in `PySpringModelRestService` and `CrudRepositoryImplementationService` to use `SessionContextHolder` for session handling, replacing the previous context manager approach. - Added `@Transactional` decorator to relevant methods to ensure proper transaction management. - Enhanced error handling in the `Transactional` decorator to raise specific exceptions. - Updated tests to clear session state before and after tests to maintain isolation and prevent side effects.
1 parent a7f4a65 commit 708906d

File tree

7 files changed

+53
-36
lines changed

7 files changed

+53
-36
lines changed

py_spring_model/core/session_context_holder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ def wrapper(*args, **kwargs):
1818
result = func(*args, **kwargs)
1919
session.commit()
2020
return result
21-
except Exception:
21+
except Exception as error:
2222
session.rollback()
23-
raise
23+
raise error
2424
finally:
2525
SessionContextHolder.clear_session()
2626
return wrapper

py_spring_model/py_spring_model_provider.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def provider_init(self) -> None:
148148
def provide_py_spring_model() -> EntityProvider:
149149
return PySpringModelProvider(
150150
rest_controller_classes=[
151-
# PySpringModelRestController
151+
PySpringModelRestController
152152
],
153153
component_classes=[
154154
PySpringModelRestService,

py_spring_model/py_spring_model_rest/service/curd_repository_implementation_service/crud_repository_implementation_service.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from sqlmodel.sql.expression import SelectOfScalar
2020

2121
from py_spring_model.core.model import PySpringModel
22+
from py_spring_model.core.session_context_holder import SessionContextHolder, Transactional
2223
from py_spring_model.repository.crud_repository import CrudRepository
2324
from py_spring_model.py_spring_model_rest.service.curd_repository_implementation_service.method_query_builder import (
2425
_MetodQueryBuilder,
@@ -150,14 +151,15 @@ def _get_sql_statement(
150151
query = query.where(filter_condition_stack.pop())
151152
return query
152153

154+
@Transactional
153155
def _session_execute(self, statement: SelectOfScalar, is_one_result: bool) -> Any:
154-
with PySpringModel.create_session() as session:
155-
logger.debug(f"Executing query: \n{str(statement)}")
156-
result = (
157-
session.exec(statement).first()
158-
if is_one_result
159-
else session.exec(statement).fetchall()
160-
)
156+
session = SessionContextHolder.get_or_create_session()
157+
logger.debug(f"Executing query: \n{str(statement)}")
158+
result = (
159+
session.exec(statement).first()
160+
if is_one_result
161+
else session.exec(statement).fetchall()
162+
)
161163
return result
162164

163165
def post_construct(self) -> None:

py_spring_model/py_spring_model_rest/service/py_spring_model_rest_service.py

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from py_spring_core import Component
55

66
from py_spring_model import PySpringModel
7+
from py_spring_model.core.session_context_holder import SessionContextHolder, Transactional
78

89
ID = TypeVar("ID", int, UUID)
910
ModelT = TypeVar("ModelT", bound=PySpringModel)
@@ -20,44 +21,48 @@ class PySpringModelRestService(Component):
2021
5. Updating an existing model,
2122
6. Deleting a model by ID.
2223
"""
23-
2424
def get_all_models(self) -> dict[str, type[PySpringModel]]:
2525
return PySpringModel.get_model_lookup()
2626

27+
@Transactional
2728
def get(self, model_type: Type[ModelT], id: ID) -> Optional[ModelT]:
28-
with PySpringModel.create_managed_session() as session:
29-
return session.get(model_type, id) # type: ignore
29+
session = SessionContextHolder.get_or_create_session()
30+
return session.get(model_type, id) # type: ignore
3031

32+
@Transactional
3133
def get_all_by_ids(self, model_type: Type[ModelT], ids: list[ID]) -> list[ModelT]:
32-
with PySpringModel.create_managed_session() as session:
33-
return session.query(model_type).filter(model_type.id.in_(ids)).all() # type: ignore
34-
34+
session = SessionContextHolder.get_or_create_session()
35+
return session.query(model_type).filter(model_type.id.in_(ids)).all() # type: ignore
36+
@Transactional
3537
def get_all(
3638
self, model_type: Type[ModelT], limit: int, offset: int
3739
) -> list[ModelT]:
38-
with PySpringModel.create_managed_session() as session:
39-
return session.query(model_type).offset(offset).limit(limit).all()
40+
session = SessionContextHolder.get_or_create_session()
41+
return session.query(model_type).offset(offset).limit(limit).all()
4042

43+
@Transactional
4144
def create(self, model: ModelT) -> ModelT:
42-
with PySpringModel.create_managed_session() as session:
43-
session.add(model)
44-
return model
45+
session = SessionContextHolder.get_or_create_session()
46+
session.add(model)
47+
return model
4548

49+
@Transactional
4650
def update(self, id: ID, model: ModelT) -> Optional[ModelT]:
47-
with PySpringModel.create_managed_session() as session:
48-
model_type = type(model)
49-
primary_keys = PySpringModel.get_primary_key_columns(model_type)
50-
optional_model = session.get(model_type, id) # type: ignore
51-
if optional_model is None:
52-
return
53-
54-
for key, value in model.model_dump().items():
55-
if key in primary_keys:
56-
continue
57-
setattr(optional_model, key, value)
58-
session.add(optional_model)
51+
session = SessionContextHolder.get_or_create_session()
52+
model_type = type(model)
53+
primary_keys = PySpringModel.get_primary_key_columns(model_type)
54+
optional_model = session.get(model_type, id) # type: ignore
55+
if optional_model is None:
56+
return
57+
58+
for key, value in model.model_dump().items():
59+
if key in primary_keys:
60+
continue
61+
setattr(optional_model, key, value)
62+
session.add(optional_model)
5963

64+
@Transactional
6065
def delete(self, model_type: Type[ModelT], id: ID) -> None:
61-
with PySpringModel.create_managed_session() as session:
62-
session.query(model_type).filter(model_type.id == id).delete() # type: ignore
63-
session.commit()
66+
session = SessionContextHolder.get_or_create_session()
67+
session.query(model_type).filter(model_type.id == id).delete() # type: ignore
68+
session.commit()

tests/test_crud_repository.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from sqlmodel import Field, SQLModel
55

66
from py_spring_model import PySpringModel
7+
from py_spring_model.core.session_context_holder import SessionContextHolder
78
from py_spring_model.repository.crud_repository import CrudRepository
89

910
class User(PySpringModel, table=True):
@@ -19,11 +20,13 @@ def setup_method(self):
1920
logger.info("Setting up test environment...")
2021
self.engine = create_engine("sqlite:///:memory:", echo=True)
2122
PySpringModel._engine = self.engine
23+
SessionContextHolder.clear_session()
2224
SQLModel.metadata.create_all(self.engine)
2325

2426
def teardown_method(self):
2527
logger.info("Tearing down test environment...")
2628
SQLModel.metadata.drop_all(self.engine)
29+
SessionContextHolder.clear_session()
2730

2831
@pytest.fixture
2932
def user_repository(self):

tests/test_crud_repository_implementation_service.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from sqlalchemy import create_engine
77
from sqlmodel import SQLModel
88
from py_spring_model import PySpringModel, Field, CrudRepository, Query
9+
from py_spring_model.core.session_context_holder import SessionContextHolder
910
from py_spring_model.py_spring_model_rest.service.curd_repository_implementation_service.crud_repository_implementation_service import CrudRepositoryImplementationService
1011
from py_spring_model.py_spring_model_rest.service.curd_repository_implementation_service.method_query_builder import _MetodQueryBuilder
1112

@@ -32,11 +33,13 @@ def setup_method(self):
3233
logger.info("Setting up test environment...")
3334
self.engine = create_engine("sqlite:///:memory:", echo=True)
3435
PySpringModel._engine = self.engine
36+
SessionContextHolder.clear_session()
3537
SQLModel.metadata.create_all(self.engine)
3638

3739
def teardown_method(self):
3840
logger.info("Tearing down test environment...")
3941
SQLModel.metadata.drop_all(self.engine)
42+
SessionContextHolder.clear_session()
4043

4144
@pytest.fixture
4245
def user_repository(self):

tests/test_query_modifying_operations.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from unittest.mock import patch, MagicMock
77

88
from py_spring_model import PySpringModel
9+
from py_spring_model.core.session_context_holder import SessionContextHolder
910
from py_spring_model.py_spring_model_rest.service.query_service.query import Query, QueryExecutionService
1011
from py_spring_model.repository.crud_repository import CrudRepository
1112

@@ -88,14 +89,17 @@ def setup_method(self):
8889
PySpringModel.set_engine(self.engine)
8990
PySpringModel.set_metadata(SQLModel.metadata)
9091
PySpringModel.set_models([TestUser])
92+
SessionContextHolder.clear_session()
9193
SQLModel.metadata.create_all(self.engine)
9294
self.repository = TestUserRepository()
95+
9396
self.repository.insert_user_with_commit(name="John Doe", email="john@example.com", age=30)
9497

9598
def teardown_method(self):
9699
"""Clean up test environment"""
97100
logger.info("Tearing down test environment...")
98101
SQLModel.metadata.drop_all(self.engine)
102+
SessionContextHolder.clear_session()
99103

100104
def test_insert_with_commit_true(self):
101105
"""Test INSERT operation with is_modifying=True (should commit)"""

0 commit comments

Comments
 (0)