fastapi-pagination icon indicating copy to clipboard operation
fastapi-pagination copied to clipboard

In fastapi async, I have a complex return model pagination, using sqlalchemy How to nest pagination

Open 13129 opened this issue 1 year ago • 2 comments

Expect results

{
  "data": {
    "items": [
      {
        "id": "string",
        "cate_name": "string",
        "create_time": "2024-01-09T05:35:17.426Z",
        "last_modify_time": "2024-01-09T05:35:17.426Z"
      }
    ],
    "total": 0,
    "page": 1,
    "size": 1,
    "pages": 0
  },
  "code":200,
  "message":""
}

Actual output

TypeError: Object of type JobCategory is not JSON serializable
#sqlalchemy model
class JobCategory(DBBase):
    __tablename__ = 'data_job_category_info'
    cate_name = Column("cate_name", String(128), comment="", unique=True)
    create_time = Column("create_time", DateTime, server_default=func.now(), comment="")
    last_modify_time = Column("last_modify_time", DateTime, server_default=func.now(), onupdate=func.now(),
                              comment="")
#pydantic model
class JobCateSchema(BaseModelSchema):
    cate_name: str = Field(title='', )
    create_time: datetime = Field(title='', )
    last_modify_time: datetime = Field(title='', )

    class Config:
        from_attributes = True

class RestfulModel(BaseModel):
    code: int
    message: str
    data: Page[JobCateSchema]  # noqa: F401
#router get all
    @router.get(path='', dependencies=[Depends(pagination_ctx(Page))], response_model=RestfulModel)
    async def ov_job_cate_get_all(session=Depends(router.db_func)):
        query = select(JobCategory).order_by(JobCategory.id)
        result = await paginate(session, query)
        # result = jsonable_encoder(result.scalars(), exclude_unset=True)
        return resp_200(data=dict(result))

How do I configure to get the pagination nested in

13129 avatar Jan 09 '24 05:01 13129

I had been struggling with a similar issue(raise RuntimeError("Use params or add_pagination")) for a time, and after referring to your code(dependencies=), I was able to solve the problem. I am extremely grateful for your help. I would like to share my solution method: python 3.11

#...
T = TypeVar('T')
class Result(GenericModel, Generic[T]):
    code: int = 0
    message: str = 'OK'
    time: str = ''
    data: Optional[T] = None

#...
@router.get("/product", dependencies=[Depends(pagination_ctx(Page))], response_model=Result[Page[ProductOut]])
async def query(title: str | None = None, db: Session = Depends(xx.get_session)) -> Any:
    return result.ok(service.query(title))
#...

junxy avatar Jan 09 '24 10:01 junxy

I used pydantic's model_dump() method and it passed successfully

@staticmethod
    @router.get(path='', dependencies=[Depends(pagination_ctx(PageS))],
                response_model=ResultSerializer[PageS[router.schema]])
    async def ov_job_cate_get_all(session=Depends(router.db_func)):
        query = select(JobCategory).order_by(JobCategory.id)
        result = await paginate(session, query)
        result = jsonable_encoder(result.model_dump())
        return resp_200(data=result)

However, a new problem arises with associated objects

 File "F:\Workspace\py_frame\data_preprocess\src\api\job.py", line 110, in ov_job_info_get_all
    result = jsonable_encoder(result.model_dump())
             │                │      └ <function BaseModel.model_dump at 0x00000243B411E200>
             │                └ CustomizedPage(items=[(<src.models.job.JobInfo object at 0x00000243B6FBD090>, '12fasa'), (<src.models.job.JobInfo object at 0...
             └ <function jsonable_encoder at 0x00000243B5EC1300>

  File "F:\Workspace\py_frame\data_preprocess\venv\Lib\site-packages\fastapi\encoders.py", line 287, in jsonable_encoder
    encoded_value = jsonable_encoder(
                    └ <function jsonable_encoder at 0x00000243B5EC1300>
  File "F:\Workspace\py_frame\data_preprocess\venv\Lib\site-packages\fastapi\encoders.py", line 301, in jsonable_encoder
    jsonable_encoder(
    └ <function jsonable_encoder at 0x00000243B5EC1300>
  File "F:\Workspace\py_frame\data_preprocess\venv\Lib\site-packages\fastapi\encoders.py", line 330, in jsonable_encoder
    raise ValueError(errors) from e
                     └ [TypeError('cannot convert dictionary update sequence element #0 to a sequence'), TypeError('vars() argument must have __dict...

ValueError: [TypeError('cannot convert dictionary update sequence element #0 to a sequence'), TypeError('vars() argument must have __dict__ attribute')]
class JobInfo(DBBase):
    __tablename__ = 'data_job_info'
    job_name = Column("job_name", String(128), comment="作业名称", unique=True)
    cate_id = Column("cate_id", String(128), comment="作业类别id")
    create_time = Column("create_time", DateTime, server_default=func.now(), comment="创建时间")
    last_modify_time = Column("last_modify_time", DateTime, server_default=func.now(), onupdate=func.now(),
                              comment="修改时间")

@staticmethod
    @router.get(path='', dependencies=[Depends(pagination_ctx(PageS))],
                response_model=ResultSerializer[PageS[JobInfoSchemaSerializer]])
    async def ov_job_info_get_all(session=Depends(router.db_func)):
        query = select(JobInfo, JobCategory.cate_name).join(JobCategory
                                                            , onclause=JobInfo.cate_id == JobCategory.id).order_by(
            JobInfo.id)
        result = await paginate(session, query)
        result = jsonable_encoder(result.model_dump())
        print(result)
        return resp_200(data=result)

13129 avatar Jan 10 '24 03:01 13129

Hi @13129,

Sorry for long response. Could you please try to update pagination_ctx call to:

#router get all
    @router.get(path='', dependencies=[Depends(pagination_ctx(Page[JobCateSchema]))], response_model=RestfulModel)
    async def ov_job_cate_get_all(session=Depends(router.db_func)):
        query = select(JobCategory).order_by(JobCategory.id)
        result = await paginate(session, query)
        # result = jsonable_encoder(result.scalars(), exclude_unset=True)
        return resp_200(data=dict(result))

uriyyo avatar Feb 09 '24 12:02 uriyyo

Is this issue still relevant? Can I somehow help you?

uriyyo avatar Feb 09 '24 12:02 uriyyo

@13129 Any updates? Can I somehow help you?

uriyyo avatar Mar 02 '24 21:03 uriyyo

@13129 Is this issue still relevant?

uriyyo avatar Mar 15 '24 17:03 uriyyo

@13129 I'm closing this issue. Please, reopen it in case this issue is still relevant.

uriyyo avatar Apr 06 '24 14:04 uriyyo