Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add between as a usable operator #551

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/fastapi_filter_sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ class UserFilter(Filter):

See: https://github.com/tiangolo/fastapi/issues/4700 for why we need to wrap `Query` in `Field`.
"""
age__in: Optional[List[int]] = None
age__between: Optional[List[List[int]]] = None
order_by: List[str] = ["age"]
search: Optional[str] = None

Expand Down
24 changes: 13 additions & 11 deletions fastapi_filter/contrib/sqlalchemy/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def _backward_compatible_value_for_like_and_ilike(value: str):
"isnull": lambda value: ("is_", None) if value is True else ("is_not", None),
"lt": lambda value: ("__lt__", value),
"lte": lambda value: ("__le__", value),
"between": lambda value: ("between", (value[0], value[1])),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be beneficial to enhance protection against IndexError. For instance, if a client passes __between=21, instead of between=21,33.

"like": lambda value: ("like", _backward_compatible_value_for_like_and_ilike(value)),
"ilike": lambda value: ("ilike", _backward_compatible_value_for_like_and_ilike(value)),
# XXX(arthurio): Mysql excludes None values when using `in` or `not in` filters.
Expand Down Expand Up @@ -89,19 +90,17 @@ class Direction(str, Enum):

@field_validator("*", mode="before")
def split_str(cls, value, field: ValidationInfo):
if (
field.field_name is not None
and (
if field.field_name is not None:
if (
field.field_name == cls.Constants.ordering_field_name
or field.field_name.endswith("__in")
or field.field_name.endswith("__not_in")
)
and isinstance(value, str)
):
if not value:
# Empty string should return [] not ['']
return []
return list(value.split(","))
or field.field_name.endswith("__between")
) and isinstance(value, str):
if not value:
# Empty string should return [] not ['']
return []
return list(value.split(","))
return value

def filter(self, query: Union[Query, Select]):
Expand All @@ -124,7 +123,10 @@ def filter(self, query: Union[Query, Select]):
query = query.filter(or_(*search_filters))
else:
model_field = getattr(self.Constants.model, field_name)
query = query.filter(getattr(model_field, operator)(value))
if isinstance(value, tuple):
query = query.filter(getattr(model_field, operator)(*value))
else:
query = query.filter(getattr(model_field, operator)(value))

return query

Expand Down
1 change: 1 addition & 0 deletions tests/test_sqlalchemy/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ class UserFilter(Filter): # type: ignore[misc, valid-type]
age__gt: Optional[int] = None
age__gte: Optional[int] = None
age__in: Optional[List[int]] = None
age__between: Optional[List[int]] = None
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type annotation here and in examples/fastapi_filter_sqlalchemy.py:105 (List[List[int]]) differs. Is this a mistake?

address: Optional[AddressFilter] = FilterDepends( # type: ignore[valid-type]
with_prefix("address", AddressFilter), by_alias=True
)
Expand Down
2 changes: 2 additions & 0 deletions tests/test_sqlalchemy/test_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
[{"address": {"country__not_in": ["France"]}}, 3],
[{"age__in": "1"}, 1],
[{"age__in": "21,33"}, 3],
[{"age__between": [21, 33]}, 3],
[{"age__between": "21,33"}, 3],
[{"address": {"country__not_in": "France"}}, 3],
[{"address": {"street__isnull": True}}, 2],
[{"address": {"city__in": ["Nantes", "Denver"]}}, 3],
Expand Down
Loading