Pydantic 强制 SQL 查询已填充的字段

问题描述 投票:0回答:1

我在 Pydantic 中遇到了有趣的情况:即使该字段已经由对象填充,当 FastAPI 返回(或者我手动使用

model_validate
)时,额外的 SQL 会被调用到某些字段(更准确地说,未延迟和连接加载)字段):

class PortfolioFullSchema(BaseModel):
    id: int
    title: str
    category: str
    cover_image: Optional[str] = None
    description: Optional[str] = None
    user_username: str
    is_saved: bool = False
    is_appreciated: bool = False
    read: int
    tools: list[str]
    tags: list[str]
    portfolio_files: list[PortfolioFileSchema]
    created_at: datetime
    simple_appreciates_count: int = 0
    senior_appreciates_count: int = 0


@router.get(
    '/{portfolio_id}',
    summary='Get portfolio detail',
    response_model=PortfolioFullSchema,
    status_code=status.HTTP_200_OK,
)
async def get_portfolio(
    portfolio_id: int,
    auth_user: OptionalAuthUser,
    db: DbDependency,
):
    """get user portfolio route"""
    builder = PortfolioQueryBuilder(
        auth_user_username=auth_user and auth_user.username,  # type: ignore[arg-type]
        portfolio_id=portfolio_id,
    )

    portfolio: Portfolio = db.execute(await builder.full()).unique()

    print(portfolio.portfolio_files) # [<models.portfolio.PortfolioFile object at 0x7f5f0a5d8a50>]
    print(portfolio.created_at) # 2024-05-22 09:20:11.749022+00:00

    portfolio.simple_appreciates_count, portfolio.senior_appreciates_count = 4, 5  # type: ignore[attr-defined]

    portfolio.read += 1
    db.add(portfolio)
    db.commit()

    return PortfolioFullSchema.model_validate(portfolio, from_attributes=True)

builder.full()
来到:

select(Portfolio).where(Portfolio.id == self.portfolio_id)
.options(joinedload(Portfolio.portfolio_files).joinedload(PortfolioFile.file))
.options(undefer(Portfolio.created_at))

其生成的SQL如下(对于id=7):

select
    user_profile.id,
    user_profile.user_username,
    user_profile.is_senior,
    user_profile.job_title,
    user_profile.company_name,
    user_profile.location,
    user_profile.biography,
    user_profile.social_profiles,
    user_profile.cover_photo,
    user_profile.open_to_work,
    user_user.username,
    user_user.id as id_1,
    user_user.first_name,
    user_user.last_name,
    user_user.email,
    user_user.avatar,
    user_user.password,
    user_user.user_type,
    user_user.is_active,
    user_user.last_login,
    portfolio_owner.id as id_2,
    portfolio_owner.portfolio_id,
    portfolio_owner.user_username as user_username_1,
    portfolio_portfolio.id as id_3,
    portfolio_portfolio.user_username as user_username_2,
    portfolio_portfolio.title,
    portfolio_portfolio.description,
    portfolio_portfolio.cover_image,
    portfolio_portfolio.category,
    portfolio_portfolio.read,
    portfolio_portfolio.tools,
    portfolio_portfolio.tags,
    portfolio_portfolio.created_at,
    core_file_1.id as id_4,
    core_file_1.name,
    core_file_1.original_name,
    core_file_1.url,
    core_file_1.mime_type,
    portfolio_file_1.id as id_5,
    portfolio_file_1.portfolio_id as portfolio_id_1,
    portfolio_file_1.file_id,
    portfolio_file_1."order",
    portfolio_file_1.slideshow
from
    portfolio_portfolio
join portfolio_owner on
    portfolio_portfolio.id = portfolio_owner.portfolio_id
join user_user on
    user_user.username = portfolio_owner.user_username
join user_profile on
    user_user.username = user_profile.user_username
left outer join portfolio_file as portfolio_file_1 on
    portfolio_portfolio.id = portfolio_file_1.portfolio_id
left outer join core_file as core_file_1 on
    core_file_1.id = portfolio_file_1.file_id
where
    portfolio_portfolio.id = 7
order by
    portfolio_owner.id

主代码中的两个打印工作正常:它们不会生成额外的 SQL,并且字段按原样加载,显示正确的结果。但是,当我执行

model_validate
时,会运行两个额外的 SQL:

select
    portfolio_portfolio.id as portfolio_portfolio_id,
    portfolio_portfolio.user_username as portfolio_portfolio_user_username,
    portfolio_portfolio.title as portfolio_portfolio_title,
    portfolio_portfolio.description as portfolio_portfolio_description,
    portfolio_portfolio.cover_image as portfolio_portfolio_cover_image,
    portfolio_portfolio.category as portfolio_portfolio_category,
    portfolio_portfolio.read as portfolio_portfolio_read,
    portfolio_portfolio.tools as portfolio_portfolio_tools,
    portfolio_portfolio.tags as portfolio_portfolio_tags,
    core_file_1.id as core_file_1_id,
    core_file_1.name as core_file_1_name,
    core_file_1.original_name as core_file_1_original_name,
    core_file_1.url as core_file_1_url,
    core_file_1.mime_type as core_file_1_mime_type,
    portfolio_file_1.id as portfolio_file_1_id,
    portfolio_file_1.portfolio_id as portfolio_file_1_portfolio_id,
    portfolio_file_1.file_id as portfolio_file_1_file_id,
    portfolio_file_1."order" as portfolio_file_1_order,
    portfolio_file_1.slideshow as portfolio_file_1_slideshow
from
    portfolio_portfolio
left outer join portfolio_file as portfolio_file_1 on
    portfolio_portfolio.id = portfolio_file_1.portfolio_id
left outer join core_file as core_file_1 on
    core_file_1.id = portfolio_file_1.file_id
where
    portfolio_portfolio.id = 7

并且

select
    portfolio_portfolio.created_at as portfolio_portfolio_created_at
from
    portfolio_portfolio
where
    portfolio_portfolio.id = 7

但是,不应触发这些数据,因为

portfolio
对象已经包含这些数据。

python sqlalchemy fastapi pydantic
1个回答
0
投票

您可以给出联合类型提示,在特定类型旁边也允许 None。

© www.soinside.com 2019 - 2024. All rights reserved.