querymodelling 0.0.16__tar.gz → 0.0.17__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.
- {querymodelling-0.0.16 → querymodelling-0.0.17}/.github/workflows/run-tests.yml +1 -1
- {querymodelling-0.0.16 → querymodelling-0.0.17}/HISTORY.md +7 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/PKG-INFO +82 -1
- querymodelling-0.0.17/README.md +91 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/_version.py +3 -3
- querymodelling-0.0.17/querymodelling/sql_filter.py +808 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling.egg-info/PKG-INFO +82 -1
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling.egg-info/SOURCES.txt +3 -1
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling.egg-info/scm_file_list.json +3 -1
- querymodelling-0.0.17/querymodelling.egg-info/scm_version.json +8 -0
- querymodelling-0.0.17/tests/sql_filter_test.py +358 -0
- querymodelling-0.0.16/README.md +0 -10
- querymodelling-0.0.16/querymodelling.egg-info/scm_version.json +0 -8
- {querymodelling-0.0.16 → querymodelling-0.0.17}/.github/workflows/run-publish.yml +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/.gitignore +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/.readthedocs.yaml +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/.vscode/extensions.json +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/.vscode/settings.json +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/AUTHORS.rst +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/LICENSE +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/docs/Makefile +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/docs/conf.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/docs/index.rst +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/docs/make.bat +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/docs/requirements.txt +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/docs/user/install.rst +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/pyproject.toml +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/__helper__.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/__init__.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/__version__.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/base.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/fields.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/model.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/pydantic.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/redis_om.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling/sql.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling.egg-info/dependency_links.txt +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling.egg-info/requires.txt +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/querymodelling.egg-info/top_level.txt +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/setup.cfg +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/setup.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/tests/__init__.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/tests/__main__.py +0 -0
- {querymodelling-0.0.16 → querymodelling-0.0.17}/tests/basic_test.py +0 -0
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
Release History
|
|
2
2
|
===============
|
|
3
3
|
|
|
4
|
+
Unreleased
|
|
5
|
+
----------
|
|
6
|
+
|
|
7
|
+
- sql_filter: add typed, nested and generator based SQL filter models
|
|
8
|
+
(``SqlFilterModel``) with ``and`` / ``or`` / ``not``, typed operators,
|
|
9
|
+
SQL functions and JSON schema support
|
|
10
|
+
|
|
4
11
|
0.0.12 (2025-10-27)
|
|
5
12
|
|
|
6
13
|
- sql: add support for filtering boolean fields
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: querymodelling
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.17
|
|
4
4
|
Summary: build consistent api query models for fastapi
|
|
5
5
|
Author: George Haddad
|
|
6
6
|
Author-email: George Haddad <georgeh87@live.de>
|
|
@@ -224,3 +224,84 @@ At least you will need to have the pydantic package installed. Of course it make
|
|
|
224
224
|
|
|
225
225
|
## Example
|
|
226
226
|
|
|
227
|
+
### Typed SQL filter models
|
|
228
|
+
|
|
229
|
+
`SqlFilterModel` generates a nested, fully typed filter model (incl. JSON
|
|
230
|
+
schema) from a model and compiles it into a SQLAlchemy where clause.
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from sqlmodel import SQLModel, Field, select
|
|
234
|
+
from querymodelling.sql_filter import SqlFilterModel
|
|
235
|
+
|
|
236
|
+
class ExampleModel(SQLModel, table=True):
|
|
237
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
238
|
+
name: str
|
|
239
|
+
description: str
|
|
240
|
+
|
|
241
|
+
ExampleFilter = SqlFilterModel(ExampleModel)
|
|
242
|
+
|
|
243
|
+
query = ExampleFilter.model_validate({
|
|
244
|
+
"and": {
|
|
245
|
+
"name": {"contains": "wurst", "startswith": "bump"},
|
|
246
|
+
"description": {"lower": {"contains": "test"}},
|
|
247
|
+
},
|
|
248
|
+
"or": [{"id": {"in": [1, 2]}}, {"name": {"length": {"gt": 3}}}],
|
|
249
|
+
})
|
|
250
|
+
statement = query.apply_sql(select(ExampleModel))
|
|
251
|
+
# or: select(ExampleModel).where(query.to_sql_expression())
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
* Fields of the filter object are combined with `AND`.
|
|
255
|
+
* `and` / `or` accept an object (its conditions are combined) or a list of
|
|
256
|
+
objects (each object is combined with `AND`, the list with the operator).
|
|
257
|
+
`not` negates an object. Everything can be nested.
|
|
258
|
+
* Operators per type (default): `eq`, `ne`, `is_null`, `in`, `not_in`
|
|
259
|
+
(scalars), `gt`, `gte`, `lt`, `lte`, `between` (numbers, dates, times),
|
|
260
|
+
`contains`, `icontains`, `startswith`, `istartswith`, `endswith`,
|
|
261
|
+
`iendswith`, `like`, `not_like`, `ilike` (strings).
|
|
262
|
+
* SQL functions per type (default): `lower`, `upper`, `trim`, `length`
|
|
263
|
+
(strings), `abs` (numbers), `date`, `year`, `month`, `day`, `hour`,
|
|
264
|
+
`minute` (dates / times). Functions return a filter of their result type,
|
|
265
|
+
e.g. `{"name": {"length": {"gt": 3}}}`.
|
|
266
|
+
|
|
267
|
+
Everything is built from generators and can be extended or restricted:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
from querymodelling.sql_filter import (
|
|
271
|
+
SqlOperator, SqlFunction, default_sql_filter_mapping, for_types, without
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
def parity(annotation):
|
|
275
|
+
yield SqlOperator(
|
|
276
|
+
"even", lambda column, value: (column % 2 == 0) == value,
|
|
277
|
+
value_type=lambda annotation: bool)
|
|
278
|
+
|
|
279
|
+
mapping = default_sql_filter_mapping(name_prefix="Custom")
|
|
280
|
+
mapping.operator_generators = [
|
|
281
|
+
without(generator, "like", "ilike")
|
|
282
|
+
for generator in mapping.operator_generators
|
|
283
|
+
]
|
|
284
|
+
mapping.add_operators(for_types(parity, int, exclude=(bool,)))
|
|
285
|
+
|
|
286
|
+
ExampleFilter = SqlFilterModel(ExampleModel, mapping=mapping)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
* operator generators `(annotation) -> Iterable[SqlOperator]`
|
|
290
|
+
* function generators `(annotation) -> Iterable[SqlFunction]`
|
|
291
|
+
* field generators `(context, model, name, field_info, annotation) ->
|
|
292
|
+
Iterable[SqlFilterField]` (`field_generators=...`, e.g. virtual fields
|
|
293
|
+
based on SQL expressions)
|
|
294
|
+
* logical generators `(model) -> Iterable[SqlLogicalOperator]`
|
|
295
|
+
(`logical_generators=...`)
|
|
296
|
+
|
|
297
|
+
The filter can be used within existing query models as well:
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
from querymodelling.pydantic import QueryField
|
|
301
|
+
from querymodelling.sql_filter import sql_filter_expression
|
|
302
|
+
|
|
303
|
+
class ExampleQuery(PageQuery, BaseModel):
|
|
304
|
+
filter: ExampleFilter | None = QueryField(
|
|
305
|
+
sql_filter_expression, default=None)
|
|
306
|
+
```
|
|
307
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# querymodelling
|
|
2
|
+
build consistent api query models for fastapi
|
|
3
|
+
|
|
4
|
+
## Requirements
|
|
5
|
+
At least you will need to have the pydantic package installed. Of course it make sense to have some kind of service supporting query models like fast api. Currently sql models are implemented - but feel free to create new ones.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
## Example
|
|
10
|
+
|
|
11
|
+
### Typed SQL filter models
|
|
12
|
+
|
|
13
|
+
`SqlFilterModel` generates a nested, fully typed filter model (incl. JSON
|
|
14
|
+
schema) from a model and compiles it into a SQLAlchemy where clause.
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from sqlmodel import SQLModel, Field, select
|
|
18
|
+
from querymodelling.sql_filter import SqlFilterModel
|
|
19
|
+
|
|
20
|
+
class ExampleModel(SQLModel, table=True):
|
|
21
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
22
|
+
name: str
|
|
23
|
+
description: str
|
|
24
|
+
|
|
25
|
+
ExampleFilter = SqlFilterModel(ExampleModel)
|
|
26
|
+
|
|
27
|
+
query = ExampleFilter.model_validate({
|
|
28
|
+
"and": {
|
|
29
|
+
"name": {"contains": "wurst", "startswith": "bump"},
|
|
30
|
+
"description": {"lower": {"contains": "test"}},
|
|
31
|
+
},
|
|
32
|
+
"or": [{"id": {"in": [1, 2]}}, {"name": {"length": {"gt": 3}}}],
|
|
33
|
+
})
|
|
34
|
+
statement = query.apply_sql(select(ExampleModel))
|
|
35
|
+
# or: select(ExampleModel).where(query.to_sql_expression())
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
* Fields of the filter object are combined with `AND`.
|
|
39
|
+
* `and` / `or` accept an object (its conditions are combined) or a list of
|
|
40
|
+
objects (each object is combined with `AND`, the list with the operator).
|
|
41
|
+
`not` negates an object. Everything can be nested.
|
|
42
|
+
* Operators per type (default): `eq`, `ne`, `is_null`, `in`, `not_in`
|
|
43
|
+
(scalars), `gt`, `gte`, `lt`, `lte`, `between` (numbers, dates, times),
|
|
44
|
+
`contains`, `icontains`, `startswith`, `istartswith`, `endswith`,
|
|
45
|
+
`iendswith`, `like`, `not_like`, `ilike` (strings).
|
|
46
|
+
* SQL functions per type (default): `lower`, `upper`, `trim`, `length`
|
|
47
|
+
(strings), `abs` (numbers), `date`, `year`, `month`, `day`, `hour`,
|
|
48
|
+
`minute` (dates / times). Functions return a filter of their result type,
|
|
49
|
+
e.g. `{"name": {"length": {"gt": 3}}}`.
|
|
50
|
+
|
|
51
|
+
Everything is built from generators and can be extended or restricted:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from querymodelling.sql_filter import (
|
|
55
|
+
SqlOperator, SqlFunction, default_sql_filter_mapping, for_types, without
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def parity(annotation):
|
|
59
|
+
yield SqlOperator(
|
|
60
|
+
"even", lambda column, value: (column % 2 == 0) == value,
|
|
61
|
+
value_type=lambda annotation: bool)
|
|
62
|
+
|
|
63
|
+
mapping = default_sql_filter_mapping(name_prefix="Custom")
|
|
64
|
+
mapping.operator_generators = [
|
|
65
|
+
without(generator, "like", "ilike")
|
|
66
|
+
for generator in mapping.operator_generators
|
|
67
|
+
]
|
|
68
|
+
mapping.add_operators(for_types(parity, int, exclude=(bool,)))
|
|
69
|
+
|
|
70
|
+
ExampleFilter = SqlFilterModel(ExampleModel, mapping=mapping)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
* operator generators `(annotation) -> Iterable[SqlOperator]`
|
|
74
|
+
* function generators `(annotation) -> Iterable[SqlFunction]`
|
|
75
|
+
* field generators `(context, model, name, field_info, annotation) ->
|
|
76
|
+
Iterable[SqlFilterField]` (`field_generators=...`, e.g. virtual fields
|
|
77
|
+
based on SQL expressions)
|
|
78
|
+
* logical generators `(model) -> Iterable[SqlLogicalOperator]`
|
|
79
|
+
(`logical_generators=...`)
|
|
80
|
+
|
|
81
|
+
The filter can be used within existing query models as well:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from querymodelling.pydantic import QueryField
|
|
85
|
+
from querymodelling.sql_filter import sql_filter_expression
|
|
86
|
+
|
|
87
|
+
class ExampleQuery(PageQuery, BaseModel):
|
|
88
|
+
filter: ExampleFilter | None = QueryField(
|
|
89
|
+
sql_filter_expression, default=None)
|
|
90
|
+
```
|
|
91
|
+
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
21
|
+
__version__ = version = '0.0.17'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 17)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g9261b3e64'
|