rb-commons 0.4.13__py3-none-any.whl → 0.5.1__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.
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import uuid
2
- from typing import TypeVar, Type, Generic, Optional, List, Dict, Literal, Union, Sequence
3
- from sqlalchemy import select, delete, update, and_, func, desc, inspect
4
+ from typing import TypeVar, Type, Generic, Optional, List, Dict, Literal, Union, Sequence, Any, Iterable
5
+ from sqlalchemy import select, delete, update, and_, func, desc, inspect, or_
4
6
  from sqlalchemy.exc import IntegrityError, SQLAlchemyError, NoResultFound
5
7
  from sqlalchemy.ext.asyncio import AsyncSession
6
8
  from sqlalchemy.orm import declarative_base, InstrumentedAttribute, selectinload, RelationshipProperty
@@ -10,6 +12,46 @@ from rb_commons.orm.exceptions import DatabaseException, InternalException
10
12
 
11
13
  ModelType = TypeVar('ModelType', bound=declarative_base())
12
14
 
15
+ class Q:
16
+ """Boolean logic container that can be combined with `&`, `|`, and `~`."""
17
+
18
+ def __init__(self, **lookups: Any) -> None:
19
+ self.lookups: Dict[str, Any] = lookups
20
+ self.children: List[Q] = []
21
+ self._operator: str = "AND"
22
+ self.negated: bool = False
23
+
24
+ def _combine(self, other: "Q", operator: str) -> "Q":
25
+ combined = Q()
26
+ combined.children = [self, other]
27
+ combined._operator = operator
28
+ return combined
29
+
30
+ def __or__(self, other: "Q") -> "Q":
31
+ return self._combine(other, "OR")
32
+
33
+ def __and__(self, other: "Q") -> "Q":
34
+ return self._combine(other, "AND")
35
+
36
+ def __invert__(self) -> "Q":
37
+ clone = Q()
38
+ clone.lookups = self.lookups.copy()
39
+ clone.children = list(self.children)
40
+ clone._operator = self._operator
41
+ clone.negated = not self.negated
42
+ return clone
43
+
44
+ def __repr__(self) -> str:
45
+ if self.lookups:
46
+ base = f"Q({self.lookups})"
47
+ else:
48
+ base = "Q()"
49
+ if self.children:
50
+ base += f" {self._operator} {self.children}"
51
+ if self.negated:
52
+ base = f"NOT({base})"
53
+ return base
54
+
13
55
  def with_transaction_error_handling(func):
14
56
  async def wrapper(self, *args, **kwargs):
15
57
  try:
@@ -29,336 +71,198 @@ class BaseManager(Generic[ModelType]):
29
71
  model: Type[ModelType]
30
72
 
31
73
  def __init__(self, session: AsyncSession) -> None:
32
- self.session = session
33
- self.data = None
34
- self.filters = []
35
- self._filtered = False
36
- self._limit = None
74
+ self.session: AsyncSession = session
75
+ self.filters: List[Any] = []
76
+ self._filtered: bool = False
77
+ self._limit: Optional[int] = None
37
78
 
38
79
  async def _smart_commit(self, instance: Optional[ModelType] = None) -> Optional[ModelType]:
39
80
  if not self.session.in_transaction():
40
81
  await self.session.commit()
41
-
42
- if instance:
82
+ if instance is not None:
43
83
  await self.session.refresh(instance)
44
84
  return instance
85
+ return None
86
+
87
+ def _parse_lookup(self, lookup: str, value: Any):
88
+ parts = lookup.split("__")
89
+ operator = "eq"
90
+ if parts[-1] in {"eq", "ne", "gt", "lt", "gte", "lte", "in", "contains", "null"}:
91
+ operator = parts.pop()
92
+
93
+ current: Union[Type[ModelType], InstrumentedAttribute] = self.model
94
+ # walk relationships
95
+ for field in parts:
96
+ attr = getattr(current, field, None)
97
+ if attr is None:
98
+ raise ValueError(f"Invalid filter field: {'.'.join(parts)}")
99
+ if hasattr(attr, "property") and isinstance(attr.property, RelationshipProperty):
100
+ current = attr.property.mapper.class_
101
+ else:
102
+ current = attr
103
+
104
+ if operator == "eq":
105
+ return current == value
106
+ if operator == "ne":
107
+ return current != value
108
+ if operator == "gt":
109
+ return current > value
110
+ if operator == "lt":
111
+ return current < value
112
+ if operator == "gte":
113
+ return current >= value
114
+ if operator == "lte":
115
+ return current <= value
116
+ if operator == "in":
117
+ if not isinstance(value, (list, tuple, set)):
118
+ raise ValueError(f"{lookup}__in requires an iterable")
119
+ return current.in_(value)
120
+ if operator == "contains":
121
+ return current.ilike(f"%{value}%")
122
+ if operator == "null":
123
+ return current.is_(None) if value else current.isnot(None)
124
+ raise ValueError(f"Unsupported operator in lookup: {lookup}")
125
+
126
+ def _q_to_expr(self, q: Q):
127
+ clauses: List[Any] = [self._parse_lookup(k, v) for k, v in q.lookups.items()]
128
+ for child in q.children:
129
+ clauses.append(self._q_to_expr(child))
130
+ combined = (
131
+ True
132
+ if not clauses
133
+ else (or_(*clauses) if q._operator == "OR" else and_(*clauses))
134
+ )
135
+ return ~combined if q.negated else combined
45
136
 
46
- async def get(self, pk: Union[str, int, uuid.UUID], load_relations: Sequence[str] = None) -> Optional[ModelType]:
47
- """
48
- get object based on conditions
49
- """
50
- stmt = select(self.model).filter_by(id=pk)
137
+ def filter(self, *expressions: Any, **lookups: Any) -> "BaseManager[ModelType]":
138
+ """Add **AND** constraints (default behaviour).
51
139
 
52
- if load_relations:
53
- for rel in load_relations:
54
- stmt = stmt.options(selectinload(getattr(self.model, rel)))
55
-
56
- result = await self.session.execute(stmt)
57
- instance = result.scalar_one_or_none()
140
+ * `expressions` can be raw SQLAlchemy clauses **or** `Q` objects.
141
+ * `lookups` are Django‑style keyword filters.
142
+ """
143
+ self._filtered = True
144
+ for expr in expressions:
145
+ self.filters.append(self._q_to_expr(expr) if isinstance(expr, Q) else expr)
146
+ for k, v in lookups.items():
147
+ self.filters.append(self._parse_lookup(k, v))
148
+ return self
58
149
 
59
- if instance is None:
60
- raise NotFoundException(
61
- message="Object does not exist",
62
- status=404,
63
- code="0001",
64
- )
150
+ def or_filter(self, *expressions: Any, **lookups: Any) -> "BaseManager[ModelType]":
151
+ """Add one OR group (shortcut for `filter(Q() | Q())`)."""
152
+ or_clauses: List[Any] = []
153
+ for expr in expressions:
154
+ or_clauses.append(self._q_to_expr(expr) if isinstance(expr, Q) else expr)
155
+ for k, v in lookups.items():
156
+ or_clauses.append(self._parse_lookup(k, v))
157
+ if or_clauses:
158
+ self._filtered = True
159
+ self.filters.append(or_(*or_clauses))
160
+ return self
65
161
 
66
- return instance
162
+ def limit(self, value: int) -> "BaseManager[ModelType]":
163
+ self._limit = value
164
+ return self
67
165
 
68
166
  def _apply_eager_loading(self, stmt, load_all_relations: bool = False):
69
167
  if not load_all_relations:
70
168
  return stmt
71
-
72
- opts = []
73
- visited = set()
169
+ opts: List[Any] = []
170
+ visited: set[Any] = set()
74
171
 
75
172
  def recurse(model, loader=None):
76
173
  mapper = inspect(model)
77
174
  if mapper in visited:
78
175
  return
79
176
  visited.add(mapper)
80
-
81
177
  for rel in mapper.relationships:
82
178
  attr = getattr(model, rel.key)
83
- if loader is None:
84
- this_loader = selectinload(attr)
85
- else:
86
- this_loader = loader.selectinload(attr)
179
+ this_loader = selectinload(attr) if loader is None else loader.selectinload(attr)
87
180
  opts.append(this_loader)
88
181
  recurse(rel.mapper.class_, this_loader)
89
182
 
90
183
  recurse(self.model)
91
184
  return stmt.options(*opts)
92
185
 
93
- def filter(self, **kwargs) -> 'BaseManager[ModelType]':
94
- """
95
- Dynamically apply filters to the query.
96
-
97
- Supported operators:
98
- - __eq (e.g., field__eq=value) or just field=value
99
- - __ne (field__ne=value)
100
- - __gt (field__gt=value)
101
- - __lt (field__lt=value)
102
- - __gte (field__gte=value)
103
- - __lte (field__lte=value)
104
- - __in (field__in=[val1, val2, ...])
105
- - __contains (field__contains='text')
106
- - __null (field__null=True/False) - True for IS NULL, False for IS NOT NULL
107
-
108
- Additionally supports nested paths, e.g.,
109
- product__shop_id=None
110
- product__shop__country='US'
111
- """
112
- self._filtered = True
113
- self.filters = []
114
-
115
- for key, value in kwargs.items():
116
- parts = key.split("__")
117
-
118
- operator = "eq"
119
-
120
- if parts[-1] in {"eq", "ne", "gt", "lt", "gte", "lte", "in", "contains", "null"}:
121
- operator = parts.pop()
122
-
123
- current_attr = self.model
124
-
125
- for field_name in parts:
126
- attr_candidate = getattr(current_attr, field_name, None)
127
- if attr_candidate is None:
128
- raise ValueError(f"Invalid filter field: {'.'.join(parts)}")
129
-
130
- if hasattr(attr_candidate, "property") and isinstance(attr_candidate.property, RelationshipProperty):
131
- current_attr = attr_candidate.property.mapper.class_
132
- else:
133
- current_attr = attr_candidate
134
-
135
- if operator == "eq":
136
- # e.g., column == value
137
- self.filters.append(current_attr == value)
138
-
139
- elif operator == "ne":
140
- # e.g., column != value
141
- self.filters.append(current_attr != value)
142
-
143
- elif operator == "gt":
144
- self.filters.append(current_attr > value)
145
-
146
- elif operator == "lt":
147
- self.filters.append(current_attr < value)
148
-
149
- elif operator == "gte":
150
- self.filters.append(current_attr >= value)
151
-
152
- elif operator == "lte":
153
- self.filters.append(current_attr <= value)
154
-
155
- elif operator == "in":
156
- if not isinstance(value, list):
157
- raise ValueError(f"{'.'.join(parts)}__in requires a list, got {type(value)}")
158
- self.filters.append(current_attr.in_(value))
159
-
160
- elif operator == "contains":
161
- # e.g., column ILIKE %value%
162
- self.filters.append(current_attr.ilike(f"%{value}%"))
163
-
164
- elif operator == "null":
165
- if value is True:
166
- self.filters.append(current_attr.is_(None))
167
- else:
168
- self.filters.append(current_attr.isnot(None))
169
-
170
- return self
171
-
172
- def _ensure_filtered(self):
173
- """Ensure that `filter()` has been called before using certain methods."""
174
- if not self._filtered:
175
- raise RuntimeError("You must call `filter()` before using this method.")
176
-
177
- async def _execute_query_and_unique_data(self, stmt, load_all_relations: bool) -> List[ModelType]:
186
+ async def _execute_query(self, stmt, load_all_relations: bool):
178
187
  stmt = self._apply_eager_loading(stmt, load_all_relations)
179
188
  result = await self.session.execute(stmt)
180
189
  rows = result.scalars().all()
181
- unique_by_pk = {obj.id: obj for obj in rows}
182
- return list(unique_by_pk.values())
190
+ return list({obj.id: obj for obj in rows}.values())
183
191
 
184
- async def all(self, load_all_relations: bool = False) -> List[ModelType] | None:
185
- try:
186
- stmt = select(self.model)
187
-
188
- if self._filtered:
189
- stmt = stmt.filter(and_(*self.filters))
190
-
191
- if self._limit:
192
- stmt = stmt.limit(self._limit)
193
-
194
- self._clear_query_state()
195
-
196
- return await self._execute_query_and_unique_data(stmt, load_all_relations)
197
- finally:
198
- self._clear_query_state()
199
-
200
- def _clear_query_state(self):
201
- """Clear all query state after execution"""
192
+ def _reset_state(self):
193
+ self.filters.clear()
202
194
  self._filtered = False
203
- self.filters = []
204
195
  self._limit = None
205
196
 
206
- async def paginate(self, limit: int = 10, offset: int = 0, load_all_relations: bool = False) -> List[ModelType]:
207
- self._ensure_filtered()
208
- stmt = select(self.model).filter(and_(*self.filters)).limit(limit).offset(offset)
209
- return await self._execute_query_and_unique_data(stmt, load_all_relations)
210
-
211
- def limit(self, value: int) -> 'BaseManager[ModelType]':
212
- """
213
- Set a limit on the number of results returned by queries like `all()` or `first()`.
214
- """
215
- self._limit = value
216
- return self
197
+ async def all(self, load_all_relations: bool = False):
198
+ stmt = select(self.model)
199
+ if self.filters:
200
+ stmt = stmt.filter(and_(*self.filters))
201
+ if self._limit:
202
+ stmt = stmt.limit(self._limit)
203
+ try:
204
+ return await self._execute_query(stmt, load_all_relations)
205
+ finally:
206
+ self._reset_state()
217
207
 
218
- async def first(self, load_relations: Sequence[str] = None) -> Optional[ModelType]:
219
- """Return the first matching object, or None."""
208
+ async def first(self, load_relations: Optional[Sequence[str]] = None):
220
209
  self._ensure_filtered()
221
-
222
210
  stmt = select(self.model).filter(and_(*self.filters))
223
-
224
211
  if load_relations:
225
212
  for rel in load_relations:
226
213
  stmt = stmt.options(selectinload(getattr(self.model, rel)))
227
-
228
214
  result = await self.session.execute(stmt)
215
+ self._reset_state()
229
216
  return result.scalars().first()
230
217
 
231
- async def last(self, load_relations: Sequence[str] = None) -> Optional[ModelType]:
232
- """Return the last matching object, or None."""
233
-
218
+ async def last(self, load_relations: Optional[Sequence[str]] = None):
234
219
  self._ensure_filtered()
235
-
236
- stmt = select(self.model).filter(and_(*self.filters)).order_by(desc(self.model.id))
237
-
220
+ stmt = (
221
+ select(self.model)
222
+ .filter(and_(*self.filters))
223
+ .order_by(desc(self.model.id))
224
+ )
238
225
  if load_relations:
239
226
  for rel in load_relations:
240
227
  stmt = stmt.options(selectinload(getattr(self.model, rel)))
241
-
242
228
  result = await self.session.execute(stmt)
229
+ self._reset_state()
243
230
  return result.scalars().first()
244
231
 
245
232
  async def count(self) -> int:
246
- """Return the count of matching records."""
247
-
248
233
  self._ensure_filtered()
249
234
  query = select(func.count()).select_from(self.model).filter(and_(*self.filters))
250
235
  result = await self.session.execute(query)
236
+ self._reset_state()
251
237
  return result.scalar_one()
252
238
 
239
+ async def paginate(
240
+ self,
241
+ limit: int = 10,
242
+ offset: int = 0,
243
+ load_all_relations: bool = False,
244
+ ):
245
+ self._ensure_filtered()
246
+ stmt = (
247
+ select(self.model)
248
+ .filter(and_(*self.filters))
249
+ .limit(limit)
250
+ .offset(offset)
251
+ )
252
+ try:
253
+ return await self._execute_query(stmt, load_all_relations)
254
+ finally:
255
+ self._reset_state()
256
+
253
257
  @with_transaction_error_handling
254
- async def create(self, **kwargs) -> ModelType:
255
- """
256
- Create a new object
257
- """
258
+ async def create(self, **kwargs):
258
259
  obj = self.model(**kwargs)
259
-
260
260
  self.session.add(obj)
261
261
  await self.session.flush()
262
262
  return await self._smart_commit(obj)
263
263
 
264
264
  @with_transaction_error_handling
265
- async def delete(self, instance: ModelType = None) -> bool:
266
- """
267
- Delete object(s) with flexible filtering options
268
- - If `instance` is provided, delete that single instance.
269
- - If `instance` is not provided, delete according to self.filters.
270
-
271
- :arg instance: Model instance to delete.
272
- :return: Number of deleted records or None
273
- """
274
-
275
- if instance is not None:
276
- await self.session.delete(instance)
277
- await self.session.commit()
278
- return True
279
-
280
- self._ensure_filtered()
281
-
282
- try:
283
- delete_stmt = delete(self.model).where(and_(*self.filters))
284
- await self.session.execute(delete_stmt)
285
- await self.session.commit()
286
- return True
287
- except NoResultFound:
288
- return False
289
-
290
- @with_transaction_error_handling
291
- async def bulk_delete(self) -> int:
292
- """
293
- Bulk delete with flexible filtering.
294
-
295
- Automatically commits if not inside a transaction.
296
-
297
- :return: Number of deleted records
298
- """
299
- self._ensure_filtered()
300
-
301
- delete_stmt = delete(self.model).where(and_(*self.filters))
302
- result = await self.session.execute(delete_stmt)
303
- await self._smart_commit()
304
- return result.rowcount # ignore
305
-
306
- @with_transaction_error_handling
307
- async def update_by_filters(self, filters: Dict, **update_fields) -> Optional[ModelType]:
308
- """
309
- Update object(s) with flexible filtering options
310
-
311
- :param filters: Conditions for selecting records to update
312
- :param update_fields: Fields and values to update
313
- :return: Number of updated records
314
- """
315
- if not update_fields:
316
- raise InternalException("No fields provided for update")
317
-
318
- update_stmt = update(self.model).filter_by(**filters).values(**update_fields)
319
- await self.session.execute(update_stmt)
320
- await self.session.commit()
321
- updated_instance = await self.get(**filters)
322
- return updated_instance
323
-
324
- @with_transaction_error_handling
325
- async def update(self, instance: ModelType, **update_fields) -> Optional[ModelType]:
326
- """
327
- Update an existing database instance with new fields
328
-
329
- :param instance: The database model instance to update
330
- :param update_fields: Keyword arguments of fields to update
331
- :return: The updated instance
332
-
333
- :raises InternalException: If integrity violation
334
- :raises DatabaseException: For database-related errors
335
- """
336
- # Validate update fields
337
- if not update_fields:
338
- raise InternalException("No fields provided for update")
339
-
340
- # Apply updates directly to the instance
341
- for key, value in update_fields.items():
342
- setattr(instance, key, value)
343
-
344
- self.session.add(instance)
345
- await self._smart_commit()
346
-
347
- return instance
348
-
349
- @with_transaction_error_handling
350
- async def save(self, instance: ModelType) -> Optional[ModelType]:
351
- """
352
- Save instance
353
-
354
- :param instance: The database model instance to save
355
- :return: The saved instance
356
-
357
- Automatically commits if not inside a transaction.
358
-
359
- :raises InternalException: If integrity violation
360
- :raises DatabaseException: For database-related errors
361
- """
265
+ async def save(self, instance: ModelType):
362
266
  self.session.add(instance)
363
267
  await self.session.flush()
364
268
  return await self._smart_commit(instance)
@@ -393,71 +297,97 @@ class BaseManager(Generic[ModelType]):
393
297
 
394
298
  return loaded_instance
395
299
 
396
- async def is_exists(self, **kwargs) -> bool:
397
- stmt = select(self.model).filter_by(**kwargs)
398
- result = await self.session.execute(stmt)
399
- return result.scalar_one_or_none() is not None
300
+ @with_transaction_error_handling
301
+ async def update(self, instance: ModelType, **fields):
302
+ if not fields:
303
+ raise InternalException("No fields provided for update")
304
+ for k, v in fields.items():
305
+ setattr(instance, k, v)
306
+ self.session.add(instance)
307
+ await self._smart_commit()
308
+ return instance
400
309
 
401
310
  @with_transaction_error_handling
402
- async def bulk_save(self, instances: List[ModelType]) -> None:
403
- """
404
- Bulk save a list of instances into the database.
311
+ async def update_by_filters(self, filters: Dict[str, Any], **fields):
312
+ if not fields:
313
+ raise InternalException("No fields provided for update")
314
+ stmt = update(self.model).filter_by(**filters).values(**fields)
315
+ await self.session.execute(stmt)
316
+ await self.session.commit()
317
+ return await self.get(**filters)
405
318
 
406
- If inside a transaction, flushes only.
407
- If not in a transaction, commits after flush.
319
+ @with_transaction_error_handling
320
+ async def delete(self, instance: Optional[ModelType] = None):
321
+ if instance is not None:
322
+ await self.session.delete(instance)
323
+ await self.session.commit()
324
+ return True
325
+ self._ensure_filtered()
326
+ stmt = delete(self.model).where(and_(*self.filters))
327
+ await self.session.execute(stmt)
328
+ await self.session.commit()
329
+ self._reset_state()
330
+ return True
408
331
 
409
- :param instances: List of instances
410
- :raises DatabaseException: If any database error occurs
411
- :raises InternalException: If any unexpected error occurs
412
- """
332
+ @with_transaction_error_handling
333
+ async def bulk_save(self, instances: Iterable[ModelType]):
413
334
  if not instances:
414
335
  return
415
-
416
- self.session.add_all(instances)
336
+ self.session.add_all(list(instances))
417
337
  await self.session.flush()
418
-
419
338
  if not self.session.in_transaction():
420
339
  await self.session.commit()
421
340
 
422
- def has_relation(self, relation_name: str) -> 'BaseManager[ModelType]':
423
- """
424
- Check if a relationship exists between models using an EXISTS subquery.
425
-
426
- :param relation_name Name of the relationship to check. Must be a valid relationship
427
- defined in the model.
428
-
429
- :return BaseManager[ModelType]: Self instance for method chaining.
341
+ @with_transaction_error_handling
342
+ async def bulk_delete(self):
343
+ self._ensure_filtered()
344
+ stmt = delete(self.model).where(and_(*self.filters))
345
+ result = await self.session.execute(stmt)
346
+ await self._smart_commit()
347
+ self._reset_state()
348
+ return result.rowcount
349
+
350
+ async def get(
351
+ self,
352
+ pk: Union[str, int, uuid.UUID],
353
+ load_relations: Optional[Sequence[str]] = None,
354
+ ):
355
+ stmt = select(self.model).filter_by(id=pk)
356
+ if load_relations:
357
+ for rel in load_relations:
358
+ stmt = stmt.options(selectinload(getattr(self.model, rel)))
359
+ result = await self.session.execute(stmt)
360
+ instance = result.scalar_one_or_none()
361
+ if instance is None:
362
+ raise NotFoundException("Object does not exist", 404, "0001")
363
+ return instance
430
364
 
431
- :raise DatabaseException: If there's an error constructing the subquery.
432
- :raise InternalException: If there's an unexpected error in relationship handling.
365
+ async def is_exists(self, **lookups):
366
+ stmt = select(self.model).filter_by(**lookups)
367
+ result = await self.session.execute(stmt)
368
+ return result.scalar_one_or_none() is not None
433
369
 
434
- Notes:
435
- - The relationship must be properly defined in the SQLAlchemy model
436
- - Uses a non-correlated EXISTS subquery for better performance
437
- - Silently continues if the relationship doesn't exist
438
- """
439
- # Get the relationship property
370
+ def has_relation(self, relation_name: str):
440
371
  relationship = getattr(self.model, relation_name)
441
-
442
- # Create subquery using select
443
372
  subquery = (
444
373
  select(1)
445
374
  .select_from(relationship.property.mapper.class_)
446
375
  .where(relationship.property.primaryjoin)
447
376
  .exists()
448
377
  )
449
-
450
- # Add the exists condition to filters
451
378
  self.filters.append(subquery)
452
-
379
+ self._filtered = True
453
380
  return self
454
381
 
455
- def model_to_dict(self, instance: ModelType, exclude: set = None):
382
+ def model_to_dict(self, instance: ModelType, exclude: set[str] | None = None):
456
383
  exclude = exclude or set()
457
-
458
384
  return {
459
- c.key: getattr(instance, c.key)
460
- for c in inspect(instance).mapper.column_attrs
461
- if c.key not in exclude
385
+ col.key: getattr(instance, col.key)
386
+ for col in inspect(instance).mapper.column_attrs
387
+ if col.key not in exclude
462
388
  }
463
389
 
390
+ def _ensure_filtered(self):
391
+ if not self._filtered:
392
+ raise RuntimeError("You must call `filter()` before this operation.")
393
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rb-commons
3
- Version: 0.4.13
3
+ Version: 0.5.1
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=dip4cVh6BHPJJP91gsdvC9ez0m1Z3zvZkWz0Fom2C_0,16944
14
+ rb_commons/orm/managers.py,sha256=w0vTrGfugWDC1ccGU2b19DfGQ6_6X3CWMbBzkjpQiFs,14659
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.4.13.dist-info/METADATA,sha256=5rjQ8F4d_351Pc-2ZX9BD7jxoFyfE7aNV6Q0HP3JQkA,6571
24
- rb_commons-0.4.13.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
25
- rb_commons-0.4.13.dist-info/top_level.txt,sha256=HPx_WAYo3_fbg1WCeGHsz3wPGio1ucbnrlm2lmqlJog,11
26
- rb_commons-0.4.13.dist-info/RECORD,,
23
+ rb_commons-0.5.1.dist-info/METADATA,sha256=VDh2GcvBL_Uhvka30qhbR_MCnAcxjP-swb4OPhXyz6Q,6570
24
+ rb_commons-0.5.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
25
+ rb_commons-0.5.1.dist-info/top_level.txt,sha256=HPx_WAYo3_fbg1WCeGHsz3wPGio1ucbnrlm2lmqlJog,11
26
+ rb_commons-0.5.1.dist-info/RECORD,,