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.
- rb_commons/orm/managers.py +73 -33
- {rb_commons-0.3.8.dist-info → rb_commons-0.4.0.dist-info}/METADATA +1 -1
- {rb_commons-0.3.8.dist-info → rb_commons-0.4.0.dist-info}/RECORD +5 -5
- {rb_commons-0.3.8.dist-info → rb_commons-0.4.0.dist-info}/WHEEL +0 -0
- {rb_commons-0.3.8.dist-info → rb_commons-0.4.0.dist-info}/top_level.txt +0 -0
rb_commons/orm/managers.py
CHANGED
@@ -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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
86
|
-
|
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
|
-
|
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
|
-
|
91
|
-
|
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
|
-
|
128
|
+
# e.g., column == value
|
129
|
+
self.filters.append(current_attr == value)
|
130
|
+
|
96
131
|
elif operator == "ne":
|
97
|
-
|
132
|
+
# e.g., column != value
|
133
|
+
self.filters.append(current_attr != value)
|
134
|
+
|
98
135
|
elif operator == "gt":
|
99
|
-
self.filters.append(
|
136
|
+
self.filters.append(current_attr > value)
|
137
|
+
|
100
138
|
elif operator == "lt":
|
101
|
-
self.filters.append(
|
139
|
+
self.filters.append(current_attr < value)
|
140
|
+
|
102
141
|
elif operator == "gte":
|
103
|
-
self.filters.append(
|
142
|
+
self.filters.append(current_attr >= value)
|
143
|
+
|
104
144
|
elif operator == "lte":
|
105
|
-
self.filters.append(
|
106
|
-
|
145
|
+
self.filters.append(current_attr <= value)
|
146
|
+
|
147
|
+
elif operator == "in":
|
107
148
|
if not isinstance(value, list):
|
108
|
-
raise ValueError(f"
|
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
|
-
|
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
|
-
|
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())
|
@@ -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=
|
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.
|
23
|
-
rb_commons-0.
|
24
|
-
rb_commons-0.
|
25
|
-
rb_commons-0.
|
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,,
|
File without changes
|
File without changes
|