Skip to content

Commit b6a5d09

Browse files
CosmoVmahenzon
authored andcommitted
updated patch logic and refactor
1 parent c9cf63f commit b6a5d09

4 files changed

Lines changed: 82 additions & 44 deletions

File tree

fastapi_jsonapi/data_layers/sqla_orm.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -319,15 +319,14 @@ async def update_object(
319319
has_updated = False
320320
for field_name, new_value in new_data.items():
321321
# TODO: get field alias (if present) and get attribute by alias (rarely used, but required)
322-
try:
323-
old_value = getattr(obj, field_name)
324-
325-
if old_value != new_value:
326-
setattr(obj, field_name, new_value)
327-
has_updated = True
328-
except AttributeError:
329-
err_message = f'Can\'t find an attribute "{field_name}" in model {self.model.__name__}'
330-
logging.warning(err_message)
322+
missing = object()
323+
324+
if (old_value := getattr(obj, field_name, missing)) is missing:
325+
continue
326+
327+
if old_value != new_value:
328+
setattr(obj, field_name, new_value)
329+
has_updated = True
331330
try:
332331
await self.session.commit()
333332
except DBAPIError as e:

fastapi_jsonapi/querystring.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,18 +150,15 @@ def filters(self) -> List[dict]:
150150
if filters is not None:
151151
try:
152152
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-
161-
results.extend(json.loads(filters))
162153
except (ValueError, TypeError):
163154
msg = "Parse error"
164155
raise InvalidFilters(msg)
156+
157+
if not isinstance(loaded_filters, list):
158+
msg = f"Incorrect filters format, expected list of conditions but got {type(loaded_filters).__name__}"
159+
raise InvalidFilters(msg)
160+
161+
results.extend(loaded_filters)
165162
if self._get_key_values("filter["):
166163
results.extend(self._simple_filters(self._get_key_values("filter[")))
167164
return results

fastapi_jsonapi/utils/sqla.py

Lines changed: 2 additions & 2 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, TypeSchema
3+
from fastapi_jsonapi.data_typing import TypeModel
44

55

6-
def get_related_model_cls(cls: TypeSchema, relation_name: str) -> Type[TypeModel]:
6+
def get_related_model_cls(cls: Type[TypeModel], relation_name: str) -> Type[TypeModel]:
77
"""
88
99
SQLA Get related model class

tests/test_api/test_api_sqla_with_includes.py

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,34 @@ def association_key(data: dict):
4646
return data["type"], data["id"]
4747

4848

49+
def build_app_custom(
50+
schema,
51+
schema_in_patch,
52+
schema_in_post,
53+
model,
54+
resource_type: str = "misc",
55+
) -> FastAPI:
56+
router: APIRouter = APIRouter()
57+
58+
RoutersJSONAPI(
59+
router=router,
60+
path="/misc",
61+
tags=["Misc"],
62+
class_detail=DetailViewBaseGeneric,
63+
class_list=ListViewBaseGeneric,
64+
schema=schema,
65+
resource_type=resource_type,
66+
schema_in_patch=schema_in_patch,
67+
schema_in_post=schema_in_post,
68+
model=model,
69+
)
70+
71+
app = build_app_plain()
72+
app.include_router(router, prefix="")
73+
74+
return app
75+
76+
4977
async def test_root(client: AsyncClient):
5078
response = await client.get("/docs")
5179
assert response.status_code == status.HTTP_200_OK
@@ -891,31 +919,11 @@ async def test_create_user_and_fetch_data(self, app: FastAPI, client: AsyncClien
891919
assert response_data["data"]["attributes"] == create_user_body["data"]["attributes"]
892920
assert response_data["data"]["id"] == user_id
893921

894-
def _build_app_custom(self, schema, schema_in_post, model, resource_type: str = "misc") -> FastAPI:
895-
router: APIRouter = APIRouter()
896-
897-
RoutersJSONAPI(
898-
router=router,
899-
path="/misc",
900-
tags=["Misc"],
901-
class_detail=DetailViewBaseGeneric,
902-
class_list=ListViewBaseGeneric,
903-
schema=schema,
904-
resource_type=resource_type,
905-
schema_in_patch=UserPatchSchema,
906-
schema_in_post=schema_in_post,
907-
model=model,
908-
)
909-
910-
app = build_app_plain()
911-
app.include_router(router, prefix="")
912-
913-
return app
914-
915922
async def test_create_id_by_client(self):
916923
resource_type = "user"
917-
app = self._build_app_custom(
924+
app = build_app_custom(
918925
UserSchema,
926+
UserPatchSchema,
919927
UserInSchemaAllowIdOnPost,
920928
User,
921929
resource_type=resource_type,
@@ -949,7 +957,7 @@ async def test_create_id_by_client(self):
949957
}
950958

951959
async def test_create_id_by_client_uuid_type(self):
952-
app = self._build_app_custom(IdCastSchema, IdCastSchema, IdCast)
960+
app = build_app_custom(IdCastSchema, IdCastSchema, IdCastSchema, IdCast)
953961

954962
new_id = str(uuid4())
955963
create_body = {
@@ -975,7 +983,8 @@ async def test_create_id_by_client_uuid_type(self):
975983

976984
async def test_create_with_relationship_to_the_same_table(self):
977985
resource_type = "self_relationship"
978-
app = self._build_app_custom(
986+
app = build_app_custom(
987+
SelfRelationshipSchema,
979988
SelfRelationshipSchema,
980989
SelfRelationshipSchema,
981990
SelfRelationship,
@@ -1090,6 +1099,39 @@ async def test_patch_object(
10901099
"meta": None,
10911100
}
10921101

1102+
async def test_do_nothing_with_field_not_presented_in_model(
1103+
self,
1104+
user_1: User,
1105+
):
1106+
class UserPatchSchemaWithExtraAttribute(UserPatchSchema):
1107+
attr_which_is_not_presented_in_model: str
1108+
1109+
resource_type = "user"
1110+
app = build_app_custom(
1111+
UserSchema,
1112+
UserPatchSchemaWithExtraAttribute,
1113+
UserPatchSchemaWithExtraAttribute,
1114+
User,
1115+
resource_type=resource_type,
1116+
)
1117+
new_attrs = UserPatchSchemaWithExtraAttribute(
1118+
name=fake.name(),
1119+
age=fake.pyint(),
1120+
email=fake.email(),
1121+
attr_which_is_not_presented_in_model=fake.name(),
1122+
).dict()
1123+
1124+
patch_user_body = {
1125+
"data": {
1126+
"id": user_1.id,
1127+
"attributes": new_attrs,
1128+
},
1129+
}
1130+
async with AsyncClient(app=app, base_url="http://test") as client:
1131+
url = app.url_path_for(f"update_{resource_type}_detail", obj_id=user_1.id)
1132+
res = await client.patch(url, json=patch_user_body)
1133+
assert res.status_code == status.HTTP_200_OK, res.text
1134+
10931135

10941136
class TestPatchObjectRelationshipsToOne:
10951137
async def test_ok_when_foreign_key_of_related_object_is_nullable(

0 commit comments

Comments
 (0)