fastapi: sqlalchemy:在model中增加需要查询数据的属性
一,功能说明:
用户表users,用户头像的地址放在assets表中, 需要查询得到:
在异步 SQLAlchemy (AsyncSession) 环境下,由于 Python 的 @property 只能是同步方法,我们绝对不能在 @property 内部使用 await 去执行数据库查询。
在异步架构中,最标准、性能最好的做法是:利用 relationship 配合 selectinload(或 joinedload)在预加载数据后,再通过同步的 @property 或 Pydantic 转换为默认值。
所以这个问题的解决稍微麻烦一些,因为需要修改查询条件
在sql中使用 selectinload 预加载关联的 avatar_asset,这一步如果不做,则我们所加的属性会触发异常 :
例如:
'User.avatar_asset' is not available due to lazy='raise'
二,代码:
User类
# 用户模型
class User(MyBaseModel):
__tablename__ = "users"
username: Mapped[str] = mapped_column(String(100),server_default="''", comment='用户名')
nickname: Mapped[str] = mapped_column(String(100), server_default="''", comment='昵称')
password: Mapped[str] = mapped_column(String(100), server_default="''", comment='密码')
avatar_id: Mapped[Optional[BigInteger]] = mapped_column(ForeignKey("assets.id"), nullable=True)
# 1. 建立异步关联关系
# 使用 lazy="raise" 可以防止在未预加载时误调用导致报错,强迫编写高性能代码
avatar_asset: Mapped[Optional["Asset"]] = relationship("Asset", lazy="raise")
# 2. 定义同步 property
@property
def avatar_url(self) -> str:
DEFAULT_AVATAR = "https://example.com/default.png"
try:
# 如果已经预加载了关联数据,直接读取
if self.avatar_asset:
return self.avatar_asset.full_path
else:
return DEFAULT_AVATAR
except Exception as err:
# 如果没有预加载(触发了 lazy="raise" 异常),转为返回默认值或抛出特定说明
print(err)
return DEFAULT_AVATAR
调用:
async with AsyncSessionLocal() as db:
# 1. 查询用户是否存在
stmt = select(User).where(User.username == current_user['username']).options(selectinload(User.avatar_asset))
result = await db.execute(stmt)
user = result.scalar_one_or_none()
print("user:",user)
print("avatar_url:", user.avatar_url)
浙公网安备 33010602011771号