question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Multiple Responses Models

See original GitHub issue

Example

Resource:

@router.get("/season/{season_id}", response_model=Union[Split, Season], response_model_exclude={'id'})
async def get_season(season_id: int, split: Optional[int] = None):
    season = await Season.get_by_season_id(season_id)
    if not season:
        return JSONResponse(status_code=404, content={"message": "Season not found"})
    if split:
        return await Split.get_by_number(season.id, split)
    else:
        return season

Models

  • Season:
class Season(Model, ABC):
    season_id: Optional[int] = Field(1, ge=1)
    start_date: Optional[datetime] = datetime(date.today().year, 1, 1)
    end_date: Optional[datetime] = datetime(date.today().year, 12, 31)
    splits: Optional[List[ObjectId]] = []

    async def create(self) -> None:
        self.season_id = await Season.count_seasons() + 1
        await self.save()

    @classmethod
    async def get_by_id(cls, _id: int) -> "Season":
        return await engine.find_one(cls, cls.id == _id)
  • Split:
class Split(Model, ABC):
    split_id: Optional[int] = Field(1, ge=1)
    number: Optional[int] = Field(1, ge=1)
    start_date: Optional[datetime] = datetime(date.today().year, date.today().month, 1)
    end_date: Optional[datetime]
    season: Optional[ObjectId]

    async def create(self) -> None:
        season = await Season.get_last()
        self.split_id = await self.count_splits() + 1
        self.number = season.count_splits() + 1
        self.start_date = datetime(date.today().year, date.today().month, 1)
        self.end_date = self.last_day_of_month(self.start_date)
        self.season = season.id
        await self.save()
        season.splits.append(self.id)
        await season.save()

    @classmethod
    async def get_by_number(cls, season_id, split_number: int) -> "Split":
        return await engine.find_one(cls, cls.season == season_id, cls.number == split_number)

Description

  • I expect to return a season model when the query is empty, and a split model if the query is not empty
  • When I try to get “/season/3?split=2” it returns
{
    "split_id": 2,
    "number": 2,
    "start_date": "2020-12-01T00:00:00",
    "end_date": "2020-12-31T00:00:00",
    "season": "5fd894151470b8ec917c6007"
}

as expected.

  • But when I try to get “/season/3” it returns
{
    "split_id": 1,
    "number": 1,
    "start_date": "2020-01-01T00:00:00",
    "end_date": "2020-12-31T00:00:00",
    "season": null
}

which is still a Split Model and not a Season Model, even though the method returns a Season Model and not a Split Model.

The expected output would be:

{
    "season_id": 3,
    "start_date": "2020-01-01T00:00:00",
    "end_date": "2020-12-31T00:00:00",
    "splits": [
        "5fd894271470b8ec917c6008",
        "5fd8942e1470b8ec917c6009"
    ]
}

which I achieve if I swap Split and Season on:

@router.get("/season/{season_id}", response_model=Union[Season, Split], response_model_exclude={'id'})

So it seems that only the first model is being used.

Environment

  • OS: Windows 10

  • FastAPI Version: 0.62.0

  • Python version: 3.9.1

Additional context

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:9 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
Mausecommented, Dec 29, 2020

@hellocoldworld I think you’ll find that’s exactly why Literal exists


import json
from pydantic import BaseModel
from typing import Literal, Union

class Season(BaseModel):
    model_type: Literal['season']
    hello: int

class Split(BaseModel):
    model_type: Literal['split']
    hello: str


class Parent(BaseModel):
    thing: Union[Season, Split]
0reactions
waderoberts123commented, Aug 15, 2022

Constructing the pydantic model object prior to returning it ensures that the data is valid, so you can directly return a JSONResponse after using the jsonable_encoder on the pydantic object.

Keep in mind that you will still want to keep the type reference in the response_model parameter so that your OpenAPI docs will still work.

References:

Read more comments on GitHub >

github_iconTop Results From Across the Web

Models with of Multiple Responses
Introduction. This chapter reviews some of the methodology which is being used in a statistical investigation of the assessment process associated with GNVQ....
Read more >
Multiple Response Regression for Gaussian Mixture Models ...
Multiple response regression is a useful regression technique to model multiple response variables using the same set of predictor variables ...
Read more >
11.1 - Multiple Responses | STAT 503
As expected, multiple response analysis starts with building a regression model for each response separately. For instance, in Example 11.2 we can fit...
Read more >
Getting started with Multivariate Multiple Regression
Multivariate Multiple Regression is the method of modeling multiple responses, or dependent variables, with a single set of predictor ...
Read more >
Modelling Strategies for Repeated Multiple ... - Research Online
This article discusses modelling strategies of a repeated multiple response variable, a categorical variable for which respondents can select any number of ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found