2

I'm trying to alter the content of a list view on FastAPI, depending on a get parameter. As the format is defined by a pydantic model, how can I customize it (or use an alternative model from within the view)?

Here's my view:

from fastapi_pagination import Page, Params, paginate
from pydantic import BaseModel
from sqlalchemy.orm import Session


class EventSerializer(BaseModel):
    id: str
    # ...

class EventAttendeeSerializer(BaseModel):
    id: str
    event: str  # contains the event UUID
    # ...

    class Config:
        orm_mode = True


@api.get("/", response_model=Page[EventAttendeeSerializer])
async def get_list(db: Session, pagination: Params = Depends(), extend: str = None):
    objects = db.query(myDbModel).all()
    if "event" in extend.split(","):
        # return EventSerializer for each object instead of id
    
    return paginate(objects, pagination)

At runtime, it would work like this:

GET /v1/event-attendees/
{
    "items": [
        {
            "id": <event_attendee_id>,
            "event": <event_id>,
        }
    ],
    "total": 1,
    "page": 1,
    "size": 50,
}
GET /v1/event-attendees/?extend=event
{
    "items": [
        {
            "id": <event_attendee_id>,
            "event": {
                "id": <event_id>,
                # ...
            }
        }
    ],
    "total": 1,
    "page": 1,
    "size": 50,
}

I searched for some kind of hooks in the pydantic and FastAPI docs and source code, but did not find anything relevant. Anyone can help please?

Chris
  • 18,724
  • 6
  • 46
  • 80
koleror
  • 338
  • 1
  • 6
  • 22
  • Does this answer your question? [How to return data in JSON format using FastAPI?](https://stackoverflow.com/questions/73972660/how-to-return-data-in-json-format-using-fastapi) – Chris Jan 13 '23 at 13:48
  • Please take a look at [this answer](https://stackoverflow.com/a/74173023/17865804), as well as [this answer](https://stackoverflow.com/a/73580096/17865804). – Chris Jan 13 '23 at 13:49
  • Not really, as I'm actually trying to avoid doing the object serialization manually and keep using fastapi/pydantic object serialization process. In a perfect world, I'd like to be able to implement it somewhere within the serializer (but I would need to access the request from there or pass parameters). – koleror Jan 13 '23 at 14:04
  • That's a good idea, but the problem is I'm actually returning a list of models coming from database, which gets converted by the serializer. – koleror Jan 13 '23 at 21:26

1 Answers1

1

You can decalre a response_model using Union (of two types) and return the model your wish if a condition is met. Since you are returning a list of objects/models, you can have response_model declared as a List of Union.

Working Example

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Union


class Simple(BaseModel):
    id: int


class Specific(Simple):
    description: str


RESULT = {
    'id': 1,
    'description': 'test'
}


app = FastAPI()

    
@app.get('/results', response_model=List[Union[Specific, Simple]])
def get_results(specific: bool = False):
    if specific:
        results = [Specific(**RESULT)] * 2
    else:
        results = [Simple(**RESULT)] * 2

    return results

Using FastAPI 0.89.0+, you can alternatively declare the return type / response_model in the function return type annotation, for instance:

@app.get('/results')
def get_results(specific: bool = False) -> List[Union[Specific, Simple]]:
    if specific:
        # ...

As for using alternate serialisers (as mentioned in your question), please have a look at this answer and this answer. You can read about how FastAPI serialises a return value by default in this answer.

Chris
  • 18,724
  • 6
  • 46
  • 80
  • Thanks for your answer. That would work for a get_detail, where I would return a single object, but would force me to use a loop for list responses, which would be inefficient. That's exactly what I'm trying to avoid. – koleror Jan 15 '23 at 10:58