Skip to content

Commit d60ba71

Browse files
authored
feature: allow sending exporting models parameters to to_binary_expressions and apply_filters methods (#23)
1 parent c385cf4 commit d60ba71

File tree

4 files changed

+115
-4
lines changed

4 files changed

+115
-4
lines changed

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,45 @@ query = query.filter(*binary_expression)
119119
query = query.apply_filters(query=query)
120120
```
121121

122+
Sometimes, it is necessary to manipulate sent data before applying filters.
123+
For example, a field should not be directly converted to a filter; instead, custom logic should be applied.
124+
As of version `0.1.3`, the `to_binary_expressions` and `apply_filters` methods accept the `export_params` argument to address this situation.
125+
Values mentioned in the Pydantic dictionary [export section](https://docs.pydantic.dev/1.10/usage/exporting_models/ ) can be sent as `export_params`.
126+
127+
```python
128+
import typing
129+
130+
class CustomBaseModel(SqlAlchemyFilterBaseModel):
131+
id__gte: int = None
132+
name__in: typing.List[str] = None
133+
filter_to_exclude: typing.Any = None
134+
135+
class ConverterConfig:
136+
model = SomeModel
137+
138+
139+
custom_dataclass = CustomBaseModel(
140+
id__gte=1,
141+
name__in=['abc', 'def'],
142+
filter_to_exclude="filter_value",
143+
)
144+
145+
# filter_to_exclude field will be excluded from converting basemodel to sqlalchemy filters
146+
147+
binary_expression = custom_dataclass.to_binary_expressions(
148+
export_params={'exclude': {'filter_to_exclude'}, }
149+
)
150+
151+
query = query.filter(*binary_expression)
152+
153+
# or
154+
155+
query = query.apply_filters(
156+
query=query,
157+
export_params={'exclude': {'filter_to_exclude'}, }
158+
)
159+
```
160+
122161
Order by:
123162
```python
124163
import typing

dataclass_sqlalchemy_mixins/pydantic_mixins/sqlalchemy_base_models.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,14 @@ def _to_dict(self, **kwargs):
5656
dict_values[dict_key] = list(map(expected_type, value))
5757
return dict_values
5858

59-
def to_binary_expressions(self):
60-
filters = self._to_dict(exclude_none=True)
59+
def to_binary_expressions(
60+
self,
61+
export_params=None,
62+
):
63+
if export_params is None:
64+
export_params = dict()
65+
66+
filters = self._to_dict(exclude_none=True, **export_params)
6167

6268
return self.get_binary_expressions(
6369
filters=filters,
@@ -66,8 +72,12 @@ def to_binary_expressions(self):
6672
def apply_filters(
6773
self,
6874
query,
75+
export_params=None,
6976
):
70-
filters = self._to_dict(exclude_none=True)
77+
if export_params is None:
78+
export_params = dict()
79+
80+
filters = self._to_dict(exclude_none=True, **export_params)
7181

7282
filters_binary_expressions = self.get_models_binary_expressions(
7383
filters=filters,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "dataclass-sqlalchemy-mixins"
3-
version = "0.1.2"
3+
version = "0.1.3"
44
description = "Allows to convert dataclasses to sqlalchemy filters and orderings."
55
authors = ["ViAchKoN"]
66
readme = "README.md"

tests/filtering/test_fields_filters.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,3 +631,65 @@ def test_filter__like_ilike__ok(
631631

632632
for expected_item, result in zip(expected_items, results):
633633
assert result.as_dict() == expected_item.as_dict()
634+
635+
636+
@pytest.mark.parametrize(
637+
"apply_filters",
638+
[
639+
True,
640+
False,
641+
],
642+
)
643+
def test_filter__eq__dict_kwargs__ok(
644+
db_session,
645+
get_sqlalchemy_filter_base_model,
646+
apply_filters,
647+
):
648+
expected_item_name = "expected_item_name"
649+
650+
# Create expected item
651+
expected_item = models_factory.ItemFactory.create(name=expected_item_name)
652+
653+
# Create unexpected items
654+
unexpected_items = models_factory.ItemFactory.create_batch(size=4)
655+
656+
unexpected_items_ids = [unexpected_item.id for unexpected_item in unexpected_items]
657+
658+
filters_model = get_sqlalchemy_filter_base_model(
659+
base_model=models.Item,
660+
field_kwargs={"name": (str, ...), "id__in": (tp.List[int], ...)},
661+
model_kwargs={
662+
"name": expected_item_name,
663+
"id__in": unexpected_items_ids,
664+
},
665+
)
666+
667+
assert db_session.query(models.Item).count() == 5
668+
669+
if apply_filters:
670+
query = select(models.Item)
671+
query = filters_model.apply_filters(
672+
query=query,
673+
export_params={
674+
"exclude": {"id__in"},
675+
},
676+
)
677+
results = db_session.execute(query).scalars().all()
678+
else:
679+
results = (
680+
db_session.query(models.Item)
681+
.filter(
682+
*filters_model.to_binary_expressions(
683+
export_params={
684+
"exclude": {"id__in"},
685+
}
686+
)
687+
)
688+
.all()
689+
)
690+
691+
assert len(results) == 1
692+
693+
result = results[0]
694+
695+
assert result.as_dict() == expected_item.as_dict()

0 commit comments

Comments
 (0)