Skip to content

Commit c9cf63f

Browse files
CosmoVmahenzon
authored andcommitted
added test case for nested query filter and fix
1 parent 942698b commit c9cf63f

5 files changed

Lines changed: 122 additions & 30 deletions

File tree

docs/filtering.rst

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -81,31 +81,29 @@ You can also use boolean combination of operations:
8181
"val":"%admin%"
8282
},
8383
{
84-
"or": {
85-
[
86-
{
87-
"not": {
84+
"or": [
85+
{
86+
"not": {
87+
"name": "first_name",
88+
"op": "eq",
89+
"val": "John"
90+
}
91+
},
92+
{
93+
"and": [
94+
{
8895
"name": "first_name",
89-
"op": "eq",
90-
"val":"John"
96+
"op": "like",
97+
"val": "%Jim%"
98+
},
99+
{
100+
"name": "date_create",
101+
"op": "gt",
102+
"val": "1990-01-01"
91103
}
92-
},
93-
{
94-
"and": [
95-
{
96-
"name": "first_name",
97-
"op": "like",
98-
"val": "%Jim%"
99-
},
100-
{
101-
"name": "date_create",
102-
"op": "gt",
103-
"val": "1990-01-01"
104-
}
105-
]
106-
}
107-
]
108-
}
104+
]
105+
}
106+
]
109107
}
110108
] HTTP/1.1
111109
Accept: application/vnd.api+json

fastapi_jsonapi/data_layers/sqla_orm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ async def apply_relationships(self, obj: TypeModel, data_create: BaseJSONAPIItem
140140
relationship_info: RelationshipInfo = field.field_info.extra["relationship"]
141141

142142
# ...
143-
related_model = get_related_model_cls(obj, relation_name)
143+
related_model = get_related_model_cls(type(obj), relation_name)
144144

145145
if relationship_info.many:
146146
assert isinstance(relationship_in, BaseJSONAPIRelationshipDataToManySchema)

fastapi_jsonapi/querystring.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ def filters(self) -> List[dict]:
149149
filters = self.qs.get("filter")
150150
if filters is not None:
151151
try:
152+
loaded_filters = json.loads(filters)
153+
154+
if not isinstance(loaded_filters, list):
155+
msg = (
156+
"Incorrect filters format, expected list of "
157+
f"conditions but got {type(loaded_filters).__name__}"
158+
)
159+
raise InvalidFilters(msg)
160+
152161
results.extend(json.loads(filters))
153162
except (ValueError, TypeError):
154163
msg = "Parse error"

fastapi_jsonapi/utils/sqla.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from typing import Type
22

3-
from fastapi_jsonapi.data_typing import TypeModel
3+
from fastapi_jsonapi.data_typing import TypeModel, TypeSchema
44

55

6-
def get_related_model_cls(obj: TypeModel, relation_name: str) -> Type[TypeModel]:
6+
def get_related_model_cls(cls: TypeSchema, relation_name: str) -> Type[TypeModel]:
77
"""
88
99
SQLA Get related model class
@@ -18,8 +18,8 @@ class Computer(sqla_base):
1818
class ComputerSchema(pydantic_base):
1919
owner = Field(alias="user", relationship=...)
2020
21-
:param obj:
21+
:param cls:
2222
:param relation_name:
2323
:return:
2424
"""
25-
return getattr(obj.__class__, relation_name).property.mapper.class_
25+
return getattr(cls, relation_name).property.mapper.class_

tests/test_api/test_api_sqla_with_includes.py

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
from fastapi import APIRouter, FastAPI, status
88
from httpx import AsyncClient
99
from pytest import mark, param # noqa PT013
10+
from sqlalchemy.ext.asyncio import AsyncSession
1011

1112
from fastapi_jsonapi import RoutersJSONAPI
1213
from fastapi_jsonapi.views.view_base import ViewBase
1314
from tests.fixtures.app import build_app_plain
14-
from tests.fixtures.entities import create_user
15+
from tests.fixtures.entities import build_workplace, create_user
1516
from tests.fixtures.views import DetailViewBaseGeneric, ListViewBaseGeneric
1617
from tests.misc.utils import fake
1718
from tests.models import (
@@ -1799,6 +1800,90 @@ async def test_composite_filter_with_mutually_exclusive_conditions(
17991800
"meta": {"count": 0, "totalPages": 1},
18001801
}
18011802

1803+
async def test_filter_with_nested_conditions(
1804+
self,
1805+
app: FastAPI,
1806+
async_session: AsyncSession,
1807+
client: AsyncClient,
1808+
):
1809+
workplace_name = "Common workplace name"
1810+
1811+
workplace_1, workplace_2, workplace_3, workplace_4 = (
1812+
await build_workplace(async_session, name=workplace_name),
1813+
await build_workplace(async_session, name=workplace_name),
1814+
await build_workplace(async_session, name=workplace_name),
1815+
await build_workplace(async_session, name=workplace_name),
1816+
)
1817+
1818+
user_1, user_2, _, user_4 = (
1819+
await create_user(async_session, name="John Doe", age=20, workplace=workplace_1),
1820+
await create_user(async_session, name="Jane Doe", age=25, workplace=workplace_2),
1821+
await create_user(async_session, name="Jonny Doe", age=30, workplace=workplace_3),
1822+
await create_user(async_session, name="Mary Jane", age=21, workplace=workplace_4),
1823+
)
1824+
1825+
params = {
1826+
"filter": dumps(
1827+
[
1828+
{
1829+
"name": "workplace.name",
1830+
"op": "eq",
1831+
"val": workplace_name,
1832+
},
1833+
{
1834+
"or": [
1835+
{
1836+
"not": {
1837+
"name": "name",
1838+
"op": "ne",
1839+
"val": "Mary Jane",
1840+
},
1841+
},
1842+
{
1843+
"and": [
1844+
{
1845+
"name": "name",
1846+
"op": "like",
1847+
"val": "%Doe%",
1848+
},
1849+
{
1850+
"name": "age",
1851+
"op": "lt",
1852+
"val": 30,
1853+
},
1854+
],
1855+
},
1856+
],
1857+
},
1858+
],
1859+
),
1860+
}
1861+
1862+
url = app.url_path_for("get_user_list")
1863+
res = await client.get(url, params=params)
1864+
assert res.status_code == status.HTTP_200_OK, res.text
1865+
assert res.json() == {
1866+
"data": [
1867+
{
1868+
"attributes": UserAttributesBaseSchema.from_orm(user_1),
1869+
"id": str(user_1.id),
1870+
"type": "user",
1871+
},
1872+
{
1873+
"attributes": UserAttributesBaseSchema.from_orm(user_2),
1874+
"id": str(user_2.id),
1875+
"type": "user",
1876+
},
1877+
{
1878+
"attributes": UserAttributesBaseSchema.from_orm(user_4),
1879+
"id": str(user_4.id),
1880+
"type": "user",
1881+
},
1882+
],
1883+
"jsonapi": {"version": "1.0"},
1884+
"meta": {"count": 3, "totalPages": 1},
1885+
}
1886+
18021887

18031888
ASCENDING = ""
18041889
DESCENDING = "-"
@@ -1819,7 +1904,7 @@ async def test_sort(
18191904
self,
18201905
app: FastAPI,
18211906
client: AsyncClient,
1822-
async_session,
1907+
async_session: AsyncSession,
18231908
order: str,
18241909
):
18251910
user_1, _, user_3 = (

0 commit comments

Comments
 (0)