You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI+SQLAlchemy ORM获取关联表字段的正确方法及报错解决

解决方案

方法一:直接查询所需字段

不需要查询整个关联表模型,只把需要的城市、州、国家名称字段加入查询,这样返回的结果可以直接处理成目标结构,避免Row对象的序列化问题:

query = (
    db.query(
        UserModel,
        CityModel.name.label("city_name"),
        StateModel.name.label("state_name"),
        CountryModel.name.label("country_name")
    )
    .join(UserDocumentModel)
    .join(UserAddressModel, UserAddressModel.user_id == UserModel.id)
    .join(CityModel, CityModel.id == UserAddressModel.city_id)
    .join(StateModel, StateModel.id == CityModel.state_id)
    .join(CountryModel, CountryModel.id == StateModel.country_id)
    .filter(
        UserModel.is_deleted == is_deleted,
        UserAddressModel.is_deleted == 0
    )
)

results = query.all()

拿到结果后,每个元素是(UserModel实例, 城市名, 州名, 国家名)的元组,可手动组装成所需结构:

user_list = []
for user, city_name, state_name, country_name in results:
    user_data = {k: v for k, v in user.__dict__.items() if not k.startswith("_")}
    user_data.update({
        "city_name": city_name,
        "state_name": state_name,
        "country_name": country_name
    })
    user_list.append(user_data)

如果用Pydantic,也可以直接定义包含这些字段的模型来接收转换。

方法二:利用ORM关系预加载

在模型中定义好关联关系,查询时通过joinedload预加载关联数据,直接通过UserModel的属性访问关联名称,代码更简洁:

第一步:定义模型间的关联关系

# UserModel
class UserModel(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    is_deleted = Column(Integer, default=0)
    # 假设用户与地址为一对一关系
    address = relationship("UserAddressModel", uselist=False, back_populates="user")

# UserAddressModel
class UserAddressModel(Base):
    __tablename__ = "user_addresses"
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey("users.id"))
    city_id = Column(Integer, ForeignKey("cities.id"))
    is_deleted = Column(Integer, default=0)
    user = relationship("UserModel", back_populates="address")
    city = relationship("CityModel", back_populates="addresses")

# CityModel
class CityModel(Base):
    __tablename__ = "cities"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    state_id = Column(Integer, ForeignKey("states.id"))
    addresses = relationship("UserAddressModel", back_populates="city")
    state = relationship("StateModel", back_populates="cities")

# StateModel
class StateModel(Base):
    __tablename__ = "states"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    country_id = Column(Integer, ForeignKey("countries.id"))
    cities = relationship("CityModel", back_populates="state")
    country = relationship("CountryModel", back_populates="states")

# CountryModel
class CountryModel(Base):
    __tablename__ = "countries"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    states = relationship("StateModel", back_populates="country")

第二步:查询时预加载关联数据

用joinedload一次性加载所有关联层级,避免N+1查询问题:

from sqlalchemy.orm import joinedload

query = (
    db.query(UserModel)
    .join(UserDocumentModel)
    .options(
        joinedload(UserModel.address)
        .joinedload(UserAddressModel.city)
        .joinedload(CityModel.state)
        .joinedload(StateModel.country)
    )
    .filter(
        UserModel.is_deleted == is_deleted,
        UserAddressModel.is_deleted == 0
    )
)

results = query.all()

现在可直接通过实例属性获取名称:

for user in results:
    city_name = user.address.city.name
    state_name = user.address.city.state.name
    country_name = user.address.city.state.country.name

这种方式符合ORM使用习惯,返回的都是Model实例,序列化到Pydantic也更方便。


内容的提问来源于stack exchange,提问作者Ronak Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 21:43:24