SARepo 0.1.4__tar.gz → 0.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SARepo
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Minimal, explicit Repository & Unit-of-Work layer over SQLAlchemy 2.x
5
5
  Author: nurbergenovv
6
6
  License: MIT
@@ -1,39 +1,41 @@
1
1
 
2
2
  from typing import Generic, List, TypeVar, Type, Optional, Any, Protocol
3
+
4
+ from SARepo.sa_repo import Spec
3
5
  from .base import Page, PageRequest
4
6
 
5
7
  T = TypeVar("T")
6
8
 
7
9
  class CrudRepository(Protocol, Generic[T]):
8
10
  model: Type[T]
9
- def getAll(self, limit: Optional[int]) -> List[T]: ...
10
- def get(self, id_: Any) -> T: ...
11
- def try_get(self, id_: Any) -> Optional[T]: ...
11
+ def getAll(self, limit: Optional[int] = None, *, include_deleted: bool = False) -> List[T]: ...
12
+ def get(self, id_: Any, *, include_deleted: bool = False) -> T: ...
13
+ def try_get(self, id_: Any, *, include_deleted: bool = False) -> Optional[T]: ...
12
14
  def add(self, entity: T) -> T: ...
13
15
  def update(self, entity: T) -> T: ...
14
16
  def remove(self, entity: T) -> None: ...
15
17
  def delete_by_id(self, id_: Any) -> bool: ...
16
- def page(self, page: PageRequest, spec=None, order_by=None) -> Page[T]: ...
18
+ def page(self, page: PageRequest, spec: Optional[Spec] = None, order_by=None, *, include_deleted: bool = False) -> Page[T]: ...
17
19
  def get_all_by_column(
18
- self,
19
- column_name: str,
20
- value: Any,
21
- *,
22
- limit: Optional[int] = None,
23
- order_by=None,
24
- include_deleted: bool = False,
25
- **extra_filters
26
- ) -> list[T]: ...
20
+ self,
21
+ column_name: str,
22
+ value: Any,
23
+ *,
24
+ limit: Optional[int] = None,
25
+ order_by=None,
26
+ include_deleted: bool = False,
27
+ **extra_filters
28
+ ) -> list[T]: ...
27
29
  def find_all_by_column(
28
- self,
29
- column_name: str,
30
- value: Any,
31
- *,
32
- limit: Optional[int] = None,
33
- order_by=None,
34
- include_deleted: bool = False,
35
- **extra_filters
36
- ) -> list[T]: ...
30
+ self,
31
+ column_name: str,
32
+ value: Any,
33
+ *,
34
+ limit: Optional[int] = None,
35
+ order_by=None,
36
+ include_deleted: bool = False,
37
+ **extra_filters
38
+ ) -> list[T]: ...
37
39
  def get_or_create(
38
40
  self,
39
41
  defaults: Optional[dict] = None,
@@ -28,22 +28,38 @@ class SARepository(Generic[T]):
28
28
  def _select(self):
29
29
  return select(self.model)
30
30
 
31
- def getAll(self, limit: Optional[int] = None) -> List[T]:
31
+ def _has_soft_delete(self) -> bool:
32
+ return hasattr(self.model, "is_deleted")
33
+
34
+ def _apply_alive_filter(self, stmt, include_deleted: bool) :
35
+ if self._has_soft_delete() and not include_deleted:
36
+ stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
37
+ return stmt
38
+
39
+ def getAll(self, limit: Optional[int] = None, *, include_deleted: bool = False) -> List[T]:
32
40
  stmt = select(self.model)
41
+ stmt = self._apply_alive_filter(stmt, include_deleted)
33
42
  if limit is not None:
34
43
  stmt = stmt.limit(limit)
35
44
  result = self.session.execute(stmt)
36
- return result.scalars().all()
45
+ return result.scalars().all()
37
46
 
38
- def get(self, id_: Any) -> T:
47
+ def get(self, id_: Any, *, include_deleted: bool = False) -> T:
39
48
  obj = self.session.get(self.model, id_)
40
49
  if not obj:
41
50
  raise NotFoundError(f"{self.model.__name__}({id_}) not found")
51
+ if self._has_soft_delete() and not include_deleted and getattr(obj, "is_deleted", False):
52
+ raise NotFoundError(f"{self.model.__name__}({id_}) not found") # скрываем как «нет»
42
53
  return obj
43
54
 
44
- def try_get(self, id_: Any) -> Optional[T]:
45
- return self.session.get(self.model, id_)
46
-
55
+ def try_get(self, id_: Any, *, include_deleted: bool = False) -> Optional[T]:
56
+ obj = self.session.get(self.model, id_)
57
+ if not obj:
58
+ return None
59
+ if self._has_soft_delete() and not include_deleted and getattr(obj, "is_deleted", False):
60
+ return None
61
+ return obj
62
+
47
63
  def add(self, entity: T) -> T:
48
64
  self.session.add(entity)
49
65
  self.session.flush()
@@ -76,17 +92,20 @@ class SARepository(Generic[T]):
76
92
  self.remove(obj)
77
93
  return True
78
94
 
79
- def page(self, page: PageRequest, spec: Optional[Spec] = None, order_by=None) -> Page[T]:
80
- stmt = self._select()
95
+ def page(self, page: PageRequest, spec: Optional[Spec] = None, order_by=None, *, include_deleted: bool = False) -> Page[T]:
96
+ base = self._select()
81
97
  if spec:
82
- stmt = spec(stmt)
98
+ base = spec(base)
99
+ base = self._apply_alive_filter(base, include_deleted)
83
100
  if order_by is not None:
84
- stmt = stmt.order_by(order_by)
101
+ base = base.order_by(order_by)
102
+
85
103
  total = self.session.execute(
86
- select(func.count()).select_from(stmt.subquery())
104
+ select(func.count()).select_from(base.subquery())
87
105
  ).scalar_one()
106
+
88
107
  items = self.session.execute(
89
- stmt.offset(page.page * page.size).limit(page.size)
108
+ base.offset(page.page * page.size).limit(page.size)
90
109
  ).scalars().all()
91
110
  return Page(items, total, page.page, page.size)
92
111
 
@@ -102,8 +121,7 @@ class SARepository(Generic[T]):
102
121
  ) -> list[T]:
103
122
  col = self._resolve_column(column_name)
104
123
  stmt = select(self.model).where(col == value)
105
- if not include_deleted and hasattr(self.model, "is_deleted"):
106
- stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
124
+ stmt = self._apply_alive_filter(stmt, include_deleted)
107
125
  stmt = self._apply_filters(stmt, **extra_filters)
108
126
  if order_by is not None:
109
127
  stmt = stmt.order_by(order_by)
@@ -111,7 +129,7 @@ class SARepository(Generic[T]):
111
129
  stmt = stmt.limit(limit)
112
130
  res = self.session.execute(stmt)
113
131
  return res.scalars().all()
114
-
132
+
115
133
  # Alias
116
134
  def find_all_by_column(self, *args, **kwargs):
117
135
  return self.get_all_by_column(*args, **kwargs)
@@ -222,21 +240,37 @@ class SAAsyncRepository(Generic[T]):
222
240
  def _select(self):
223
241
  return select(self.model)
224
242
 
225
- async def getAll(self, limit: Optional[int] = None) -> List[T]:
243
+ def _has_soft_delete(self) -> bool:
244
+ return hasattr(self.model, "is_deleted")
245
+
246
+ def _apply_alive_filter(self, stmt, include_deleted: bool) :
247
+ if self._has_soft_delete() and not include_deleted:
248
+ stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
249
+ return stmt
250
+
251
+ async def getAll(self, limit: Optional[int] = None, *, include_deleted: bool = False) -> List[T]:
226
252
  stmt = select(self.model)
253
+ stmt = self._apply_alive_filter(stmt, include_deleted)
227
254
  if limit is not None:
228
255
  stmt = stmt.limit(limit)
229
256
  result = await self.session.execute(stmt)
230
257
  return result.scalars().all()
231
258
 
232
- async def get(self, id_: Any) -> T:
259
+ async def get(self, id_: Any, *, include_deleted: bool = False) -> T:
233
260
  obj = await self.session.get(self.model, id_)
234
261
  if not obj:
235
262
  raise NotFoundError(f"{self.model.__name__}({id_}) not found")
263
+ if self._has_soft_delete() and not include_deleted and getattr(obj, "is_deleted", False):
264
+ raise NotFoundError(f"{self.model.__name__}({id_}) not found")
236
265
  return obj
237
266
 
238
- async def try_get(self, id_: Any) -> Optional[T]:
239
- return await self.session.get(self.model, id_)
267
+ async def try_get(self, id_: Any, *, include_deleted: bool = False) -> Optional[T]:
268
+ obj = await self.session.get(self.model, id_)
269
+ if not obj:
270
+ return None
271
+ if self._has_soft_delete() and not include_deleted and getattr(obj, "is_deleted", False):
272
+ return None
273
+ return obj
240
274
 
241
275
  async def add(self, entity: T) -> T:
242
276
  self.session.add(entity)
@@ -270,21 +304,24 @@ class SAAsyncRepository(Generic[T]):
270
304
  await self.remove(obj)
271
305
  return True
272
306
 
273
- async def page(self, page: PageRequest, spec: Optional[Spec] = None, order_by=None) -> Page[T]:
274
- stmt = self._select()
307
+ async def page(self, page: PageRequest, spec: Optional[Spec] = None, order_by=None, *, include_deleted: bool = False) -> Page[T]: # type: ignore
308
+ base = self._select()
275
309
  if spec:
276
- stmt = spec(stmt)
310
+ base = spec(base)
311
+ base = self._apply_alive_filter(base, include_deleted)
277
312
  if order_by is not None:
278
- stmt = stmt.order_by(order_by)
313
+ base = base.order_by(order_by)
314
+
279
315
  total = (await self.session.execute(
280
- select(func.count()).select_from(stmt.subquery())
316
+ select(func.count()).select_from(base.subquery())
281
317
  )).scalar_one()
318
+
282
319
  res = await self.session.execute(
283
- stmt.offset(page.page * page.size).limit(page.size)
320
+ base.offset(page.page * page.size).limit(page.size)
284
321
  )
285
322
  items = res.scalars().all()
286
323
  return Page(items, total, page.page, page.size)
287
-
324
+
288
325
  async def get_all_by_column(
289
326
  self,
290
327
  column_name: str,
@@ -297,8 +334,7 @@ class SAAsyncRepository(Generic[T]):
297
334
  ) -> list[T]:
298
335
  col = self._resolve_column(column_name)
299
336
  stmt = select(self.model).where(col == value)
300
- if not include_deleted and hasattr(self.model, "is_deleted"):
301
- stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
337
+ stmt = self._apply_alive_filter(stmt, include_deleted)
302
338
  stmt = self._apply_filters(stmt, **extra_filters)
303
339
  if order_by is not None:
304
340
  stmt = stmt.order_by(order_by)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SARepo
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Minimal, explicit Repository & Unit-of-Work layer over SQLAlchemy 2.x
5
5
  Author: nurbergenovv
6
6
  License: MIT
@@ -1,7 +1,7 @@
1
1
 
2
2
  [project]
3
3
  name = "SARepo"
4
- version = "0.1.4"
4
+ version = "0.1.5"
5
5
  description = "Minimal, explicit Repository & Unit-of-Work layer over SQLAlchemy 2.x"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.11"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes