Fastapi: How to add submodel ids as a list to the model?

Created on 21 Oct 2020  路  7Comments  路  Source: tiangolo/fastapi

First check

  • [x] I added a very descriptive title to this issue.
  • [x] I used the GitHub search to find a similar issue and didn't find it.
  • [x] I searched the FastAPI documentation, with the integrated search.
  • [x] I already searched in Google "How to X in FastAPI" and didn't find any information.
  • [x] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [x] I already checked if it is not related to FastAPI but to Pydantic.
  • [x] I already checked if it is not related to FastAPI but to Swagger UI.
  • [x] I already checked if it is not related to FastAPI but to ReDoc.
  • [x] After submitting this, I commit to one of:

    • Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

    • I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

    • Implement a Pull Request for a confirmed bug.

Example

Here's a self-contained, minimal, reproducible, example with my use case:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}

# sqlalchemy models

class User(Base):
    __tablename__ = "user"
    id = Column( Integer, primary_key=True, index=True)
    full_name = Column( String, index=True)
    email = Column( String, unique=True, index=True)
    hashed_password = Column( String)
    is_active = Column( Boolean(), default=False)
    is_superuser = Column( Boolean(), default=False)
    items = relationship("Item", back_populates="owner")

class Item(Base):
    __tablename__ = "items"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, index=True)
    owner_id = Column(Integer, ForeignKey("user.id"))
    owner = relationship("User", back_populates="items")

# pydantic models

class UserBase(BaseModel):
    email: Optional[str] = None
    full_name: Optional[str] = None
   items: Optional[List[ItemInDBBase]]

class ItemBase(BaseModel):
    title: str = None
    description: str = None
    owner_id: int

class ItemInDBBase(ItemBase):
    id: int
    title: str
    owner_id: int

Description

the frontend expects the fastapi to return the data in following format.

"users": {
"id": 5,
"full_name": "XYZ",
"email": "[email protected]",
"items": [1, 2]
}

How can I return the id's of item as a list rather than returning the item model

question

All 7 comments

Assuming you follow the recommended structure:

from typing import List

from fastapi import FastAPI, Depends
from sqlalchemy import Session

from app.database import get_session
from app.models import User

app = FastAPI()

@app.get("/items", response_model=List[int])
def list_items(session: Session = Depends(get_session)):
    return session.query(User.id).distinct()

In your example, just adding the endpoint itself is fine.

Something like this would fit closer, I think.

class UserBase(BaseModel):
    email: Optional[str] = None
    full_name: Optional[str] = None
    items: Optional[List[int]]

    class Config:
        orm_mode = True

        class getter_dict():
            def __init__(self, item):
                self.item = item

            def get(self, name, default=None):
                value = getattr(self.item, name, default)
                if isinstance(value, list):
                    return [item.id for item in value]
                else:
                    return value

@app.get("/", response_model=UserBase)
async def read_root():
    return User(items=[Item(id=10)])

I completely missunderstood the question. What @Mause said makes more sense.

Thanks. This was extremely helpful as I am just getting started with fastapi

@Mause Do you have a complete example? I was not able to reproduce your example...
I was able to do it with validators, but not with getter_dict.

Here is a full example:

from fastapi import FastAPI
from typing import Optional, List
from pydantic import BaseModel
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

app = FastAPI()


# sqlalchemy models


class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, index=True)
    full_name = Column(String, index=True)
    email = Column(String, unique=True, index=True)
    hashed_password = Column(String)
    is_active = Column(Boolean(), default=False)
    is_superuser = Column(Boolean(), default=False)
    items = relationship("Item", back_populates="owner")


class Item(Base):
    __tablename__ = "items"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, index=True)
    owner_id = Column(Integer, ForeignKey("user.id"))
    owner = relationship("User", back_populates="items")


# pydantic models


class UserBase(BaseModel):
    email: Optional[str] = None
    full_name: Optional[str] = None
    items: Optional[List[int]]

    class Config:
        orm_mode = True

        class getter_dict():
            def __init__(self, item):
                self.item = item

            def get(self, name, default=None):
                value = getattr(self.item, name, default)
                if isinstance(value, list):
                    return [item.id for item in value]
                else:
                    return value


class ItemBase(BaseModel):
    title: Optional[str] = None
    description: Optional[str] = None
    owner_id: int


class ItemInDBBase(ItemBase):
    id: int
    title: str
    owner_id: int


@app.get("/", response_model=UserBase)
async def read_root():
    return User(items=[Item(id=10)])


from fastapi.testclient import TestClient

tc = TestClient(app)

print(tc.get('/').json())

Thank you! You made me realize what I was doing wrong :)

Was this page helpful?
0 / 5 - 0 ratings