如何用SQLAlchemy的.join()方法获取多表关联数据?
解决SQLAlchemy关联查询后无法直接访问关联表字段的问题
嘿,我看你遇到了个典型的SQLAlchemy ORM使用问题——虽然用了.join()做表连接,但查询返回的User对象没法直接访问Areas或UserGroups的字段,对吧?这核心原因是你只给模型加了外键,却没定义ORM关系(relationship),SQLAlchemy不知道怎么把关联表的数据映射到User对象上。下面我给你一步步解决:
1. 先给模型补上ORM关系定义
修改你的模型类,添加relationship来建立表之间的关联,这样SQLAlchemy就能把关联表的数据绑定到User对象的属性上:
class User(db.Model): user_id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(20), unique=True) usergroup = db.Column(db.Integer, db.ForeignKey('user_group.group_id'), nullable=False) area = db.Column(db.Integer, db.ForeignKey('areas.area_id'), nullable=False) # 新增关联关系,给User对象加两个属性:user_group和area_info user_group = db.relationship('UserGroups', backref='users', foreign_keys=[usergroup]) area_info = db.relationship('Areas', backref='users', foreign_keys=[area]) class UserGroups(db.Model): __tablename__ = 'user_group' # 划重点!你的外键指向的是'user_group'表,必须指定这个表名 id = db.Column(db.Integer, primary_key=True) group_id = db.Column(db.Integer, nullable=False, unique=True) group_name = db.Column(db.String(64), nullable=False, unique=True) class Areas(db.Model): __tablename__ = 'areas' id = db.Column(db.Integer, primary_key=True) area_id = db.Column(db.Integer, nullable=False, unique=True) area_name = db.Column(db.String(64), nullable=False, unique=True)
这里有两个细节要注意:
UserGroups必须指定__tablename__ = 'user_group',因为你User里的外键是db.ForeignKey('user_group.group_id'),SQLAlchemy要匹配这个表名才能找到关联relationship里的foreign_keys是用来指定对应的外键字段,防止多个外键时出现歧义;backref是反向引用,比如之后你可以用some_group.users拿到这个组下的所有用户
2. 修改查询,轻松访问关联字段
现在模型有了关系,你有两种方式获取关联数据:
方式一:懒加载(默认行为)
直接用你原来的查询语句就行,当你访问a.area_info时,SQLAlchemy会自动帮你执行额外查询拿到Areas的数据:
a = User.query.filter(User.user_id==1).join(UserGroups, User.usergroup==UserGroups.group_id).join(Areas, User.area==Areas.area_id).first() # 现在就能像你期望的那样访问了 print(a.area_info.area_name) # 拿到区域名称 print(a.user_group.group_name) # 拿到用户组名称
方式二:预加载(推荐,避免N+1查询)
如果想一次性把所有关联数据都查出来,避免后续多次查询数据库,可以用joinedload:
from sqlalchemy.orm import joinedload a = User.query.filter(User.user_id==1)\ .options(joinedload(User.user_group), joinedload(User.area_info))\ .first() # 访问方式完全一样 print(a.area_info.area_name) print(a.user_group.group_name)
这种方式会生成一个包含所有表的JOIN查询,一次性把所有需要的数据都加载进来,性能更好,尤其是查询多个用户的时候。
补充:如果不想改模型,也可以用with_entities直接取字段
要是你暂时不想修改模型,也可以直接指定要查询的字段,返回一个元组:
a = User.query.filter(User.user_id==1)\ .join(UserGroups, User.usergroup==UserGroups.group_id)\ .join(Areas, User.area==Areas.area_id)\ .with_entities(User, UserGroups.group_name, Areas.area_name)\ .first() # 这样访问: print(a[0].name) # User对象的name字段 print(a[1]) # 用户组名称 print(a[2]) # 区域名称
不过这种方式返回的不是单一的User对象,而是元组,不如定义ORM关系后访问起来顺手。
内容的提问来源于stack exchange,提问作者Carl
相关产品推荐
相关产品推荐

