【问题标题】:Add more data from a get request with FastAPI使用 FastAPI 从获取请求中添加更多数据
【发布时间】:2021-02-20 18:10:40
【问题描述】:

我开始在 python 和 sqlalchemy 中使用 FastAPI 构建一个 api: 这是模型的一部分:

class Game(Base):
    __tablename__ = "games"

    id = Column(Integer, primary_key=True, index=True)
    league_id = Column(Integer)
    radiant_score = Column(Integer)
    dire_score = Column(Integer)
    duration = Column(Integer)
    is_valid = Column(Boolean, default=True)

    playerstats = relationship("PlayerStat", back_populates="match")


class PlayerStat(Base):
    __tablename__ = "playerstats"

    match_id = Column(Integer, ForeignKey("games.id"), primary_key=True)
    slot = Column(Integer, primary_key=True)
    hero_id = Column(Integer, ForeignKey("heros.id"))
    num_kills = Column(Integer, default=None)
    isRadiant = Column(Boolean, default=None)

    match = relationship("Game", back_populates="playerstats")
    heros = relationship("Hero", back_populates="playerstats")

在此之后,我为 pydantic 创建了模式/模型(很长的部分):

    class PlayerStatBase(BaseModel):
    slot: int
    hero_id: int
    num_kills: int
    isRadiant: bool


class PlayerStatCreate(PlayerStatBase):
    pass


class PlayerStat(PlayerStatBase):
    slot: int
    hero_id: int
    num_kills: int
    isRadiant: bool

    class Config:
        orm_mode = True

class GameBase(BaseModel):
    id: int
    league_id: int
    radiant_score: int
    dire_score: int
    duration: int
    is_valid: bool


class GameCreate(GameBase):
    pass


class Game(GameBase):
    id: int
    league_id: int
    radiant_score: int
    dire_score: int
    duration: int
    is_valid: bool
    players: List[PlayerStat] = [{}]

    class Config:
        orm_mode = True

还有我在 api 中使用的 crud 函数:

    def get_match(db: Session, match_id: int):
    print(db.query(models.Game).filter(models.Game.id == match_id))
    return db.query(models.Game).filter(models.Game.id == match_id).first()

api路由是:

@app.get("/matches/{match_id}", response_model=schemas.Game)
def read_game(match_id: int, db: Session = Depends(get_db)):
    db_game = crud.get_match(db, match_id=match_id)
    if db_game is None:
        raise HTTPException(status_code=404, detail="Game not found")
    return db_game

我得到的结果是下一个:

{
  "id": 1,
  "league_id": 10,
  "radiant_score": 41,
  "dire_score": 5,
  "duration": 3541,
  "is_valid": true,
  "players": [
    {}
  ]
}

我想用相应比赛的球员统计数据列表(按插槽排序)填充“球员”:

"players" : [
  {
    "slot": 0,
    "hero_id": 14,
    "num_kills": 54,
    "isRadiant": true
  },
  {
    "slot": 1,
    "hero_id": 15,
    "num_kills": 1,
    "isRadiant": false
  }
]

我想我需要尝试其中一个模型/模式或 crud 函数,但不知道是哪一个? 此外,也许有一些无用或构建不佳的 pydantic 模式

PS : 我遵循了 FastAPI 文档的指导方针(我推荐阅读)。

感谢您的帮助!

【问题讨论】:

    标签: python-3.x database api sqlalchemy fastapi


    【解决方案1】:

    我相信你的问题不在fastapi的范围内,而是在sqlalchemy的范围内。 当您查询具有关系的 orm 对象时,fastapi 的标准是在访问关系时延迟加载关系。 由于您从不直接访问关系 playerstats,因此它不会加载。有关信息,请参阅the docs

    您的问题的解决方案应该是将 crud 函数更新为:

    return db.query(models.Game).filter(models.Game.id == match_id)
                                .options(selectinload(models.Game.playerstats)).first()
    

    “在加载中选择”是一种急切加载,它将在提交查询时加载关系。如果您希望在每个查询中都发生这种行为,您可以将 orm 更新为:

    playerstats = relationship("PlayerStat", back_populates="match", lazy="selectin")
    

    我希望这会有所帮助。这是我对 stackoverflow 的第一个回答 :)

    编辑:实际上还有另一件事。在您的 orm 中,关系称为“playerstats”,而您在 pydantic 模型中将属性命名为“players”。那是行不通的。将 pydantic 属性名称从“players”更改为“playerstats”,现在一切正常。

    编辑 2:你可以猜到,没有一切都行不通。我刚刚看到还缺少另一件事。在 pydantic 模型中,您可以设置 orm 选项。这在使用 sqlalchemy 时非常重要。我向所有 pydantic 模型推荐这个。 这必须在每个 pydantic 模型及其属性模型上设置

    class OtherModel(BaseModel):
        value: str = None
    
        class Config:
            orm_mode = True
    
    
    class SomePydanticModel(BaseModel):
        value: str = None
        some_other_model: OtherModel = None
    
        class Config:
             orm_mode = True
    

    现在我们也可以再次修改你的 crud 方法的 return 语句:

    return Game.from_orm(db.query(models.Game).options(selectinload(models.Game.playerstats)).filter(models.Game.id == match_id).first())
    

    现在一切终于可以正常工作了:)

    【讨论】:

    • 感谢您的回答,您给了我解决方案并解释了还有什么问题。你知道我怎么能把玩家的结果格式化成这样的东西(在 playerstat 数据本身之外按 playerstat.slot 排序):``` "players" : { "0" : { "slot": 0, "hero_id": 14,“num_kills”:54,“isRadiant”:真},“1”:{“slot”:1,“hero_id”:15,“num_kills”:1,“isRadiant”:假}}```跨度>
    • @aut0wash 通常不推荐这种方式来格式化数据。当您重复信息时,可以通过数组索引以相同的方式访问这些信息。对于排序,我建议使用“oder by”:riptutorial.com/sqlalchemy/example/12146/order-by
    猜你喜欢
    • 2023-02-10
    • 2016-03-04
    • 1970-01-01
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    • 2021-02-26
    • 2021-12-18
    • 1970-01-01
    相关资源
    最近更新 更多