rb-commons 0.2.6__tar.gz → 0.2.7__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.
Files changed (29) hide show
  1. {rb_commons-0.2.6 → rb_commons-0.2.7}/PKG-INFO +1 -1
  2. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/orm/managers.py +90 -78
  3. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons.egg-info/PKG-INFO +1 -1
  4. {rb_commons-0.2.6 → rb_commons-0.2.7}/setup.py +1 -1
  5. {rb_commons-0.2.6 → rb_commons-0.2.7}/README.md +0 -0
  6. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/__init__.py +0 -0
  7. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/broker/__init__.py +0 -0
  8. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/broker/consumer.py +0 -0
  9. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/configs/__init__.py +0 -0
  10. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/configs/config.py +0 -0
  11. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/configs/injections.py +0 -0
  12. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/configs/rabbitmq.py +0 -0
  13. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/http/__init__.py +0 -0
  14. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/http/base_api.py +0 -0
  15. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/http/consul.py +0 -0
  16. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/http/exceptions.py +0 -0
  17. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/orm/__init__.py +0 -0
  18. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/orm/exceptions.py +0 -0
  19. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/permissions/__init__.py +0 -0
  20. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/permissions/role_permissions.py +0 -0
  21. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/schemes/__init__.py +0 -0
  22. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/schemes/jwt.py +0 -0
  23. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/utils/__init__.py +0 -0
  24. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons/utils/media.py +0 -0
  25. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons.egg-info/SOURCES.txt +0 -0
  26. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons.egg-info/dependency_links.txt +0 -0
  27. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons.egg-info/requires.txt +0 -0
  28. {rb_commons-0.2.6 → rb_commons-0.2.7}/rb_commons.egg-info/top_level.txt +0 -0
  29. {rb_commons-0.2.6 → rb_commons-0.2.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rb-commons
3
- Version: 0.2.6
3
+ Version: 0.2.7
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
@@ -1,6 +1,6 @@
1
1
  import uuid
2
2
  from typing import TypeVar, Type, Generic, Optional, List, Dict, Literal, Union
3
- from sqlalchemy import select, delete, update, and_
3
+ from sqlalchemy import select, delete, update, and_, func
4
4
  from sqlalchemy.exc import IntegrityError, SQLAlchemyError, NoResultFound
5
5
  from sqlalchemy.ext.asyncio import AsyncSession
6
6
  from sqlalchemy.orm import declarative_base, InstrumentedAttribute
@@ -10,6 +10,21 @@ from rb_commons.orm.exceptions import DatabaseException, InternalException
10
10
 
11
11
  ModelType = TypeVar('ModelType', bound=declarative_base())
12
12
 
13
+ def with_transaction_error_handling(func):
14
+ async def wrapper(self, *args, **kwargs):
15
+ try:
16
+ return await func(self, *args, **kwargs)
17
+ except IntegrityError as e:
18
+ await self.session.rollback()
19
+ raise InternalException(f"Constraint violation: {str(e)}") from e
20
+ except SQLAlchemyError as e:
21
+ await self.session.rollback()
22
+ raise DatabaseException(f"Database error: {str(e)}") from e
23
+ except Exception as e:
24
+ await self.session.rollback()
25
+ raise InternalException(f"Unexpected error: {str(e)}") from e
26
+ return wrapper
27
+
13
28
  class BaseManager(Generic[ModelType]):
14
29
  model: Type[ModelType]
15
30
 
@@ -19,6 +34,19 @@ class BaseManager(Generic[ModelType]):
19
34
  self.filters = []
20
35
  self._filtered = False
21
36
 
37
+ async def _persist(self, instance: ModelType) -> Optional[ModelType]:
38
+ self.session.add(instance)
39
+ await self.session.flush()
40
+ return await self._smart_commit(instance)
41
+
42
+ async def _smart_commit(self, instance: Optional[ModelType] = None) -> Optional[ModelType]:
43
+ if not self.session.in_transaction():
44
+ await self.session.commit()
45
+
46
+ if instance:
47
+ await self.session.refresh(instance)
48
+ return instance
49
+
22
50
  async def get(self, pk: Union[str, int, uuid.UUID]) -> Optional[ModelType]:
23
51
  """
24
52
  get object based on conditions
@@ -111,35 +139,24 @@ class BaseManager(Generic[ModelType]):
111
139
 
112
140
  async def count(self) -> int:
113
141
  """Return the count of matching records."""
114
- self._ensure_filtered()
115
142
 
116
- query = select(self.model).filter(and_(*self.filters))
143
+ self._ensure_filtered()
144
+ query = select(func.count()).select_from(self.model).filter(and_(*self.filters))
117
145
  result = await self.session.execute(query)
118
- return len(result.scalars().all())
146
+ return result.scalar_one()
119
147
 
148
+ @with_transaction_error_handling
120
149
  async def create(self, **kwargs) -> ModelType:
121
150
  """
122
151
  Create a new object
123
152
  """
124
153
  obj = self.model(**kwargs)
125
154
 
126
- try:
127
- self.session.add(obj)
128
- await self.session.flush()
129
- await self.session.commit()
130
- await self.session.refresh(obj)
131
- return obj
132
- except IntegrityError as e:
133
- await self.session.rollback()
134
- raise DatabaseException(f"Constraint violation: {str(e)}") from e
135
- except SQLAlchemyError as e:
136
- await self.session.rollback()
137
- raise DatabaseException(f"Database error occurred: {str(e)}") from e
138
- except Exception as e:
139
- await self.session.rollback()
140
- raise InternalException(f"Unexpected error during creation: {str(e)}") from e
141
-
155
+ self.session.add(obj)
156
+ await self.session.flush()
157
+ return await self._smart_commit(obj)
142
158
 
159
+ @with_transaction_error_handling
143
160
  async def delete(self):
144
161
  """
145
162
  Delete object(s) with flexible filtering options
@@ -155,27 +172,24 @@ class BaseManager(Generic[ModelType]):
155
172
  return result.rowcount
156
173
  except NoResultFound:
157
174
  return False
158
- except SQLAlchemyError as e:
159
- await self.session.rollback()
160
- raise DatabaseException(f"Delete operation failed: {str(e)}") from e
161
175
 
176
+ @with_transaction_error_handling
162
177
  async def bulk_delete(self) -> int:
163
178
  """
164
- Bulk delete with flexible filtering
179
+ Bulk delete with flexible filtering.
180
+
181
+ Automatically commits if not inside a transaction.
165
182
 
166
- :return: Number of deleted records
183
+ :return: Number of deleted records
167
184
  """
168
185
  self._ensure_filtered()
169
186
 
170
- try:
171
- delete_stmt = delete(self.model).where(and_(*self.filters))
172
- result = await self.session.execute(delete_stmt)
173
- await self.session.commit()
174
- return result.rowcount()
175
- except SQLAlchemyError as e:
176
- await self.session.rollback()
177
- raise DatabaseException(f"Bulk delete failed: {str(e)}") from e
187
+ delete_stmt = delete(self.model).where(and_(*self.filters))
188
+ result = await self.session.execute(delete_stmt)
189
+ await self._smart_commit()
190
+ return result.rowcount # ignore
178
191
 
192
+ @with_transaction_error_handling
179
193
  async def update_by_filters(self, filters: Dict, **update_fields) -> Optional[ModelType]:
180
194
  """
181
195
  Update object(s) with flexible filtering options
@@ -187,19 +201,13 @@ class BaseManager(Generic[ModelType]):
187
201
  if not update_fields:
188
202
  raise InternalException("No fields provided for update")
189
203
 
190
- try:
191
- update_stmt = update(self.model).filter_by(**filters).values(**update_fields)
192
- await self.session.execute(update_stmt)
193
- await self.session.commit()
194
- updated_instance = await self.get(**filters)
195
- return updated_instance
196
- except IntegrityError as e:
197
- await self.session.rollback()
198
- raise InternalException(f"Constraint violation: {str(e)}") from e
199
- except SQLAlchemyError as e:
200
- await self.session.rollback()
201
- raise DatabaseException(f"Update operation failed: {str(e)}") from e
204
+ update_stmt = update(self.model).filter_by(**filters).values(**update_fields)
205
+ await self.session.execute(update_stmt)
206
+ await self.session.commit()
207
+ updated_instance = await self.get(**filters)
208
+ return updated_instance
202
209
 
210
+ @with_transaction_error_handling
203
211
  async def update(self, instance: ModelType, **update_fields) -> Optional[ModelType]:
204
212
  """
205
213
  Update an existing database instance with new fields
@@ -208,32 +216,23 @@ class BaseManager(Generic[ModelType]):
208
216
  :param update_fields: Keyword arguments of fields to update
209
217
  :return: The updated instance
210
218
 
211
- :raises ValueError: If no update fields are provided
212
- :raises RuntimeError: For database-related errors
219
+ :raises InternalException: If integrity violation
220
+ :raises DatabaseException: For database-related errors
213
221
  """
214
222
  # Validate update fields
215
223
  if not update_fields:
216
224
  raise InternalException("No fields provided for update")
217
225
 
218
- try:
219
- # Apply updates directly to the instance
220
- for key, value in update_fields.items():
221
- setattr(instance, key, value)
222
-
223
- self.session.add(instance)
224
- await self.session.commit()
225
- await self.session.refresh(instance)
226
+ # Apply updates directly to the instance
227
+ for key, value in update_fields.items():
228
+ setattr(instance, key, value)
226
229
 
227
- return instance
230
+ self.session.add(instance)
231
+ await self._smart_commit()
228
232
 
229
- except IntegrityError as e:
230
- await self.session.rollback()
231
- raise InternalException(f"Constraint violation: {str(e)}") from e
232
-
233
- except SQLAlchemyError as e:
234
- await self.session.rollback()
235
- raise DatabaseException(f"Update operation failed: {str(e)}") from e
233
+ return instance
236
234
 
235
+ @with_transaction_error_handling
237
236
  async def save(self, instance: ModelType) -> Optional[ModelType]:
238
237
  """
239
238
  Save instance
@@ -241,24 +240,37 @@ class BaseManager(Generic[ModelType]):
241
240
  :param instance: The database model instance to save
242
241
  :return: The saved instance
243
242
 
244
- :raises ValueError: If no update fields are provided
245
- :raises RuntimeError: For database-related errors
243
+ Automatically commits if not inside a transaction.
244
+
245
+ :raises InternalException: If integrity violation
246
+ :raises DatabaseException: For database-related errors
246
247
  """
247
- try:
248
- self.session.add(instance)
249
- await self.session.commit()
250
- await self.session.refresh(instance)
251
- return instance
248
+ self.session.add(instance)
249
+ await self.session.flush()
250
+ return await self._smart_commit(instance)
252
251
 
253
- except IntegrityError as e:
254
- await self.session.rollback()
255
- raise InternalException(f"Constraint violation: {str(e)}") from e
252
+ async def is_exists(self, **kwargs) -> bool:
253
+ stmt = select(self.model).filter_by(**kwargs)
254
+ result = await self.session.execute(stmt)
255
+ return result.scalar_one_or_none() is not None
256
256
 
257
- except SQLAlchemyError as e:
258
- await self.session.rollback()
259
- raise DatabaseException(f"Update operation failed: {str(e)}") from e
257
+ @with_transaction_error_handling
258
+ async def bulk_save(self, instances: List[ModelType]) -> None:
259
+ """
260
+ Bulk save a list of instances into the database.
260
261
 
262
+ If inside a transaction, flushes only.
263
+ If not in a transaction, commits after flush.
261
264
 
262
- async def is_exists(self, **kwargs) -> bool:
263
- return await self.get(**kwargs) is not None
265
+ :param instances: List of instances
266
+ :raises DatabaseException: If any database error occurs
267
+ :raises InternalException: If any unexpected error occurs
268
+ """
269
+ if not instances:
270
+ return
264
271
 
272
+ self.session.add_all(instances)
273
+ await self.session.flush()
274
+
275
+ if not self.session.in_transaction():
276
+ await self.session.commit()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rb-commons
3
- Version: 0.2.6
3
+ Version: 0.2.7
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
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="rb-commons",
8
- version="0.2.6",
8
+ version="0.2.7",
9
9
  author="Abdulvoris",
10
10
  author_email="erkinovabdulvoris101@gmail.com",
11
11
  description="Commons of project and simplified orm based on sqlalchemy.",
File without changes
File without changes