Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enable filtering on all fields of a model in FastAPI

In Django with the restframework, you can do this:

class Item(models.Model):
    id = models.IntegerField()
    name = models.CharField(max_length=32)
    another_attribute = models.CharField(max_length=32)
    ...
    (more attributes)
    ...
    yet_another_attribute = models.CharField(max_length=32)

class ItemViewSet(viewsets.ReadOnlyModelViewSet):
    permission_classes = [IsAuthenticated]
    serializer_class = ItemSerializer
    filterset_fields = '__all__' # <- this enables filtering on all fields
    queryset = Item.objects.all()

If I want to allow filtering, filterset_fields = '__all__' would allow me to do something like api/item/?(attribute)=(value) and allow me to filter on any attribute

I'm going through the tutorial (https://fastapi.tiangolo.com/tutorial/sql-databases/#crud-utils) and it looks like there is a lot of manual filtering involved:

from fastapi_sqlalchemy import db

class Item(BaseModel):
    id: int
    name: str
    another_attribute: str
    ...
    (more attributes)
    ...
    yet_another_attribute: str

# is it necessary to manually include all the fields I want to filter on as optional query parameters?
@app.get("/items/")
async def read_item(
    db: Session,
    id: Optional[int] = None,
    name: Optional[str] = None,
    another_attribute: Optional[str] = None,
    ...
    (more attributes)
    ...
    yet_another_attribute: Optional[str] = None
):
    # and then I'd need to check if the query parameter has been specified, and if so, filter it.
    queryset = db.session.query(Item)
    if id:
        queryset = queryset.filter(Item.id == id)
    if name:
        queryset = queryset.filter(Item.name == name)
    if another_attribute:
        queryset = queryset.filter(Item.another_attribute == another_attribute)
    ...
    (repeat above pattern for more attributes)
    ...
    if yet_another_attribute:
        queryset = queryset.filter(Item.yet_another_attribute == yet_another_attribute)

What is the preferred way of implementing the above behaviour? Are there any packages that will save me from having to do a lot of manual filtering that will give me the same behaviour as conveniently as the Django Rest Framework viewsets?

Or is manually including all the fields I want to filter on as optional query parameters, then checking for each parameter and then filtering if present the only way?

like image 811
A G Avatar asked Jan 29 '26 19:01

A G


1 Answers

It is possible but not yet perfect:

from fastapi.params import Depends

@app.get("/items/")
async def read_item(item: Item = Depends()):
    pass

See FastAPI documentation for details.

The downside is that the parameters are required as maybe specified in the Item class. It is possible to write a subclass with all optional parameters (e.g. like described here). It is working for instances of the class but FastAPI does not seem to reflect those in the API docs. If anyone has a solution to that I'd be happy to learn.

Alternatively you can have multiple models as described here. But I don't like this approach.

To answer your 2nd question you can access all generic parameters like this:

@app.get("/items/")
async def read_item(
    db: Session,
    id: Optional[int] = None,
    name: Optional[str] = None,
    another_attribute: Optional[str] = None,
    ...
    (more attributes)
    ...
    yet_another_attribute: Optional[str] = None
):
    params = locals().copy()
    ...
    for attr in [x for x in params if params[x] is not None]:
        query = query.filter(getattr(db_model.Item, attr).like(params[attr]))
like image 80
HeyMan Avatar answered Feb 01 '26 12:02

HeyMan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!