SARepo 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl
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.
- SARepo/repo.py +38 -4
- SARepo/sa_repo.py +299 -28
- {sarepo-0.1.3.dist-info → sarepo-0.1.5.dist-info}/METADATA +1 -1
- sarepo-0.1.5.dist-info/RECORD +12 -0
- sarepo-0.1.3.dist-info/RECORD +0 -12
- {sarepo-0.1.3.dist-info → sarepo-0.1.5.dist-info}/WHEEL +0 -0
- {sarepo-0.1.3.dist-info → sarepo-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {sarepo-0.1.3.dist-info → sarepo-0.1.5.dist-info}/top_level.txt +0 -0
SARepo/repo.py
CHANGED
@@ -1,16 +1,50 @@
|
|
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]: ...
|
19
|
+
def get_all_by_column(
|
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]: ...
|
29
|
+
def find_all_by_column(
|
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]: ...
|
39
|
+
def get_or_create(
|
40
|
+
self,
|
41
|
+
defaults: Optional[dict] = None,
|
42
|
+
**unique_filters
|
43
|
+
) -> tuple[T, bool]: ...
|
44
|
+
def raw_query(self, sql: str, params: Optional[dict] = None) -> list[dict]: ...
|
45
|
+
def aggregate_avg(self, column_name: str, **filters) -> Optional[float]: ...
|
46
|
+
def aggregate_min(self, column_name: str, **filters): ...
|
47
|
+
def aggregate_max(self, column_name: str, **filters): ...
|
48
|
+
def aggregate_sum(self, column_name: str, **filters): ...
|
49
|
+
def count(self, **filters) -> int: ...
|
50
|
+
def restore(self, id_: Any) -> bool: ...
|
SARepo/sa_repo.py
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
from typing import List, Type, Generic, TypeVar, Optional, Sequence, Any, Callable
|
3
3
|
from sqlalchemy.orm import Session
|
4
4
|
from sqlalchemy.ext.asyncio import AsyncSession
|
5
|
-
from sqlalchemy import inspect, select, func
|
5
|
+
from sqlalchemy import inspect, select, func, text
|
6
6
|
from .base import PageRequest, Page, NotFoundError
|
7
7
|
|
8
8
|
T = TypeVar("T")
|
9
|
-
Spec = Callable
|
9
|
+
Spec = Callable
|
10
10
|
|
11
11
|
class SARepository(Generic[T]):
|
12
12
|
"""Synchronous repository implementation for SQLAlchemy 2.x."""
|
@@ -14,25 +14,52 @@ class SARepository(Generic[T]):
|
|
14
14
|
self.model = model
|
15
15
|
self.session = session
|
16
16
|
|
17
|
+
def _resolve_column(self, column_name: str):
|
18
|
+
try:
|
19
|
+
return getattr(self.model, column_name)
|
20
|
+
except AttributeError as e:
|
21
|
+
raise ValueError(f"Model {self.model.__name__} has no column '{column_name}'") from e
|
22
|
+
|
23
|
+
def _apply_filters(self, stmt, **filters):
|
24
|
+
if filters:
|
25
|
+
stmt = stmt.filter_by(**filters)
|
26
|
+
return stmt
|
27
|
+
|
17
28
|
def _select(self):
|
18
29
|
return select(self.model)
|
19
30
|
|
20
|
-
def
|
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]:
|
21
40
|
stmt = select(self.model)
|
41
|
+
stmt = self._apply_alive_filter(stmt, include_deleted)
|
22
42
|
if limit is not None:
|
23
43
|
stmt = stmt.limit(limit)
|
24
44
|
result = self.session.execute(stmt)
|
25
|
-
return result.scalars().all()
|
45
|
+
return result.scalars().all()
|
26
46
|
|
27
|
-
def get(self, id_: Any) -> T:
|
47
|
+
def get(self, id_: Any, *, include_deleted: bool = False) -> T:
|
28
48
|
obj = self.session.get(self.model, id_)
|
29
49
|
if not obj:
|
30
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") # скрываем как «нет»
|
31
53
|
return obj
|
32
54
|
|
33
|
-
def try_get(self, id_: Any) -> Optional[T]:
|
34
|
-
|
35
|
-
|
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
|
+
|
36
63
|
def add(self, entity: T) -> T:
|
37
64
|
self.session.add(entity)
|
38
65
|
self.session.flush()
|
@@ -65,44 +92,185 @@ class SARepository(Generic[T]):
|
|
65
92
|
self.remove(obj)
|
66
93
|
return True
|
67
94
|
|
68
|
-
def page(self, page: PageRequest, spec: Optional[Spec] = None, order_by=None) -> Page[T]:
|
69
|
-
|
95
|
+
def page(self, page: PageRequest, spec: Optional[Spec] = None, order_by=None, *, include_deleted: bool = False) -> Page[T]:
|
96
|
+
base = self._select()
|
70
97
|
if spec:
|
71
|
-
|
98
|
+
base = spec(base)
|
99
|
+
base = self._apply_alive_filter(base, include_deleted)
|
72
100
|
if order_by is not None:
|
73
|
-
|
101
|
+
base = base.order_by(order_by)
|
102
|
+
|
74
103
|
total = self.session.execute(
|
75
|
-
select(func.count()).select_from(
|
104
|
+
select(func.count()).select_from(base.subquery())
|
76
105
|
).scalar_one()
|
106
|
+
|
77
107
|
items = self.session.execute(
|
78
|
-
|
108
|
+
base.offset(page.page * page.size).limit(page.size)
|
79
109
|
).scalars().all()
|
80
110
|
return Page(items, total, page.page, page.size)
|
111
|
+
|
112
|
+
def get_all_by_column(
|
113
|
+
self,
|
114
|
+
column_name: str,
|
115
|
+
value: Any,
|
116
|
+
*,
|
117
|
+
limit: Optional[int] = None,
|
118
|
+
order_by=None,
|
119
|
+
include_deleted: bool = False,
|
120
|
+
**extra_filters
|
121
|
+
) -> list[T]:
|
122
|
+
col = self._resolve_column(column_name)
|
123
|
+
stmt = select(self.model).where(col == value)
|
124
|
+
stmt = self._apply_alive_filter(stmt, include_deleted)
|
125
|
+
stmt = self._apply_filters(stmt, **extra_filters)
|
126
|
+
if order_by is not None:
|
127
|
+
stmt = stmt.order_by(order_by)
|
128
|
+
if limit is not None:
|
129
|
+
stmt = stmt.limit(limit)
|
130
|
+
res = self.session.execute(stmt)
|
131
|
+
return res.scalars().all()
|
132
|
+
|
133
|
+
# Alias
|
134
|
+
def find_all_by_column(self, *args, **kwargs):
|
135
|
+
return self.get_all_by_column(*args, **kwargs)
|
136
|
+
|
137
|
+
def get_or_create(
|
138
|
+
self,
|
139
|
+
defaults: Optional[dict] = None,
|
140
|
+
**unique_filters
|
141
|
+
) -> tuple[T, bool]:
|
142
|
+
"""
|
143
|
+
Возвращает (obj, created). unique_filters определяют уникальность.
|
144
|
+
defaults дополняют поля при создании.
|
145
|
+
"""
|
146
|
+
stmt = select(self.model).filter_by(**unique_filters)
|
147
|
+
if hasattr(self.model, "is_deleted"):
|
148
|
+
stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
|
149
|
+
obj = self.session.execute(stmt).scalar_one_or_none()
|
150
|
+
if obj:
|
151
|
+
return obj, False
|
152
|
+
payload = {**unique_filters, **(defaults or {})}
|
153
|
+
obj = self.model(**payload) # type: ignore[call-arg]
|
154
|
+
self.session.add(obj)
|
155
|
+
self.session.flush()
|
156
|
+
self.session.refresh(obj)
|
157
|
+
return obj, True
|
158
|
+
|
159
|
+
def raw_query(self, sql: str, params: Optional[dict] = None) -> list[dict]:
|
160
|
+
"""
|
161
|
+
Безопасно выполняет сырой SQL (используй плейсхолдеры :name).
|
162
|
+
Возвращает список dict (строки).
|
163
|
+
"""
|
164
|
+
res = self.session.execute(text(sql), params or {})
|
165
|
+
# mapping() -> RowMapping (dict-like)
|
166
|
+
return [dict(row) for row in res.mappings().all()]
|
167
|
+
|
168
|
+
def aggregate_avg(self, column_name: str, **filters) -> Optional[float]:
|
169
|
+
col = self._resolve_column(column_name)
|
170
|
+
stmt = select(func.avg(col))
|
171
|
+
if hasattr(self.model, "is_deleted"):
|
172
|
+
stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
|
173
|
+
stmt = self._apply_filters(stmt, **filters)
|
174
|
+
return self.session.execute(stmt).scalar()
|
175
|
+
|
176
|
+
def aggregate_min(self, column_name: str, **filters):
|
177
|
+
col = self._resolve_column(column_name)
|
178
|
+
stmt = select(func.min(col))
|
179
|
+
if hasattr(self.model, "is_deleted"):
|
180
|
+
stmt = stmt.where(self.model.is_deleted == False)
|
181
|
+
stmt = self._apply_filters(stmt, **filters)
|
182
|
+
return self.session.execute(stmt).scalar()
|
183
|
+
|
184
|
+
def aggregate_max(self, column_name: str, **filters):
|
185
|
+
col = self._resolve_column(column_name)
|
186
|
+
stmt = select(func.max(col))
|
187
|
+
if hasattr(self.model, "is_deleted"):
|
188
|
+
stmt = stmt.where(self.model.is_deleted == False)
|
189
|
+
stmt = self._apply_filters(stmt, **filters)
|
190
|
+
return self.session.execute(stmt).scalar()
|
191
|
+
|
192
|
+
def aggregate_sum(self, column_name: str, **filters):
|
193
|
+
col = self._resolve_column(column_name)
|
194
|
+
stmt = select(func.sum(col))
|
195
|
+
if hasattr(self.model, "is_deleted"):
|
196
|
+
stmt = stmt.where(self.model.is_deleted == False)
|
197
|
+
stmt = self._apply_filters(stmt, **filters)
|
198
|
+
return self.session.execute(stmt).scalar()
|
199
|
+
|
200
|
+
def count(self, **filters) -> int:
|
201
|
+
stmt = select(func.count()).select_from(self.model)
|
202
|
+
if hasattr(self.model, "is_deleted") and not filters.pop("include_deleted", False):
|
203
|
+
stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
|
204
|
+
if filters:
|
205
|
+
stmt = stmt.filter_by(**filters)
|
206
|
+
return int(self.session.execute(stmt).scalar_one())
|
207
|
+
|
208
|
+
def restore(self, id_: Any) -> bool:
|
209
|
+
"""
|
210
|
+
Для soft-delete: is_deleted=False. Возвращает True, если восстановили.
|
211
|
+
"""
|
212
|
+
if not hasattr(self.model, "is_deleted"):
|
213
|
+
raise RuntimeError(f"{self.model.__name__} has no 'is_deleted' field")
|
214
|
+
obj = self.session.get(self.model, id_)
|
215
|
+
if not obj:
|
216
|
+
return False
|
217
|
+
if getattr(obj, "is_deleted", False):
|
218
|
+
setattr(obj, "is_deleted", False)
|
219
|
+
self.session.flush()
|
220
|
+
return True
|
221
|
+
return False
|
81
222
|
|
82
223
|
class SAAsyncRepository(Generic[T]):
|
83
224
|
"""Async repository implementation for SQLAlchemy 2.x."""
|
84
225
|
def __init__(self, model: Type[T], session: AsyncSession):
|
85
226
|
self.model = model
|
86
227
|
self.session = session
|
87
|
-
|
88
|
-
|
228
|
+
|
229
|
+
def _resolve_column(self, column_name: str):
|
230
|
+
try:
|
231
|
+
return getattr(self.model, column_name)
|
232
|
+
except AttributeError as e:
|
233
|
+
raise ValueError(f"Model {self.model.__name__} has no column '{column_name}'") from e
|
234
|
+
|
235
|
+
def _apply_filters(self, stmt, **filters):
|
236
|
+
if filters:
|
237
|
+
stmt = stmt.filter_by(**filters)
|
238
|
+
return stmt
|
239
|
+
|
240
|
+
def _select(self):
|
241
|
+
return select(self.model)
|
242
|
+
|
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]:
|
89
252
|
stmt = select(self.model)
|
253
|
+
stmt = self._apply_alive_filter(stmt, include_deleted)
|
90
254
|
if limit is not None:
|
91
255
|
stmt = stmt.limit(limit)
|
92
256
|
result = await self.session.execute(stmt)
|
93
257
|
return result.scalars().all()
|
94
258
|
|
95
|
-
def
|
96
|
-
return select(self.model)
|
97
|
-
|
98
|
-
async def get(self, id_: Any) -> T:
|
259
|
+
async def get(self, id_: Any, *, include_deleted: bool = False) -> T:
|
99
260
|
obj = await self.session.get(self.model, id_)
|
100
261
|
if not obj:
|
101
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")
|
102
265
|
return obj
|
103
266
|
|
104
|
-
async def try_get(self, id_: Any) -> Optional[T]:
|
105
|
-
|
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
|
106
274
|
|
107
275
|
async def add(self, entity: T) -> T:
|
108
276
|
self.session.add(entity)
|
@@ -136,17 +304,120 @@ class SAAsyncRepository(Generic[T]):
|
|
136
304
|
await self.remove(obj)
|
137
305
|
return True
|
138
306
|
|
139
|
-
async def page(self, page: PageRequest, spec: Optional[Spec] = None, order_by=None) -> Page[T]:
|
140
|
-
|
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()
|
141
309
|
if spec:
|
142
|
-
|
310
|
+
base = spec(base)
|
311
|
+
base = self._apply_alive_filter(base, include_deleted)
|
143
312
|
if order_by is not None:
|
144
|
-
|
313
|
+
base = base.order_by(order_by)
|
314
|
+
|
145
315
|
total = (await self.session.execute(
|
146
|
-
select(func.count()).select_from(
|
316
|
+
select(func.count()).select_from(base.subquery())
|
147
317
|
)).scalar_one()
|
318
|
+
|
148
319
|
res = await self.session.execute(
|
149
|
-
|
320
|
+
base.offset(page.page * page.size).limit(page.size)
|
150
321
|
)
|
151
322
|
items = res.scalars().all()
|
152
323
|
return Page(items, total, page.page, page.size)
|
324
|
+
|
325
|
+
async def get_all_by_column(
|
326
|
+
self,
|
327
|
+
column_name: str,
|
328
|
+
value: Any,
|
329
|
+
*,
|
330
|
+
limit: Optional[int] = None,
|
331
|
+
order_by=None,
|
332
|
+
include_deleted: bool = False,
|
333
|
+
**extra_filters
|
334
|
+
) -> list[T]:
|
335
|
+
col = self._resolve_column(column_name)
|
336
|
+
stmt = select(self.model).where(col == value)
|
337
|
+
stmt = self._apply_alive_filter(stmt, include_deleted)
|
338
|
+
stmt = self._apply_filters(stmt, **extra_filters)
|
339
|
+
if order_by is not None:
|
340
|
+
stmt = stmt.order_by(order_by)
|
341
|
+
if limit is not None:
|
342
|
+
stmt = stmt.limit(limit)
|
343
|
+
res = await self.session.execute(stmt)
|
344
|
+
return res.scalars().all()
|
345
|
+
|
346
|
+
# Alias
|
347
|
+
async def find_all_by_column(self, *args, **kwargs):
|
348
|
+
return await self.get_all_by_column(*args, **kwargs)
|
349
|
+
|
350
|
+
async def get_or_create(
|
351
|
+
self,
|
352
|
+
defaults: Optional[dict] = None,
|
353
|
+
**unique_filters
|
354
|
+
) -> tuple[T, bool]:
|
355
|
+
stmt = select(self.model).filter_by(**unique_filters)
|
356
|
+
if hasattr(self.model, "is_deleted"):
|
357
|
+
stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
|
358
|
+
obj = (await self.session.execute(stmt)).scalar_one_or_none()
|
359
|
+
if obj:
|
360
|
+
return obj, False
|
361
|
+
payload = {**unique_filters, **(defaults or {})}
|
362
|
+
obj = self.model(**payload) # type: ignore[call-arg]
|
363
|
+
self.session.add(obj)
|
364
|
+
await self.session.flush()
|
365
|
+
await self.session.refresh(obj)
|
366
|
+
return obj, True
|
367
|
+
|
368
|
+
async def raw_query(self, sql: str, params: Optional[dict] = None) -> list[dict]:
|
369
|
+
res = await self.session.execute(text(sql), params or {})
|
370
|
+
return [dict(row) for row in res.mappings().all()]
|
371
|
+
|
372
|
+
async def aggregate_avg(self, column_name: str, **filters) -> Optional[float]:
|
373
|
+
col = self._resolve_column(column_name)
|
374
|
+
stmt = select(func.avg(col))
|
375
|
+
if hasattr(self.model, "is_deleted"):
|
376
|
+
stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
|
377
|
+
stmt = self._apply_filters(stmt, **filters)
|
378
|
+
return (await self.session.execute(stmt)).scalar()
|
379
|
+
|
380
|
+
async def aggregate_min(self, column_name: str, **filters):
|
381
|
+
col = self._resolve_column(column_name)
|
382
|
+
stmt = select(func.min(col))
|
383
|
+
if hasattr(self.model, "is_deleted"):
|
384
|
+
stmt = stmt.where(self.model.is_deleted == False)
|
385
|
+
stmt = self._apply_filters(stmt, **filters)
|
386
|
+
return (await self.session.execute(stmt)).scalar()
|
387
|
+
|
388
|
+
async def aggregate_max(self, column_name: str, **filters):
|
389
|
+
col = self._resolve_column(column_name)
|
390
|
+
stmt = select(func.max(col))
|
391
|
+
if hasattr(self.model, "is_deleted"):
|
392
|
+
stmt = stmt.where(self.model.is_deleted == False)
|
393
|
+
stmt = self._apply_filters(stmt, **filters)
|
394
|
+
return (await self.session.execute(stmt)).scalar()
|
395
|
+
|
396
|
+
async def aggregate_sum(self, column_name: str, **filters):
|
397
|
+
col = self._resolve_column(column_name)
|
398
|
+
stmt = select(func.sum(col))
|
399
|
+
if hasattr(self.model, "is_deleted"):
|
400
|
+
stmt = stmt.where(self.model.is_deleted == False)
|
401
|
+
stmt = self._apply_filters(stmt, **filters)
|
402
|
+
return (await self.session.execute(stmt)).scalar()
|
403
|
+
|
404
|
+
async def count(self, **filters) -> int:
|
405
|
+
include_deleted = bool(filters.pop("include_deleted", False))
|
406
|
+
stmt = select(func.count()).select_from(self.model)
|
407
|
+
if hasattr(self.model, "is_deleted") and not include_deleted:
|
408
|
+
stmt = stmt.where(self.model.is_deleted == False) # noqa: E712
|
409
|
+
if filters:
|
410
|
+
stmt = stmt.filter_by(**filters)
|
411
|
+
return int((await self.session.execute(stmt)).scalar_one())
|
412
|
+
|
413
|
+
async def restore(self, id_: Any) -> bool:
|
414
|
+
if not hasattr(self.model, "is_deleted"):
|
415
|
+
raise RuntimeError(f"{self.model.__name__} has no 'is_deleted' field")
|
416
|
+
obj = await self.session.get(self.model, id_)
|
417
|
+
if not obj:
|
418
|
+
return False
|
419
|
+
if getattr(obj, "is_deleted", False):
|
420
|
+
setattr(obj, "is_deleted", False)
|
421
|
+
await self.session.flush()
|
422
|
+
return True
|
423
|
+
return False
|
@@ -0,0 +1,12 @@
|
|
1
|
+
SARepo/__init__.py,sha256=tNYbuUDloC1qVnXq7uomS3jRcaPUvy0VEZiMdYR6x1M,183
|
2
|
+
SARepo/base.py,sha256=UbAdZ9WYh_o93mrCVL3D8Q0tY_8mvm0HspO_L5m0GTQ,874
|
3
|
+
SARepo/models.py,sha256=ypSmbKAijvNH4WjSeJBgyNT8mKa_e_7-I5kiNisjGAI,570
|
4
|
+
SARepo/repo.py,sha256=N_LaqguNE3Y_9vZBPBvqQreu-6Sy9-BZFR3-pZl5eT4,1777
|
5
|
+
SARepo/sa_repo.py,sha256=iIscSTNoZGb8XMTY66_fiooWPVwGMOMYAL9BC_xS7bI,16691
|
6
|
+
SARepo/specs.py,sha256=e-X1cCkva3e71M37hcfbjNDMezZAaOkkRaPToUzhEeU,687
|
7
|
+
SARepo/uow.py,sha256=yPlvi7PoH9x2pLNt6hEin9zT5qG9axUKn_TkZcIuz1s,859
|
8
|
+
sarepo-0.1.5.dist-info/licenses/LICENSE,sha256=px90NOQCrnZ77qoFT8IvAiNS1CXQ2OU27uVBKbki9DM,131
|
9
|
+
sarepo-0.1.5.dist-info/METADATA,sha256=m9z_QHD-D1dN95nAGWvpFGAUT7hCad990Lb-nNS5two,2691
|
10
|
+
sarepo-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
11
|
+
sarepo-0.1.5.dist-info/top_level.txt,sha256=0k952UYVZLIIv3kZzFlxI0yzBMusZo3-XCQtxms_kS0,7
|
12
|
+
sarepo-0.1.5.dist-info/RECORD,,
|
sarepo-0.1.3.dist-info/RECORD
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
SARepo/__init__.py,sha256=tNYbuUDloC1qVnXq7uomS3jRcaPUvy0VEZiMdYR6x1M,183
|
2
|
-
SARepo/base.py,sha256=UbAdZ9WYh_o93mrCVL3D8Q0tY_8mvm0HspO_L5m0GTQ,874
|
3
|
-
SARepo/models.py,sha256=ypSmbKAijvNH4WjSeJBgyNT8mKa_e_7-I5kiNisjGAI,570
|
4
|
-
SARepo/repo.py,sha256=5Af4SLfbKOMJdX7MOev4OcGIGQ0HzN6QDUktURjasyU,597
|
5
|
-
SARepo/sa_repo.py,sha256=feuwBHAEdW_STJ6fHxYlSO0KvSruoqiB_v1sTCRy6JE,5332
|
6
|
-
SARepo/specs.py,sha256=e-X1cCkva3e71M37hcfbjNDMezZAaOkkRaPToUzhEeU,687
|
7
|
-
SARepo/uow.py,sha256=yPlvi7PoH9x2pLNt6hEin9zT5qG9axUKn_TkZcIuz1s,859
|
8
|
-
sarepo-0.1.3.dist-info/licenses/LICENSE,sha256=px90NOQCrnZ77qoFT8IvAiNS1CXQ2OU27uVBKbki9DM,131
|
9
|
-
sarepo-0.1.3.dist-info/METADATA,sha256=FXf9N-SsghCIQGucviE8GVVxh1viF0BV0FhKkMeg2HQ,2691
|
10
|
-
sarepo-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
11
|
-
sarepo-0.1.3.dist-info/top_level.txt,sha256=0k952UYVZLIIv3kZzFlxI0yzBMusZo3-XCQtxms_kS0,7
|
12
|
-
sarepo-0.1.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|