fastapi-fsp 0.2.3__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.
- fastapi_fsp/__init__.py +43 -0
- fastapi_fsp/builder.py +339 -0
- fastapi_fsp/config.py +158 -0
- fastapi_fsp/filters.py +372 -0
- fastapi_fsp/fsp.py +192 -298
- fastapi_fsp/pagination.py +324 -0
- fastapi_fsp/presets.py +267 -0
- fastapi_fsp/sorting.py +71 -0
- {fastapi_fsp-0.2.3.dist-info → fastapi_fsp-0.4.0.dist-info}/METADATA +159 -1
- fastapi_fsp-0.4.0.dist-info/RECORD +13 -0
- fastapi_fsp-0.2.3.dist-info/RECORD +0 -7
- {fastapi_fsp-0.2.3.dist-info → fastapi_fsp-0.4.0.dist-info}/WHEEL +0 -0
- {fastapi_fsp-0.2.3.dist-info → fastapi_fsp-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-fsp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Filter, Sort, and Paginate (FSP) utilities for FastAPI + SQLModel
|
|
5
5
|
Project-URL: Homepage, https://github.com/fromej-dev/fastapi-fsp
|
|
6
6
|
Project-URL: Repository, https://github.com/fromej-dev/fastapi-fsp
|
|
@@ -205,6 +205,164 @@ GET /heroes/?field=full_name&operator=starts_with&value=Spider&field=age&operato
|
|
|
205
205
|
- The field should be declared as `ClassVar[type]` in the SQLModel base class to work with Pydantic
|
|
206
206
|
- Only computed fields with SQL expressions are supported; Python-only properties cannot be filtered at the database level
|
|
207
207
|
|
|
208
|
+
## FilterBuilder API
|
|
209
|
+
|
|
210
|
+
For programmatic filter creation, use the fluent `FilterBuilder` API:
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from fastapi_fsp import FilterBuilder
|
|
214
|
+
|
|
215
|
+
# Instead of manually creating Filter objects:
|
|
216
|
+
# filters = [
|
|
217
|
+
# Filter(field="age", operator=FilterOperator.GTE, value="30"),
|
|
218
|
+
# Filter(field="city", operator=FilterOperator.EQ, value="Chicago"),
|
|
219
|
+
# ]
|
|
220
|
+
|
|
221
|
+
# Use the builder pattern:
|
|
222
|
+
filters = (
|
|
223
|
+
FilterBuilder()
|
|
224
|
+
.where("age").gte(30)
|
|
225
|
+
.where("city").eq("Chicago")
|
|
226
|
+
.where("active").eq(True)
|
|
227
|
+
.where("tags").in_(["python", "fastapi"])
|
|
228
|
+
.where("created_at").between("2024-01-01", "2024-12-31")
|
|
229
|
+
.build()
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Use with FSPManager
|
|
233
|
+
@app.get("/heroes/")
|
|
234
|
+
def read_heroes(session: Session = Depends(get_session), fsp: FSPManager = Depends(FSPManager)):
|
|
235
|
+
additional_filters = FilterBuilder().where("deleted").eq(False).build()
|
|
236
|
+
fsp.with_filters(additional_filters)
|
|
237
|
+
return fsp.generate_response(select(Hero), session)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Available FilterBuilder Methods
|
|
241
|
+
|
|
242
|
+
| Method | Description |
|
|
243
|
+
|--------|-------------|
|
|
244
|
+
| `.eq(value)` | Equal to |
|
|
245
|
+
| `.ne(value)` | Not equal to |
|
|
246
|
+
| `.gt(value)` | Greater than |
|
|
247
|
+
| `.gte(value)` | Greater than or equal |
|
|
248
|
+
| `.lt(value)` | Less than |
|
|
249
|
+
| `.lte(value)` | Less than or equal |
|
|
250
|
+
| `.like(pattern)` | Case-sensitive LIKE |
|
|
251
|
+
| `.ilike(pattern)` | Case-insensitive LIKE |
|
|
252
|
+
| `.in_(values)` | IN list |
|
|
253
|
+
| `.not_in(values)` | NOT IN list |
|
|
254
|
+
| `.between(low, high)` | BETWEEN range |
|
|
255
|
+
| `.is_null()` | IS NULL |
|
|
256
|
+
| `.is_not_null()` | IS NOT NULL |
|
|
257
|
+
| `.starts_with(prefix)` | Starts with (case-insensitive) |
|
|
258
|
+
| `.ends_with(suffix)` | Ends with (case-insensitive) |
|
|
259
|
+
| `.contains(substring)` | Contains (case-insensitive) |
|
|
260
|
+
|
|
261
|
+
## Common Filter Presets
|
|
262
|
+
|
|
263
|
+
For frequently used filter patterns, use `CommonFilters`:
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from fastapi_fsp import CommonFilters
|
|
267
|
+
|
|
268
|
+
# Active (non-deleted) records
|
|
269
|
+
filters = CommonFilters.active() # deleted=false
|
|
270
|
+
|
|
271
|
+
# Recent records (last 7 days)
|
|
272
|
+
filters = CommonFilters.recent(days=7)
|
|
273
|
+
|
|
274
|
+
# Date range
|
|
275
|
+
filters = CommonFilters.date_range(start=datetime(2024, 1, 1), end=datetime(2024, 12, 31))
|
|
276
|
+
|
|
277
|
+
# Records created today
|
|
278
|
+
filters = CommonFilters.today()
|
|
279
|
+
|
|
280
|
+
# Null checks
|
|
281
|
+
filters = CommonFilters.not_null("email")
|
|
282
|
+
filters = CommonFilters.is_null("deleted_at")
|
|
283
|
+
|
|
284
|
+
# Search
|
|
285
|
+
filters = CommonFilters.search("name", "john", match_type="contains")
|
|
286
|
+
|
|
287
|
+
# Combine presets
|
|
288
|
+
filters = CommonFilters.active() + CommonFilters.recent(days=30)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Configuration
|
|
292
|
+
|
|
293
|
+
Customize FSPManager behavior with `FSPConfig`:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from fastapi_fsp import FSPConfig, FSPPresets
|
|
297
|
+
|
|
298
|
+
# Custom configuration
|
|
299
|
+
config = FSPConfig(
|
|
300
|
+
max_per_page=50,
|
|
301
|
+
default_per_page=20,
|
|
302
|
+
strict_mode=True, # Raise errors for unknown fields
|
|
303
|
+
max_page=100,
|
|
304
|
+
allow_deep_pagination=False,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# Or use presets
|
|
308
|
+
config = FSPPresets.strict() # strict_mode=True
|
|
309
|
+
config = FSPPresets.limited_pagination(max_page=50) # Limit deep pagination
|
|
310
|
+
config = FSPPresets.high_volume(max_per_page=500) # High-volume APIs
|
|
311
|
+
|
|
312
|
+
# Apply configuration
|
|
313
|
+
@app.get("/heroes/")
|
|
314
|
+
def read_heroes(session: Session = Depends(get_session), fsp: FSPManager = Depends(FSPManager)):
|
|
315
|
+
fsp.apply_config(config)
|
|
316
|
+
return fsp.generate_response(select(Hero), session)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Strict Mode
|
|
320
|
+
|
|
321
|
+
When `strict_mode=True`, FSPManager raises HTTP 400 errors for unknown filter/sort fields:
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
# With strict_mode=True, this raises HTTP 400:
|
|
325
|
+
# GET /heroes/?field=unknown_field&operator=eq&value=test
|
|
326
|
+
# Error: "Unknown field 'unknown_field'. Available fields: age, id, name, secret_name"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Convenience Methods
|
|
330
|
+
|
|
331
|
+
### from_model()
|
|
332
|
+
|
|
333
|
+
Simplify common queries with `from_model()`:
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
@app.get("/heroes/")
|
|
337
|
+
def read_heroes(session: Session = Depends(get_session), fsp: FSPManager = Depends(FSPManager)):
|
|
338
|
+
# Instead of:
|
|
339
|
+
# query = select(Hero)
|
|
340
|
+
# return fsp.generate_response(query, session)
|
|
341
|
+
|
|
342
|
+
# Use:
|
|
343
|
+
return fsp.from_model(Hero, session)
|
|
344
|
+
|
|
345
|
+
# Async version
|
|
346
|
+
@app.get("/heroes/")
|
|
347
|
+
async def read_heroes(session: AsyncSession = Depends(get_session), fsp: FSPManager = Depends(FSPManager)):
|
|
348
|
+
return await fsp.from_model_async(Hero, session)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Method Chaining
|
|
352
|
+
|
|
353
|
+
Chain configuration methods:
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
@app.get("/heroes/")
|
|
357
|
+
def read_heroes(session: Session = Depends(get_session), fsp: FSPManager = Depends(FSPManager)):
|
|
358
|
+
return (
|
|
359
|
+
fsp
|
|
360
|
+
.with_filters(CommonFilters.active())
|
|
361
|
+
.apply_config(FSPPresets.strict())
|
|
362
|
+
.generate_response(select(Hero), session)
|
|
363
|
+
)
|
|
364
|
+
```
|
|
365
|
+
|
|
208
366
|
## Response model
|
|
209
367
|
|
|
210
368
|
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
fastapi_fsp/__init__.py,sha256=ncmpcclGRIGQsBkRHN-kGDfSgLFZE4_1f0PotzZ83ys,1218
|
|
2
|
+
fastapi_fsp/builder.py,sha256=due8kTVApNXWZJGVDhqe6Ch9jCqdjnNe0rvWNUOgVMw,9660
|
|
3
|
+
fastapi_fsp/config.py,sha256=GcrSyv_wOvNgLFq00fhsZznZH4Fnl0dCBJa73n2QwIs,4977
|
|
4
|
+
fastapi_fsp/filters.py,sha256=zVGPur83pNrDLgIWF2JdYdLHdHGPH-WpZcJNKjkG6Xs,11796
|
|
5
|
+
fastapi_fsp/fsp.py,sha256=ZoTVzrpzvTNQ02hoFbERhYwqFr4rQ6nGcsBcsMlicJo,18440
|
|
6
|
+
fastapi_fsp/models.py,sha256=1MwLBQFmUP8OwO3Gqby1u7s9ruimCR2XGfUzAqF4Tj4,2034
|
|
7
|
+
fastapi_fsp/pagination.py,sha256=hSro5BiyEVl_ZX2kw5-dSj4hiCh_5J7y2i4T3zNOJG0,10544
|
|
8
|
+
fastapi_fsp/presets.py,sha256=hpfUmCaeqoCeb1PimpUoGEW5S0Ycc-yBEZQq6vJWv50,8500
|
|
9
|
+
fastapi_fsp/sorting.py,sha256=rd6lQV3gz_XQ1bokklRjL8AKGiZ3KV7XIu9PyYNbde0,2125
|
|
10
|
+
fastapi_fsp-0.4.0.dist-info/METADATA,sha256=u-qSLGRucBbjEk1cVbu0r91hr-4zl86tSvzgaiYU6fI,12610
|
|
11
|
+
fastapi_fsp-0.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
12
|
+
fastapi_fsp-0.4.0.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
|
|
13
|
+
fastapi_fsp-0.4.0.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
fastapi_fsp/__init__.py,sha256=0I_XN_ptNu1NyNRpjdyVm6nwvrilAB8FyT2EfgVF_QA,215
|
|
2
|
-
fastapi_fsp/fsp.py,sha256=b6OuHgbTpWrFtxGKm7fcPpjHtD3Cdjrs22m3IYOBhp4,23986
|
|
3
|
-
fastapi_fsp/models.py,sha256=1MwLBQFmUP8OwO3Gqby1u7s9ruimCR2XGfUzAqF4Tj4,2034
|
|
4
|
-
fastapi_fsp-0.2.3.dist-info/METADATA,sha256=w4wfXs3hye-UI0-h2a81OjkG3yzzZWUq91n1HhmyUi4,8224
|
|
5
|
-
fastapi_fsp-0.2.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
-
fastapi_fsp-0.2.3.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
|
|
7
|
-
fastapi_fsp-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|