diff sat/memory/sqla.py @ 3798:b5013bada4b6

core (memory/sqla): generic `get` + `session_add` in `delete`: new generic `get` allow to retrieve an ORM object with given column and value, and optionally join load attributes. In `delete`, it is now possible to specify instances to add to session, this allow, to update them in the same transaction. It is also now possible to specify a list of object to delete them all in the same transaction. rel 367
author Goffi <goffi@goffi.org>
date Fri, 17 Jun 2022 14:15:23 +0200
parents cc653b2685f0
children 1a10b8b4f169
line wrap: on
line diff
--- a/sat/memory/sqla.py	Fri Jun 17 14:15:23 2022 +0200
+++ b/sat/memory/sqla.py	Fri Jun 17 14:15:23 2022 +0200
@@ -30,6 +30,7 @@
     sessionmaker, subqueryload, joinedload, selectinload, contains_eager
 )
 from sqlalchemy.orm.decl_api import DeclarativeMeta
+from sqlalchemy.orm.attributes import Mapped
 from sqlalchemy.future import select
 from sqlalchemy.engine import Engine, Connection
 from sqlalchemy import update, delete, and_, or_, event, func
@@ -216,6 +217,27 @@
     ## Generic
 
     @aio
+    async def get(
+        self,
+        client: SatXMPPEntity,
+        db_cls: DeclarativeMeta,
+        db_id_col: Mapped,
+        id_value: Any,
+        joined_loads=None
+    ) -> Optional[DeclarativeMeta]:
+        stmt = select(db_cls).where(db_id_col==id_value)
+        if client is not None:
+            stmt = stmt.filter_by(profile_id=self.profiles[client.profile])
+        if joined_loads is not None:
+            for joined_load in joined_loads:
+                stmt = stmt.options(joinedload(joined_load))
+        async with self.session() as session:
+            result = await session.execute(stmt)
+        if joined_loads is not None:
+            result = result.unique()
+        return result.scalar_one_or_none()
+
+    @aio
     async def add(self, db_obj: DeclarativeMeta) -> None:
         """Add an object to database"""
         async with self.session() as session:
@@ -223,11 +245,29 @@
                 session.add(db_obj)
 
     @aio
-    async def delete(self, db_obj: DeclarativeMeta) -> None:
-        """Delete an object from database"""
+    async def delete(
+        self,
+        db_obj: Union[DeclarativeMeta, List[DeclarativeMeta]],
+        session_add: Optional[List[DeclarativeMeta]]
+    ) -> None:
+        """Delete an object from database
+
+        @param db_obj: object to delete or list of objects to delete
+        @param session_add: other objects to add to session.
+            This is useful when parents of deleted objects needs to be updated too, or if
+            other objects needs to be updated in the same transaction.
+        """
+        if not db_obj:
+            return
+        if not isinstance(db_obj, list):
+            db_obj = [db_obj]
         async with self.session() as session:
             async with session.begin():
-                await session.delete(db_obj)
+                if session_add is not None:
+                    for obj in session_add:
+                        session.add(obj)
+                for obj in db_obj:
+                    await session.delete(obj)
                 await session.commit()
 
     ## Profiles