Skip to content

Commit a7063d4

Browse files
authored
Merge pull request #48 from mts-ai/fix-duplicates-in-list-response
Fix duplicated entities in list response
2 parents 550aa4e + 4444f4a commit a7063d4

File tree

3 files changed

+68
-2
lines changed

3 files changed

+68
-2
lines changed

docs/changelog.rst

+15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@ Changelog
22
#########
33

44

5+
**2.3.2**
6+
*********
7+
8+
Duplicated entities in response fix
9+
=======================================
10+
* fix duplicates in list response `#48 <https://github.com/mts-ai/FastAPI-JSONAPI/pull/48>`_
11+
12+
13+
Authors
14+
"""""""
15+
16+
* `@CosmoV`_
17+
* `@mahenzon`_
18+
19+
520
**2.3.1**
621
*********
722

fastapi_jsonapi/data_layers/sqla_orm.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from sqlalchemy.orm import joinedload, selectinload
1010
from sqlalchemy.orm.attributes import InstrumentedAttribute
1111
from sqlalchemy.orm.collections import InstrumentedList
12+
from sqlalchemy.sql import column, distinct
1213

1314
from fastapi_jsonapi import BadRequest
1415
from fastapi_jsonapi.data_layers.base import BaseDataLayer
@@ -270,7 +271,8 @@ async def get_collection_count(self, query: "Select", qs: QueryStringManager, vi
270271
if self.disable_collection_count is True:
271272
return self.default_collection_count
272273

273-
return (await self.session.execute(select(func.count()).select_from(query.subquery()))).scalar_one()
274+
count_query = select(func.count(distinct(column("id")))).select_from(query.subquery())
275+
return (await self.session.execute(count_query)).scalar_one()
274276

275277
async def get_collection(self, qs: QueryStringManager, view_kwargs: Optional[dict] = None) -> Tuple[int, list]:
276278
"""
@@ -299,7 +301,7 @@ async def get_collection(self, qs: QueryStringManager, view_kwargs: Optional[dic
299301

300302
query = self.paginate_query(query, qs.pagination)
301303

302-
collection = (await self.session.execute(query)).scalars().all()
304+
collection = (await self.session.execute(query)).unique().scalars().all()
303305

304306
collection = await self.after_get_collection(collection, qs, view_kwargs)
305307

tests/test_api/test_api_sqla_with_includes.py

+49
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,55 @@ async def test_filter_with_nested_conditions(
19581958
"meta": {"count": 3, "totalPages": 1},
19591959
}
19601960

1961+
async def test_join_by_relationships_does_not_duplicating_response_entities(
1962+
self,
1963+
app: FastAPI,
1964+
async_session: AsyncSession,
1965+
client: AsyncClient,
1966+
user_1: User,
1967+
user_1_post: PostComment,
1968+
):
1969+
text = fake.sentence()
1970+
comment_1 = PostComment(
1971+
text=text,
1972+
post_id=user_1_post.id,
1973+
author_id=user_1.id,
1974+
)
1975+
comment_2 = PostComment(
1976+
text=text,
1977+
post_id=user_1_post.id,
1978+
author_id=user_1.id,
1979+
)
1980+
async_session.add_all([comment_1, comment_2])
1981+
await async_session.commit()
1982+
1983+
params = {
1984+
"filter": dumps(
1985+
[
1986+
{
1987+
"name": "posts.comments.text",
1988+
"op": "eq",
1989+
"val": text,
1990+
},
1991+
],
1992+
),
1993+
}
1994+
1995+
url = app.url_path_for("get_user_list")
1996+
res = await client.get(url, params=params)
1997+
assert res.status_code == status.HTTP_200_OK, res.text
1998+
assert res.json() == {
1999+
"data": [
2000+
{
2001+
"attributes": UserAttributesBaseSchema.from_orm(user_1),
2002+
"id": str(user_1.id),
2003+
"type": "user",
2004+
},
2005+
],
2006+
"jsonapi": {"version": "1.0"},
2007+
"meta": {"count": 1, "totalPages": 1},
2008+
}
2009+
19612010

19622011
ASCENDING = ""
19632012
DESCENDING = "-"

0 commit comments

Comments
 (0)