Skip to content

Commit 651172e

Browse files
committed
updated to many update logic
1 parent 193aaba commit 651172e

File tree

4 files changed

+138
-62
lines changed

4 files changed

+138
-62
lines changed

fastapi_jsonapi/data_layers/sqla_orm.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,14 @@ async def apply_relationships(self, obj: TypeModel, data_create: BaseJSONAPIItem
173173

174174
if relationship_info.many:
175175
assert isinstance(relationship_in, BaseJSONAPIRelationshipDataToManySchema)
176-
related_data = await self.get_related_objects_list(
177-
related_model=related_model,
178-
related_id_field=relationship_info.id_field_name,
179-
ids=[r.id for r in relationship_in.data],
180-
)
176+
177+
related_data = []
178+
if relationship_in.data:
179+
related_data = await self.get_related_objects_list(
180+
related_model=related_model,
181+
related_id_field=relationship_info.id_field_name,
182+
ids=[r.id for r in relationship_in.data],
183+
)
181184
else:
182185
assert isinstance(relationship_in, BaseJSONAPIRelationshipDataToOneSchema)
183186

tests/models.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,13 @@ class SelfRelationship(Base):
305305
),
306306
nullable=True,
307307
)
308-
# parent = relationship("SelfRelationship", back_populates="s")
309-
self_relationship = relationship("SelfRelationship", remote_side=[id])
308+
children_objects = relationship(
309+
"SelfRelationship",
310+
backref=backref("parent_object", remote_side=[id]),
311+
)
312+
313+
if TYPE_CHECKING:
314+
parent_object: Optional["SelfRelationship"]
310315

311316

312317
class ContainsTimestamp(Base):

tests/schemas.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,11 +408,17 @@ class Config:
408408

409409

410410
class SelfRelationshipSchema(SelfRelationshipAttributesSchema):
411-
self_relationship: Optional["SelfRelationshipSchema"] = Field(
411+
parent_object: Optional["SelfRelationshipSchema"] = Field(
412412
relationship=RelationshipInfo(
413413
resource_type="self_relationship",
414414
),
415415
)
416+
children_objects: Optional[list["SelfRelationshipSchema"]] = Field(
417+
relationship=RelationshipInfo(
418+
resource_type="self_relatiosnhip",
419+
many=True,
420+
),
421+
)
416422

417423

418424
class CascadeCaseSchema(BaseModel):

tests/test_api/test_api_sqla_with_includes.py

Lines changed: 116 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,7 +1438,7 @@ async def test_create_with_relationship_to_the_same_table(self):
14381438
"name": "child",
14391439
},
14401440
"relationships": {
1441-
"self_relationship": {
1441+
"parent_object": {
14421442
"data": {
14431443
"type": resource_type,
14441444
"id": parent_object_id,
@@ -1447,7 +1447,7 @@ async def test_create_with_relationship_to_the_same_table(self):
14471447
},
14481448
},
14491449
}
1450-
url = f"{url}?include=self_relationship"
1450+
url = f"{url}?include=parent_object"
14511451
res = await client.post(url, json=create_with_relationship_body)
14521452
assert res.status_code == status.HTTP_201_CREATED, res.text
14531453

@@ -1459,7 +1459,7 @@ async def test_create_with_relationship_to_the_same_table(self):
14591459
"attributes": {"name": "child"},
14601460
"id": child_object_id,
14611461
"relationships": {
1462-
"self_relationship": {
1462+
"parent_object": {
14631463
"data": {
14641464
"id": parent_object_id,
14651465
"type": "self_relationship",
@@ -1830,57 +1830,6 @@ async def test_update_to_many_relationships(self, async_session: AsyncSession, c
18301830
],
18311831
}
18321832

1833-
async def test_remove_to_one_relationship_using_by_update(self, async_session: AsyncSession):
1834-
resource_type = "self_relationship"
1835-
app = build_app_custom(
1836-
model=SelfRelationship,
1837-
schema=SelfRelationshipSchema,
1838-
resource_type=resource_type,
1839-
)
1840-
1841-
parent_obj = SelfRelationship(name=fake.name())
1842-
child_obj = SelfRelationship(name=fake.name(), self_relationship=parent_obj)
1843-
async_session.add_all([parent_obj, child_obj])
1844-
await async_session.commit()
1845-
1846-
assert child_obj.self_relationship_id == parent_obj.id
1847-
1848-
async with AsyncClient(app=app, base_url="http://test") as client:
1849-
expected_name = fake.name()
1850-
update_body = {
1851-
"data": {
1852-
"id": str(child_obj.id),
1853-
"attributes": {
1854-
"name": expected_name,
1855-
},
1856-
"relationships": {
1857-
"self_relationship": {
1858-
"data": None,
1859-
},
1860-
},
1861-
},
1862-
}
1863-
params = {
1864-
"include": "self_relationship",
1865-
}
1866-
url = app.url_path_for(f"update_{resource_type}_detail", obj_id=child_obj.id)
1867-
res = await client.patch(url, params=params, json=update_body)
1868-
assert res.status_code == status.HTTP_200_OK, res.text
1869-
assert res.json() == {
1870-
"data": {
1871-
"attributes": SelfRelationshipAttributesSchema(name=expected_name).dict(),
1872-
"id": str(child_obj.id),
1873-
"relationships": {"self_relationship": {"data": None}},
1874-
"type": "self_relationship",
1875-
},
1876-
"included": [],
1877-
"jsonapi": {"version": "1.0"},
1878-
"meta": None,
1879-
}
1880-
1881-
await async_session.refresh(child_obj)
1882-
assert child_obj.self_relationship_id is None
1883-
18841833

18851834
class TestPatchObjectRelationshipsToOne:
18861835
async def test_ok_when_foreign_key_of_related_object_is_nullable(
@@ -2094,6 +2043,60 @@ async def test_update_resource_error_same_id(
20942043
],
20952044
}
20962045

2046+
async def test_remove_to_one_relationship_using_by_update(self, async_session: AsyncSession):
2047+
resource_type = "self_relationship"
2048+
with suppress(KeyError):
2049+
RoutersJSONAPI.all_jsonapi_routers.pop(resource_type)
2050+
2051+
app = build_app_custom(
2052+
model=SelfRelationship,
2053+
schema=SelfRelationshipSchema,
2054+
resource_type=resource_type,
2055+
)
2056+
2057+
parent_obj = SelfRelationship(name=fake.name())
2058+
child_obj = SelfRelationship(name=fake.name(), parent_object=parent_obj)
2059+
async_session.add_all([parent_obj, child_obj])
2060+
await async_session.commit()
2061+
2062+
assert child_obj.self_relationship_id == parent_obj.id
2063+
2064+
async with AsyncClient(app=app, base_url="http://test") as client:
2065+
expected_name = fake.name()
2066+
update_body = {
2067+
"data": {
2068+
"id": str(child_obj.id),
2069+
"attributes": {
2070+
"name": expected_name,
2071+
},
2072+
"relationships": {
2073+
"parent_object": {
2074+
"data": None,
2075+
},
2076+
},
2077+
},
2078+
}
2079+
params = {
2080+
"include": "parent_object",
2081+
}
2082+
url = app.url_path_for(f"update_{resource_type}_detail", obj_id=child_obj.id)
2083+
res = await client.patch(url, params=params, json=update_body)
2084+
assert res.status_code == status.HTTP_200_OK, res.text
2085+
assert res.json() == {
2086+
"data": {
2087+
"attributes": SelfRelationshipAttributesSchema(name=expected_name).dict(),
2088+
"id": str(child_obj.id),
2089+
"relationships": {"parent_object": {"data": None}},
2090+
"type": "self_relationship",
2091+
},
2092+
"included": [],
2093+
"jsonapi": {"version": "1.0"},
2094+
"meta": None,
2095+
}
2096+
2097+
await async_session.refresh(child_obj)
2098+
assert child_obj.self_relationship_id is None
2099+
20972100

20982101
class TestPatchRelationshipsToMany:
20992102
async def test_ok(
@@ -2269,6 +2272,65 @@ async def test_relationship_not_found(
22692272
],
22702273
}
22712274

2275+
async def test_remove_to_many_relationship_using_by_update(self, async_session: AsyncSession):
2276+
resource_type = "self_relationship"
2277+
with suppress(KeyError):
2278+
RoutersJSONAPI.all_jsonapi_routers.pop(resource_type)
2279+
2280+
app = build_app_custom(
2281+
model=SelfRelationship,
2282+
schema=SelfRelationshipSchema,
2283+
resource_type=resource_type,
2284+
)
2285+
2286+
parent_obj = SelfRelationship(name=fake.name())
2287+
child_obj_1 = SelfRelationship(name=fake.name(), parent_object=parent_obj)
2288+
child_obj_2 = SelfRelationship(name=fake.name(), parent_object=parent_obj)
2289+
async_session.add_all([parent_obj, child_obj_1, child_obj_2])
2290+
await async_session.commit()
2291+
2292+
assert child_obj_1.self_relationship_id == parent_obj.id
2293+
assert child_obj_2.self_relationship_id == parent_obj.id
2294+
assert len(parent_obj.children_objects) == 2 # noqa PLR2004
2295+
2296+
async with AsyncClient(app=app, base_url="http://test") as client:
2297+
expected_name = fake.name()
2298+
update_body = {
2299+
"data": {
2300+
"id": str(parent_obj.id),
2301+
"attributes": {
2302+
"name": expected_name,
2303+
},
2304+
"relationships": {
2305+
"children_objects": {
2306+
"data": None,
2307+
},
2308+
},
2309+
},
2310+
}
2311+
params = {
2312+
"include": "children_objects",
2313+
}
2314+
url = app.url_path_for(f"update_{resource_type}_detail", obj_id=parent_obj.id)
2315+
res = await client.patch(url, params=params, json=update_body)
2316+
assert res.status_code == status.HTTP_200_OK, res.text
2317+
assert res.json() == {
2318+
"data": {
2319+
"attributes": SelfRelationshipAttributesSchema(name=expected_name).dict(),
2320+
"id": str(parent_obj.id),
2321+
"relationships": {"children_objects": {"data": []}},
2322+
"type": "self_relationship",
2323+
},
2324+
"included": [],
2325+
"jsonapi": {"version": "1.0"},
2326+
"meta": None,
2327+
}
2328+
2329+
await async_session.refresh(child_obj_1)
2330+
await async_session.refresh(child_obj_2)
2331+
assert child_obj_1.self_relationship_id is None
2332+
assert child_obj_2.self_relationship_id is None
2333+
22722334

22732335
class TestDeleteObjects:
22742336
async def test_delete_object_and_fetch_404(

0 commit comments

Comments
 (0)