I am creating an application with FastAPI and I want to filter 'items' in an endpoint according to their name and category. However, I want that if any of these two fields is not passed, it is not taken into account, and if both are passed, both filters are applied. I have written the following code for this:
async def filter_items_by_name_category(
self,
name: str | None = None,
category: str | None = None
):
if name is not None:
items_by_name = await self.model.filter(name__icontains = name).all()
if category is None:
return items_by_name
if category is not None:
items_by_category = await self.model.filter(category = category).all()
if name is None:
return items_by_category
items = [item for item in items_by_name if item in items_by_category]
return items
The code works but it seems very inelegant to me! I was thinking that maybe there is another better implementation. I am using tortoise orm but I think it will not have relevance in the code.
CodePudding user response:
I think you should be able to dynamically generate the Q object that you need.
Something like:
async def filter_items_by_name_category(
self,
name: str | None = None,
category: str | None = None
):
qs = []
if name is not None:
qs.append(Q(name__icontains = name)
if category is not None:
qs.append(category = category)
return await self.model.filter(Q(*qs, join_type="AND")).all()
CodePudding user response:
How about this:
async def filter_items_by_name_category(
self,
name: str = "",
category: str = ""
):
if name:
items_by_name = await self.model.filter(name__icontains = name).all()
if not category:
return items_by_name
if category:
items_by_category = await self.model.filter(category = category).all()
if not name:
return items_by_category
return [item for item in items_by_name if item in items_by_category]
Tidied a bit by using truthy/falsy values (which avoids None
as a default) and returning items
instead of setting as a variable and then returning it.