rb-commons 0.5.0__py3-none-any.whl → 0.5.2__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.
@@ -75,6 +75,7 @@ class BaseManager(Generic[ModelType]):
75
75
  self.filters: List[Any] = []
76
76
  self._filtered: bool = False
77
77
  self._limit: Optional[int] = None
78
+ self._order_by: List[Any] = []
78
79
 
79
80
  async def _smart_commit(self, instance: Optional[ModelType] = None) -> Optional[ModelType]:
80
81
  if not self.session.in_transaction():
@@ -134,6 +135,22 @@ class BaseManager(Generic[ModelType]):
134
135
  )
135
136
  return ~combined if q.negated else combined
136
137
 
138
+ def order_by(self, *columns: Any):
139
+ """Collect ORDER BY clauses.
140
+ """
141
+ for col in columns:
142
+ if isinstance(col, str):
143
+ descending = col.startswith("-")
144
+ field_name = col.lstrip("+-")
145
+ sa_col = getattr(self.model, field_name, None)
146
+ if sa_col is None:
147
+ raise ValueError(f"Invalid order_by field '{field_name}' for {self.model.__name__}")
148
+ self._order_by.append(sa_col.desc() if descending else sa_col.asc())
149
+ else:
150
+ self._order_by.append(col)
151
+
152
+ return self
153
+
137
154
  def filter(self, *expressions: Any, **lookups: Any) -> "BaseManager[ModelType]":
138
155
  """Add **AND** constraints (default behaviour).
139
156
 
@@ -198,6 +215,8 @@ class BaseManager(Generic[ModelType]):
198
215
  stmt = select(self.model)
199
216
  if self.filters:
200
217
  stmt = stmt.filter(and_(*self.filters))
218
+ if self._order_by:
219
+ stmt = stmt.order_by(*self._order_by)
201
220
  if self._limit:
202
221
  stmt = stmt.limit(self._limit)
203
222
  try:
@@ -208,6 +227,8 @@ class BaseManager(Generic[ModelType]):
208
227
  async def first(self, load_relations: Optional[Sequence[str]] = None):
209
228
  self._ensure_filtered()
210
229
  stmt = select(self.model).filter(and_(*self.filters))
230
+ if self._order_by:
231
+ stmt = stmt.order_by(*self._order_by)
211
232
  if load_relations:
212
233
  for rel in load_relations:
213
234
  stmt = stmt.options(selectinload(getattr(self.model, rel)))
@@ -215,13 +236,12 @@ class BaseManager(Generic[ModelType]):
215
236
  self._reset_state()
216
237
  return result.scalars().first()
217
238
 
239
+
218
240
  async def last(self, load_relations: Optional[Sequence[str]] = None):
219
241
  self._ensure_filtered()
220
- stmt = (
221
- select(self.model)
222
- .filter(and_(*self.filters))
223
- .order_by(desc(self.model.id))
224
- )
242
+ stmt = select(self.model).filter(and_(*self.filters))
243
+ order = self._order_by or [self.model.id.desc()]
244
+ stmt = stmt.order_by(*order[::-1]) # reverse for LAST
225
245
  if load_relations:
226
246
  for rel in load_relations:
227
247
  stmt = stmt.options(selectinload(getattr(self.model, rel)))
@@ -229,6 +249,7 @@ class BaseManager(Generic[ModelType]):
229
249
  self._reset_state()
230
250
  return result.scalars().first()
231
251
 
252
+
232
253
  async def count(self) -> int:
233
254
  self._ensure_filtered()
234
255
  query = select(func.count()).select_from(self.model).filter(and_(*self.filters))
@@ -236,19 +257,12 @@ class BaseManager(Generic[ModelType]):
236
257
  self._reset_state()
237
258
  return result.scalar_one()
238
259
 
239
- async def paginate(
240
- self,
241
- limit: int = 10,
242
- offset: int = 0,
243
- load_all_relations: bool = False,
244
- ):
260
+ async def paginate(self, limit=10, offset=0, load_all_relations=False):
245
261
  self._ensure_filtered()
246
- stmt = (
247
- select(self.model)
248
- .filter(and_(*self.filters))
249
- .limit(limit)
250
- .offset(offset)
251
- )
262
+ stmt = select(self.model).filter(and_(*self.filters))
263
+ if self._order_by:
264
+ stmt = stmt.order_by(*self._order_by)
265
+ stmt = stmt.limit(limit).offset(offset)
252
266
  try:
253
267
  return await self._execute_query(stmt, load_all_relations)
254
268
  finally:
@@ -267,6 +281,36 @@ class BaseManager(Generic[ModelType]):
267
281
  await self.session.flush()
268
282
  return await self._smart_commit(instance)
269
283
 
284
+ @with_transaction_error_handling
285
+ async def lazy_save(self, instance: ModelType, load_relations: Sequence[str] = None) -> Optional[ModelType]:
286
+ self.session.add(instance)
287
+ await self.session.flush()
288
+ await self._smart_commit(instance)
289
+
290
+ if load_relations is None:
291
+ mapper = inspect(self.model)
292
+ load_relations = [rel.key for rel in mapper.relationships]
293
+
294
+ if not load_relations:
295
+ return instance
296
+
297
+ stmt = select(self.model).filter_by(id=instance.id)
298
+
299
+ for rel in load_relations:
300
+ stmt = stmt.options(selectinload(getattr(self.model, rel)))
301
+
302
+ result = await self.session.execute(stmt)
303
+ loaded_instance = result.scalar_one_or_none()
304
+
305
+ if loaded_instance is None:
306
+ raise NotFoundException(
307
+ message="Object saved but could not be retrieved with relationships",
308
+ status=404,
309
+ code="0001",
310
+ )
311
+
312
+ return loaded_instance
313
+
270
314
  @with_transaction_error_handling
271
315
  async def update(self, instance: ModelType, **fields):
272
316
  if not fields:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rb-commons
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Commons of project and simplified orm based on sqlalchemy.
5
5
  Home-page: https://github.com/RoboSell-organization/rb-commons
6
6
  Author: Abdulvoris
@@ -11,7 +11,7 @@ rb_commons/http/consul.py,sha256=Ioq72VD1jGwoC96set7n2SgxN40olzI-myA2lwKkYi4,186
11
11
  rb_commons/http/exceptions.py,sha256=EGRMr1cRgiJ9Q2tkfANbf0c6-zzXf1CD6J3cmCaT_FA,1885
12
12
  rb_commons/orm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  rb_commons/orm/exceptions.py,sha256=1aMctiEwrPjyehoXVX1l6ML5ZOhmDkmBISzlTD5ey1Y,509
14
- rb_commons/orm/managers.py,sha256=Tk33tD4l94IkliHFBR_BRy-jrc-JgVh9S_mXn8v0rpM,13620
14
+ rb_commons/orm/managers.py,sha256=N-iHsuWMUQDTV-unB1I8Ey5CLiwwLVvJPEcpdaVj-SU,15485
15
15
  rb_commons/orm/services.py,sha256=71eRcJ4TxZvzNz-hLXo12X4U7PGK54ZfbLAb27AjZi8,1589
16
16
  rb_commons/permissions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  rb_commons/permissions/role_permissions.py,sha256=HL50s5RHW1nk2U3-YTESg0EDGkJDu1vN4QchcfK-L5g,988
@@ -20,7 +20,7 @@ rb_commons/schemes/jwt.py,sha256=F66JJDhholuOPPzlKeoC6f1TL4gXg4oRUrV5yheNpyo,167
20
20
  rb_commons/schemes/pagination.py,sha256=8VZW1wZGJIPR9jEBUgppZUoB4uqP8ORudHkMwvEJSxg,1866
21
21
  rb_commons/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  rb_commons/utils/media.py,sha256=KNY_9SdRa3Rp7d3B1tZaXkhmzVa65RcS62BYwZP1bVM,332
23
- rb_commons-0.5.0.dist-info/METADATA,sha256=bT0B3meXrn9ME0juZhAq0XFg58MaAJxltKGBzpSHEjg,6570
24
- rb_commons-0.5.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
25
- rb_commons-0.5.0.dist-info/top_level.txt,sha256=HPx_WAYo3_fbg1WCeGHsz3wPGio1ucbnrlm2lmqlJog,11
26
- rb_commons-0.5.0.dist-info/RECORD,,
23
+ rb_commons-0.5.2.dist-info/METADATA,sha256=uChN50M_0DtG_N9Qj7IA4V6RcqCtIUdbe8hPmaDk5-8,6570
24
+ rb_commons-0.5.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
25
+ rb_commons-0.5.2.dist-info/top_level.txt,sha256=HPx_WAYo3_fbg1WCeGHsz3wPGio1ucbnrlm2lmqlJog,11
26
+ rb_commons-0.5.2.dist-info/RECORD,,