14

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?

A G
  • 997
  • 2
  • 18
  • 36

2 Answers2

4

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]))
user
  • 5,370
  • 8
  • 47
  • 75
HeyMan
  • 1,529
  • 18
  • 32
0

Definitely, it's described in the docs. Try this, ellipsis marking the field as required.

id: Optional[int] = Header(...)  # Header, path or any another place 

See https://fastapi.tiangolo.com/tutorial/query-params-str-validations/

Давид Шико
  • 362
  • 1
  • 4
  • 13
  • thanks, so it looks like I'd still need to add the above line (`(attribute) = Optional(type) = Query(None)` for every attribute invidaually. Is there a way to automatically set a query parameter for each attribute of the model? – A G Mar 18 '21 at 10:42
  • @AG Yes, use schemas as input input https://fastapi.tiangolo.com/tutorial/response-model/ – Давид Шико Mar 18 '21 at 15:04
  • The response model determines what gets returned, but I want to know if there is an easy way to filter on all attributes without manually adding it for each attribute – A G Mar 18 '21 at 20:50
  • 1
    @AG Зlease read furtherю You can also use the same model (or another) as input: https://fastapi.tiangolo.com/tutorial/response-model/#add-an-output-model – Давид Шико Mar 18 '21 at 22:08