fastapi-repository 0.0.1__py3-none-any.whl → 0.0.3__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.
- fastapi_repository/base.py +13 -14
- {fastapi_repository-0.0.1.dist-info → fastapi_repository-0.0.3.dist-info}/METADATA +16 -4
- fastapi_repository-0.0.3.dist-info/RECORD +6 -0
- fastapi_repository-0.0.1.dist-info/RECORD +0 -6
- {fastapi_repository-0.0.1.dist-info → fastapi_repository-0.0.3.dist-info}/WHEEL +0 -0
- {fastapi_repository-0.0.1.dist-info → fastapi_repository-0.0.3.dist-info}/top_level.txt +0 -0
fastapi_repository/base.py
CHANGED
@@ -7,20 +7,20 @@ from sqlalchemy import func, update, delete
|
|
7
7
|
from typing import Optional, List, Union, Dict, Any
|
8
8
|
|
9
9
|
OPERATORS = {
|
10
|
-
#
|
10
|
+
# Exact match
|
11
11
|
"exact": lambda col, val: col == val,
|
12
12
|
"iexact": lambda col, val: col.ilike(val),
|
13
|
-
#
|
13
|
+
# Partial match
|
14
14
|
"contains": lambda col, val: col.contains(val),
|
15
15
|
"icontains": lambda col, val: col.ilike(f"%{val}%"),
|
16
|
-
#
|
16
|
+
# IN clause
|
17
17
|
"in": lambda col, val: col.in_(val) if isinstance(val, list) else col.in_([val]),
|
18
|
-
#
|
18
|
+
# Comparison operators
|
19
19
|
"gt": lambda col, val: col > val,
|
20
20
|
"gte": lambda col, val: col >= val,
|
21
21
|
"lt": lambda col, val: col < val,
|
22
22
|
"lte": lambda col, val: col <= val,
|
23
|
-
#
|
23
|
+
# Starts/ends with
|
24
24
|
"startswith": lambda col, val: col.startswith(val),
|
25
25
|
"istartswith": lambda col, val: col.ilike(f"{val}%"),
|
26
26
|
"endswith": lambda col, val: col.endswith(val),
|
@@ -154,7 +154,8 @@ class BaseRepository:
|
|
154
154
|
conditions = []
|
155
155
|
if not disable_default_scope:
|
156
156
|
default_conditions = await self.__get_conditions(**self.default_scope)
|
157
|
-
|
157
|
+
if default_conditions:
|
158
|
+
conditions.extend(default_conditions)
|
158
159
|
|
159
160
|
conditions += await self.__get_conditions(**search_params)
|
160
161
|
query = select(func.count("*")).select_from(self.model).where(*conditions)
|
@@ -210,7 +211,7 @@ class BaseRepository:
|
|
210
211
|
|
211
212
|
def _apply_order_by(self, query, sorted_by: str, sorted_order: str):
|
212
213
|
"""
|
213
|
-
|
214
|
+
Helper to apply order_by to a query.
|
214
215
|
"""
|
215
216
|
column = getattr(self.model, sorted_by, None)
|
216
217
|
if not column:
|
@@ -231,14 +232,14 @@ class BaseRepository:
|
|
231
232
|
"""
|
232
233
|
conditions = []
|
233
234
|
for key, value in search_params.items():
|
234
|
-
#
|
235
|
+
# If "__" is included in the key, split into field name and operator
|
235
236
|
if "__" in key:
|
236
237
|
parts = key.split("__")
|
237
238
|
op = "exact"
|
238
|
-
if parts[-1] in OPERATORS: #
|
239
|
+
if parts[-1] in OPERATORS: # If the last part is an operator, remove it
|
239
240
|
op = parts.pop()
|
240
241
|
|
241
|
-
#
|
242
|
+
# Simple column: foo__icontains=bar
|
242
243
|
if len(parts) == 1:
|
243
244
|
column = getattr(self.model, parts[0], None)
|
244
245
|
if column is None:
|
@@ -248,7 +249,7 @@ class BaseRepository:
|
|
248
249
|
conditions.append(OPERATORS[op](column, value))
|
249
250
|
continue
|
250
251
|
|
251
|
-
#
|
252
|
+
# One-hop relationship: rel__field__op=value
|
252
253
|
rel_attr = getattr(self.model, parts[0], None)
|
253
254
|
if rel_attr is None or not hasattr(rel_attr, "property"):
|
254
255
|
raise AttributeError(
|
@@ -263,7 +264,7 @@ class BaseRepository:
|
|
263
264
|
conditions.append(rel_attr.any(OPERATORS[op](target_column, value)))
|
264
265
|
continue
|
265
266
|
else:
|
266
|
-
# "__"
|
267
|
+
# If "__" is not included, treat as simple eq (=) comparison
|
267
268
|
column = getattr(self.model, key, None)
|
268
269
|
if column is None:
|
269
270
|
raise AttributeError(
|
@@ -271,8 +272,6 @@ class BaseRepository:
|
|
271
272
|
)
|
272
273
|
conditions.append(column == value)
|
273
274
|
|
274
|
-
return conditions
|
275
|
-
|
276
275
|
async def create(self, **create_params):
|
277
276
|
"""
|
278
277
|
Generic create method that instantiates the model,
|
@@ -1,8 +1,8 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fastapi-repository
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.3
|
4
4
|
Summary: A base repository for FastAPI projects, inspired by Ruby on Rails' Active Record and Ransack.
|
5
|
-
Author-email: Seiya
|
5
|
+
Author-email: Peter Seiya Takahashi <seiya4@icloud.com>
|
6
6
|
Project-URL: Homepage, https://github.com/seiyat/fastapi-repository
|
7
7
|
Project-URL: Bug Tracker, https://github.com/seiyat/fastapi-repository/issues
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
@@ -15,7 +15,15 @@ Requires-Dist: fastapi>=0.70.0
|
|
15
15
|
|
16
16
|
# FastAPI Repository
|
17
17
|
|
18
|
-
A base repository for FastAPI projects, inspired by Ruby on Rails' Active Record and Ransack.
|
18
|
+
A base repository for FastAPI projects, inspired by Ruby on Rails' [Active Record](https://github.com/rails/rails/tree/main/activerecord) and [Ransack](https://github.com/activerecord-hackery/ransack). It provides a simple, intuitive interface for data access in asynchronous applications using SQLAlchemy.
|
19
|
+
|
20
|
+
## Features
|
21
|
+
|
22
|
+
- **Async-first:** Designed for modern asynchronous Python.
|
23
|
+
- **Simple CRUD:** `find`, `create`, `update`, `destroy` methods out of the box.
|
24
|
+
- **Powerful Filtering:** Use Ransack-style operators (`__icontains`, `__gt`, etc.) for complex queries.
|
25
|
+
- **Eager & Lazy Loading:** Control relationship loading with `joinedload` and `lazyload`.
|
26
|
+
- **Default Scoping:** Apply default conditions to all queries.
|
19
27
|
|
20
28
|
## Installation
|
21
29
|
|
@@ -23,7 +31,7 @@ A base repository for FastAPI projects, inspired by Ruby on Rails' Active Record
|
|
23
31
|
pip install fastapi-repository
|
24
32
|
```
|
25
33
|
|
26
|
-
##
|
34
|
+
## Quick Start
|
27
35
|
|
28
36
|
```python
|
29
37
|
from fastapi_repository import BaseRepository
|
@@ -34,3 +42,7 @@ class UserRepository(BaseRepository):
|
|
34
42
|
def __init__(self, session: AsyncSession):
|
35
43
|
super().__init__(session, model=User)
|
36
44
|
```
|
45
|
+
|
46
|
+
## Documentation
|
47
|
+
|
48
|
+
For a complete guide, including all available methods, advanced filtering, and default scoping, please see the [full documentation](https://github.com/PeterTakahashi/fastapi-repository/blob/main/docs/index.md).
|
@@ -0,0 +1,6 @@
|
|
1
|
+
fastapi_repository/__init__.py,sha256=ymIcIyfYlbv-TudpI3UOU1f2y57tEnRIFIYQMjnw0bI,87
|
2
|
+
fastapi_repository/base.py,sha256=y_Wdl3ODhH_srhqK5UDq4tkjzRTw16VZ91CBbtkBIRI,12289
|
3
|
+
fastapi_repository-0.0.3.dist-info/METADATA,sha256=htos5cwMKRDqR4F1j2JldRAm47IYdJtAr5NlsjHCO8s,1958
|
4
|
+
fastapi_repository-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
5
|
+
fastapi_repository-0.0.3.dist-info/top_level.txt,sha256=SSUZqBKCDo6XNjAhSFvpv4tmiPW1COl86ZR5B4ucBkU,19
|
6
|
+
fastapi_repository-0.0.3.dist-info/RECORD,,
|
@@ -1,6 +0,0 @@
|
|
1
|
-
fastapi_repository/__init__.py,sha256=ymIcIyfYlbv-TudpI3UOU1f2y57tEnRIFIYQMjnw0bI,87
|
2
|
-
fastapi_repository/base.py,sha256=5RpkNKXnl3cozCiHkWhU7sGY0QIY_36BmT8AoGge0kM,12325
|
3
|
-
fastapi_repository-0.0.1.dist-info/METADATA,sha256=X-xa_Q5eCVrC4IPlenM8dl6LwEhFrp5RrnRoWleUkBc,1089
|
4
|
-
fastapi_repository-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
5
|
-
fastapi_repository-0.0.1.dist-info/top_level.txt,sha256=SSUZqBKCDo6XNjAhSFvpv4tmiPW1COl86ZR5B4ucBkU,19
|
6
|
-
fastapi_repository-0.0.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|