Skip to content

Commit

Permalink
Merge pull request #82 from mts-ai/fix-update-relationship
Browse files Browse the repository at this point in the history
Fix update relationship
  • Loading branch information
mahenzon authored Apr 9, 2024
2 parents 39d995b + 651172e commit a5dfc0d
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 17 deletions.
27 changes: 17 additions & 10 deletions fastapi_jsonapi/data_layers/sqla_orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,19 +173,26 @@ async def apply_relationships(self, obj: TypeModel, data_create: BaseJSONAPIItem

if relationship_info.many:
assert isinstance(relationship_in, BaseJSONAPIRelationshipDataToManySchema)
related_data = await self.get_related_objects_list(
related_model=related_model,
related_id_field=relationship_info.id_field_name,
ids=[r.id for r in relationship_in.data],
)

related_data = []
if relationship_in.data:
related_data = await self.get_related_objects_list(
related_model=related_model,
related_id_field=relationship_info.id_field_name,
ids=[r.id for r in relationship_in.data],
)
else:
assert isinstance(relationship_in, BaseJSONAPIRelationshipDataToOneSchema)
related_data = await self.get_related_object(
related_model=related_model,
related_id_field=relationship_info.id_field_name,
id_value=relationship_in.data.id,
)

if relationship_in.data:
related_data = await self.get_related_object(
related_model=related_model,
related_id_field=relationship_info.id_field_name,
id_value=relationship_in.data.id,
)
else:
setattr(obj, relation_name, None)
continue
try:
hasattr(obj, relation_name)
except MissingGreenlet:
Expand Down
9 changes: 7 additions & 2 deletions tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,13 @@ class SelfRelationship(Base):
),
nullable=True,
)
# parent = relationship("SelfRelationship", back_populates="s")
self_relationship = relationship("SelfRelationship", remote_side=[id])
children_objects = relationship(
"SelfRelationship",
backref=backref("parent_object", remote_side=[id]),
)

if TYPE_CHECKING:
parent_object: Optional["SelfRelationship"]


class ContainsTimestamp(Base):
Expand Down
16 changes: 14 additions & 2 deletions tests/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,13 +400,25 @@ class CustomUUIDItemSchema(CustomUUIDItemAttributesSchema):
id: UUID = Field(client_can_set_id=True)


class SelfRelationshipSchema(BaseModel):
class SelfRelationshipAttributesSchema(BaseModel):
name: str
self_relationship: Optional["SelfRelationshipSchema"] = Field(

class Config:
orm_mode = True


class SelfRelationshipSchema(SelfRelationshipAttributesSchema):
parent_object: Optional["SelfRelationshipSchema"] = Field(
relationship=RelationshipInfo(
resource_type="self_relationship",
),
)
children_objects: Optional[list["SelfRelationshipSchema"]] = Field(
relationship=RelationshipInfo(
resource_type="self_relatiosnhip",
many=True,
),
)


class CascadeCaseSchema(BaseModel):
Expand Down
120 changes: 117 additions & 3 deletions tests/test_api/test_api_sqla_with_includes.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
CustomUUIDItemAttributesSchema,
PostAttributesBaseSchema,
PostCommentAttributesBaseSchema,
SelfRelationshipAttributesSchema,
SelfRelationshipSchema,
UserAttributesBaseSchema,
UserBioAttributesBaseSchema,
Expand Down Expand Up @@ -1437,7 +1438,7 @@ async def test_create_with_relationship_to_the_same_table(self):
"name": "child",
},
"relationships": {
"self_relationship": {
"parent_object": {
"data": {
"type": resource_type,
"id": parent_object_id,
Expand All @@ -1446,7 +1447,7 @@ async def test_create_with_relationship_to_the_same_table(self):
},
},
}
url = f"{url}?include=self_relationship"
url = f"{url}?include=parent_object"
res = await client.post(url, json=create_with_relationship_body)
assert res.status_code == status.HTTP_201_CREATED, res.text

Expand All @@ -1458,7 +1459,7 @@ async def test_create_with_relationship_to_the_same_table(self):
"attributes": {"name": "child"},
"id": child_object_id,
"relationships": {
"self_relationship": {
"parent_object": {
"data": {
"id": parent_object_id,
"type": "self_relationship",
Expand Down Expand Up @@ -2042,6 +2043,60 @@ async def test_update_resource_error_same_id(
],
}

async def test_remove_to_one_relationship_using_by_update(self, async_session: AsyncSession):
resource_type = "self_relationship"
with suppress(KeyError):
RoutersJSONAPI.all_jsonapi_routers.pop(resource_type)

app = build_app_custom(
model=SelfRelationship,
schema=SelfRelationshipSchema,
resource_type=resource_type,
)

parent_obj = SelfRelationship(name=fake.name())
child_obj = SelfRelationship(name=fake.name(), parent_object=parent_obj)
async_session.add_all([parent_obj, child_obj])
await async_session.commit()

assert child_obj.self_relationship_id == parent_obj.id

async with AsyncClient(app=app, base_url="http://test") as client:
expected_name = fake.name()
update_body = {
"data": {
"id": str(child_obj.id),
"attributes": {
"name": expected_name,
},
"relationships": {
"parent_object": {
"data": None,
},
},
},
}
params = {
"include": "parent_object",
}
url = app.url_path_for(f"update_{resource_type}_detail", obj_id=child_obj.id)
res = await client.patch(url, params=params, json=update_body)
assert res.status_code == status.HTTP_200_OK, res.text
assert res.json() == {
"data": {
"attributes": SelfRelationshipAttributesSchema(name=expected_name).dict(),
"id": str(child_obj.id),
"relationships": {"parent_object": {"data": None}},
"type": "self_relationship",
},
"included": [],
"jsonapi": {"version": "1.0"},
"meta": None,
}

await async_session.refresh(child_obj)
assert child_obj.self_relationship_id is None


class TestPatchRelationshipsToMany:
async def test_ok(
Expand Down Expand Up @@ -2217,6 +2272,65 @@ async def test_relationship_not_found(
],
}

async def test_remove_to_many_relationship_using_by_update(self, async_session: AsyncSession):
resource_type = "self_relationship"
with suppress(KeyError):
RoutersJSONAPI.all_jsonapi_routers.pop(resource_type)

app = build_app_custom(
model=SelfRelationship,
schema=SelfRelationshipSchema,
resource_type=resource_type,
)

parent_obj = SelfRelationship(name=fake.name())
child_obj_1 = SelfRelationship(name=fake.name(), parent_object=parent_obj)
child_obj_2 = SelfRelationship(name=fake.name(), parent_object=parent_obj)
async_session.add_all([parent_obj, child_obj_1, child_obj_2])
await async_session.commit()

assert child_obj_1.self_relationship_id == parent_obj.id
assert child_obj_2.self_relationship_id == parent_obj.id
assert len(parent_obj.children_objects) == 2 # noqa PLR2004

async with AsyncClient(app=app, base_url="http://test") as client:
expected_name = fake.name()
update_body = {
"data": {
"id": str(parent_obj.id),
"attributes": {
"name": expected_name,
},
"relationships": {
"children_objects": {
"data": None,
},
},
},
}
params = {
"include": "children_objects",
}
url = app.url_path_for(f"update_{resource_type}_detail", obj_id=parent_obj.id)
res = await client.patch(url, params=params, json=update_body)
assert res.status_code == status.HTTP_200_OK, res.text
assert res.json() == {
"data": {
"attributes": SelfRelationshipAttributesSchema(name=expected_name).dict(),
"id": str(parent_obj.id),
"relationships": {"children_objects": {"data": []}},
"type": "self_relationship",
},
"included": [],
"jsonapi": {"version": "1.0"},
"meta": None,
}

await async_session.refresh(child_obj_1)
await async_session.refresh(child_obj_2)
assert child_obj_1.self_relationship_id is None
assert child_obj_2.self_relationship_id is None


class TestDeleteObjects:
async def test_delete_object_and_fetch_404(
Expand Down

0 comments on commit a5dfc0d

Please sign in to comment.