rb-commons 0.3.8__py3-none-any.whl → 0.4.0__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.
@@ -64,52 +64,94 @@ class BaseManager(Generic[ModelType]):
64
64
 
65
65
  return instance
66
66
 
67
+ def _apply_eager_loading(self, stmt, load_all_relations: bool = False):
68
+ """
69
+ Apply selectinload for eager loading relationships.
70
+
71
+ :param stmt: SQLAlchemy select statement
72
+ :param load_all_relations: If True, load all relationships
73
+ :return: Modified statement with eager loading applied
74
+ """
75
+ if load_all_relations:
76
+ mapper = inspect(self.model)
77
+ load_relations = [rel.key for rel in mapper.relationships()]
78
+
79
+ if load_relations:
80
+ for rel in load_relations:
81
+ stmt = stmt.options(selectinload(getattr(self.model, rel)))
82
+
83
+ return stmt
84
+
67
85
  def filter(self, **kwargs) -> 'BaseManager[ModelType]':
68
86
  """
69
- Apply filtering conditions dynamically.
70
-
71
- Supports:
72
- - `field__eq`: Equal (`=`)
73
- - `field__ne`: Not Equal (`!=`)
74
- - `field__gt`: Greater Than (`>`)
75
- - `field__lt`: Less Than (`<`)
76
- - `field__gte`: Greater Than or Equal (`>=`)
77
- - `field__lte`: Less Than or Equal (`<=`)
78
- - `field__in`: IN Query
79
- - `field__contains`: LIKE Query
80
- """
87
+ Dynamically apply filters to the query.
88
+
89
+ Supported operators:
90
+ - __eq (e.g., field__eq=value) or just field=value
91
+ - __ne (field__ne=value)
92
+ - __gt (field__gt=value)
93
+ - __lt (field__lt=value)
94
+ - __gte (field__gte=value)
95
+ - __lte (field__lte=value)
96
+ - __in (field__in=[val1, val2, ...])
97
+ - __contains (field__contains='text')
98
+
99
+ Additionally supports nested paths, e.g.,
100
+ product__shop_id=None
101
+ product__shop__country='US'
102
+ """
81
103
  self._filtered = True
82
104
  self.filters = []
83
105
 
106
+ allowed_operators = {"eq", "ne", "gt", "lt", "gte", "lte", "in", "contains"}
107
+
84
108
  for key, value in kwargs.items():
85
- if '__' in key:
86
- field_name, operator = key.split('__', 1)
109
+ parts = key.split("__")
110
+
111
+ if parts[-1] in allowed_operators:
112
+ operator = parts[-1]
113
+ field_path = parts[:-1]
87
114
  else:
88
- field_name, operator = key, 'eq'
115
+ operator = "eq"
116
+ field_path = parts
117
+
118
+ current_attr = self.model
119
+ for idx, field_name in enumerate(field_path):
120
+ current_attr = getattr(current_attr, field_name, None)
121
+ if current_attr is None:
122
+ raise ValueError(f"Invalid filter field: {'.'.join(field_path)}")
89
123
 
90
- column = getattr(self.model, field_name, None)
91
- if column is None or not isinstance(column, InstrumentedAttribute):
92
- raise ValueError(f"Invalid filter field: {field_name}")
124
+ if not isinstance(current_attr, InstrumentedAttribute):
125
+ raise ValueError(f"Invalid filter field: {'.'.join(field_path)}")
93
126
 
94
127
  if operator == "eq":
95
- self.filters.append(column == value)
128
+ # e.g., column == value
129
+ self.filters.append(current_attr == value)
130
+
96
131
  elif operator == "ne":
97
- self.filters.append(column != value)
132
+ # e.g., column != value
133
+ self.filters.append(current_attr != value)
134
+
98
135
  elif operator == "gt":
99
- self.filters.append(column > value)
136
+ self.filters.append(current_attr > value)
137
+
100
138
  elif operator == "lt":
101
- self.filters.append(column < value)
139
+ self.filters.append(current_attr < value)
140
+
102
141
  elif operator == "gte":
103
- self.filters.append(column >= value)
142
+ self.filters.append(current_attr >= value)
143
+
104
144
  elif operator == "lte":
105
- self.filters.append(column <= value)
106
- elif operator == "in" and isinstance(value, list):
145
+ self.filters.append(current_attr <= value)
146
+
147
+ elif operator == "in":
107
148
  if not isinstance(value, list):
108
- raise ValueError(f"`{field_name}__in` requires a list, got {type(value)}")
149
+ raise ValueError(f"{'.'.join(field_path)}__in requires a list, got {type(value)}")
150
+ self.filters.append(current_attr.in_(value))
109
151
 
110
- self.filters.append(column.in_(value))
111
152
  elif operator == "contains":
112
- self.filters.append(column.ilike(f"%{value}%"))
153
+ # e.g., column ILIKE %value%
154
+ self.filters.append(current_attr.ilike(f"%{value}%"))
113
155
 
114
156
  return self
115
157
 
@@ -118,13 +160,11 @@ class BaseManager(Generic[ModelType]):
118
160
  if not self._filtered:
119
161
  raise RuntimeError("You must call `filter()` before using this method.")
120
162
 
121
- async def all(self) -> List[ModelType]:
122
- """Return all results based on applied filters."""
163
+ async def all(self, load_all_relations: bool = False) -> List[ModelType]:
123
164
  self._ensure_filtered()
124
165
 
125
- query = select(self.model)
126
- if self.filters:
127
- query = query.filter(and_(*self.filters))
166
+ query = select(self.model).filter(and_(*self.filters))
167
+ query = self._apply_eager_loading(query, load_all_relations)
128
168
 
129
169
  result = await self.session.execute(query)
130
170
  return list(result.scalars().all())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rb-commons
3
- Version: 0.3.8
3
+ Version: 0.4.0
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=iAlsBfcxVt_GUfbJckTiFgTCIDYzWcXqyyat8qC4ThQ,12475
14
+ rb_commons/orm/managers.py,sha256=IktMS_oq0drkt_MwBZTYPE179YgjkQB1HfaDcvnr5Zw,13928
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=3ZTwKclavAKSheO58fybo2uLDeTkd-iluTY7hUEjRPk,957
@@ -19,7 +19,7 @@ rb_commons/schemes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
19
19
  rb_commons/schemes/jwt.py,sha256=F66JJDhholuOPPzlKeoC6f1TL4gXg4oRUrV5yheNpyo,1675
20
20
  rb_commons/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  rb_commons/utils/media.py,sha256=KNY_9SdRa3Rp7d3B1tZaXkhmzVa65RcS62BYwZP1bVM,332
22
- rb_commons-0.3.8.dist-info/METADATA,sha256=QroXJUdprFJlGnWXdg2jfdOxC88PqY5cM36HDq57cFM,6570
23
- rb_commons-0.3.8.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
24
- rb_commons-0.3.8.dist-info/top_level.txt,sha256=HPx_WAYo3_fbg1WCeGHsz3wPGio1ucbnrlm2lmqlJog,11
25
- rb_commons-0.3.8.dist-info/RECORD,,
22
+ rb_commons-0.4.0.dist-info/METADATA,sha256=MrBcay1HyOdGBeKwC8yohoDXLFi0tsjsMdk_S39Lcag,6570
23
+ rb_commons-0.4.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
24
+ rb_commons-0.4.0.dist-info/top_level.txt,sha256=HPx_WAYo3_fbg1WCeGHsz3wPGio1ucbnrlm2lmqlJog,11
25
+ rb_commons-0.4.0.dist-info/RECORD,,