Search code examples
pythonsqlalchemybackendfastapipydantic

Pydantic cannot serialize data FastAPI


I tried a bunch of methods to fix this error but I still don’t understand what the problem is.

router:

@router.get('', response_model=List[SProject])
     async def get_all_projects():
     return await ProjectDAO.find_all()

models:

class ProjectProfile(Base):
    __tablename__ = 'project_profile'
    project_id = Column(ForeignKey('projects.id'), primary_key=True)
    profile_id = Column(ForeignKey('profiles.id'), primary_key=True)
    role = Column('role', String)
    is_creator = Column(Boolean, default=False)


class Project(Base):
    __tablename__ = 'projects'

    id = Column(Integer, primary_key=True)
    title = Column(String, nullable=False)
    description = Column(String)
    created_at = Column(Date, default=datetime.utcnow())

    profiles = relationship('Profile', secondary='project_profile',
                        back_populates='projects')

schemas:

class SProject(BaseModel):
    id: int
    title: str
    description: Optional[str] = None
    created_at: Optional[datetime] = None

    class Config:
        orm_mode = True

dao:

class ProjectDAO(BaseDAO):
    model = Project

    @classmethod
    async def find_all(cls):
        async with async_session_maker() as session:
            query = select(cls.model)
            result = await session.execute(query)
            return result.mappings().all()

response:

    raise ResponseValidationError(
fastapi.exceptions.ResponseValidationError: 2 validation errors:
    {'type': 'missing', 'loc': ('response', 0, 'id'), 'msg': 'Field required', 'input': {'Project': <app.projects.models.Project object at 0x7f6d5dc95e40>}, 'url': 'https://errors.pydantic.dev/2.3/v/missing'}
    {'type': 'missing', 'loc': ('response', 0, 'title'), 'msg': 'Field required', 'input': {'Project': <app.projects.models.Project object at 0x7f6d5dc95e40>}, 'url': 'https://errors.pydantic.dev/2.3/v/missing'}

If I remove the SProject scheme from the router, it returns data normally

Like this

router:

@router.get('')
async def get_all_projects():
    return await ProjectDAO.find_all()

response:

[
  {
    "Project": {
      "created_at": "2023-09-20",
      "id": 2,
      "description": "sdadasd",
      "title": "dasd"
    }
  }
]

Solution

  • You are getting this error because according to your response model, the response should be in the format

    [
      {
          "created_at": "2023-09-20",
          "id": 2,
          "description": "sdadasd",
          "title": "dasd"
      }
    ]
    

    You are returning this nested in a "Project" object

    [
      {
        "Project": {
          "created_at": "2023-09-20",
          "id": 2,
          "description": "sdadasd",
          "title": "dasd"
        }
      }
    ]
    

    That's the way sqlalchemy's mappings().all() returns the dictionary.

    If you want the response like this, you can solve this error by changing your response model

    class SProject(BaseModel):
        id: int
        title: str
        description: Optional[str] = None
        created_at: Optional[datetime] = None
    
        class Config:
            orm_mode = True
    
    class SProjectResponse(BaseModel):
        Project: SProject = Field()
    

    And you set List[SProjectResponse] as your response model

    @router.get('', response_model=List[SProjectResponse])
         async def get_all_projects():
         return await ProjectDAO.find_all()