Home > Software engineering >  How to filter an item in a database by different fields in python?
How to filter an item in a database by different fields in python?

Time:12-29

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.

  • Related