SQLModel中JOIN的正确用法及关联模型Relationship使用疑问
问题描述
我有User和UserRole两个SQLModel模型,想要查询用户数据时同时获取关联的用户角色信息,目前用以下代码实现了需求:
实现代码
@staticmethod async def get_users(session: AsyncSession): users = await session.execute( select(User, UserRole).join(UserRole, UserRole.role_id == User.role_id) ) result = users.all() users = [] for user in result: users.append({**user.User.dict(), "role": user.UserRole}) return users
模型定义
class User(SQLModel, table=True): user_id: Optional[int] = Field(default=None, primary_key=True, nullable=False) username: str role_id: Optional[int] = Field(default=3, foreign_key="user_role.role_id") class UserRole(SQLModel, table=True): __tablename__ = "user_role" role_id: Optional[int] = Field(default=None, primary_key=True, nullable=False) role_name: str
当前返回结果
[{ "user_id": 1, "username": "admin", "role_id": 1, "role": { "role_id": 1, "role_name": "admin" } }, { "user_id": 2, "username": "user", "role_id": 2, "role": { "role_id": 2, "role_name": "user" } }]
代码能正常运行,但我不确定这是否为最优实现,且未使用SQLModel文档中提到的Relationship字段,想请教三个问题:
- 这样的实现是否可行?
- 为何需要使用
Relationship? - 是否有无需循环处理结果的更简洁写法?
解答
1. 你的实现完全可行
当前代码通过手动关联查询+循环组装结果的方式,确实能正确获取用户和对应角色的信息,逻辑清晰且能满足需求。只要数据量不大,这种写法不会有明显性能问题,完全可以继续使用。
2. 为什么要使用Relationship?
Relationship是SQLModel(基于SQLAlchemy)提供的ORM关联工具,核心优势如下:
- 自动关联与懒加载:定义后无需每次手动写
join语句,ORM会自动处理关联逻辑;默认支持懒加载,需要时才查询关联数据,减少不必要的数据库请求。 - 对象化操作更便捷:可以直接通过
user.role访问角色对象,不用手动组装字典。比如修改角色时,直接给user.role赋值新的UserRole对象即可,ORM会自动处理外键更新。 - 避免重复代码:如果多个业务场景需要查询用户+角色的关联数据,用
Relationship不用每次都写重复的join和组装逻辑。 - 支持复杂关联:面对一对多、多对多这类复杂关系,手动写
join会非常繁琐,Relationship可以轻松处理这类场景。
3. 无需循环的简洁写法(两种方案)
方案一:使用Relationship字段优化模型与查询
先修改模型添加关联字段:
from sqlmodel import Relationship class User(SQLModel, table=True): user_id: Optional[int] = Field(default=None, primary_key=True, nullable=False) username: str role_id: Optional[int] = Field(default=3, foreign_key="user_role.role_id") role: "UserRole" = Relationship(back_populates="users") # 定义正向关联 class UserRole(SQLModel, table=True): __tablename__ = "user_role" role_id: Optional[int] = Field(default=None, primary_key=True, nullable=False) role_name: str users: list["User"] = Relationship(back_populates="role") # 反向关联(可选,按需添加)
然后查询时直接预加载关联数据,无需手动join和循环:
from sqlalchemy.orm import selectinload @staticmethod async def get_users(session: AsyncSession): # 使用selectinload预加载关联的role数据,避免N+1查询 users = await session.exec( select(User).options(selectinload(User.role)) ) # 直接转换为带role的字典列表 return [user.dict() for user in users.all()]
注:
selectinload是预加载方式,会一次性查询所有关联的角色数据,比懒加载更适合批量查询场景,能避免多次数据库请求。
方案二:不使用Relationship,用row_mapping简化结果处理
如果不想修改模型,可借助SQLAlchemy的row_mapping功能优化结果处理:
from sqlalchemy.orm import row_mapping @staticmethod async def get_users(session: AsyncSession): query = select(User, UserRole).join(UserRole, UserRole.role_id == User.role_id) # 使用row_mapping将每行结果映射为结构化对象 result = await session.execute(query.options(row_mapping())) # 直接生成目标格式的字典列表 return [{**row.User.dict(), "role": row.UserRole} for row in result.all()]
这个写法本质还是手动关联,但
row_mapping让结果访问更清晰,不过整体简洁度仍不如Relationship方案。
内容的提问来源于stack exchange,提问作者Or Nakash
相关产品推荐
相关产品推荐

