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

SQLAlchemy序列化父子对象:懒加载相关返回异常问题

问题分析与解决方案

你调试时能看到User对象中已填充domainName子对象,说明SQLAlchemy的lazy=False已经生效,问题出在对象转JSON的序列化环节——默认的序列化工具不会自动识别并包含SQLAlchemy的relationship关联字段。

以下是几种可行的解决方法:

方法1:使用Pydantic模型(推荐,适配FastAPI等现代框架)

通过定义嵌套的Pydantic Schema,显式声明要包含的关联字段,确保序列化时能完整输出数据:

from pydantic import BaseModel, ConfigDict
from typing import List

# 定义DomainName的序列化模型
class DomainNameSchema(BaseModel):
    domain_id: int
    user_id: str  # 匹配User表的主键类型(email是字符串)
    domain_name: str

    model_config = ConfigDict(from_attributes=True)  # 支持从SQLAlchemy对象导入数据

# 定义User的序列化模型,嵌套DomainNameSchema
class UserSchema(BaseModel):
    email: str
    username: str
    password: bytes  # BYTEA类型会自动序列化为base64字符串
    firstname: str
    lastname: str
    domainName: List[DomainNameSchema]  # 显式包含关联字段

    model_config = ConfigDict(from_attributes=True)

在服务中返回时,将SQLAlchemy对象转换为Pydantic模型:

user_obj = get_data(session, User, {"email": "r@tester.com"})
return UserSchema.model_validate(user_obj).model_dump()

方法2:在SQLAlchemy模型中添加自定义序列化方法

直接在User类中编写to_dict方法,手动处理关联字段的序列化:

class User(Base):
    __tablename__ = "user_account"

    # 现有字段定义...

    domainName = relationship("DomainName", cascade="all, delete-orphan", backref="user_account", lazy=False)

    def to_dict(self):
        return {
            "email": self.email,
            "username": self.username,
            "password": self.password.decode('utf-8') if self.password else "",
            "firstname": self.firstname,
            "lastname": self.lastname,
            "domainName": [
                {
                    "domain_id": d.domain_id,
                    "user_id": d.user_id,
                    "domain_name": d.domain_name
                } for d in self.domainName
            ]
        }

返回时调用该方法:

user_obj = get_data(session, User, {"email": "r@tester.com"})
return user_obj.to_dict()

方法3:自定义JSON编码器(适配标准json模块)

如果直接使用Python标准库的json.dumps,需要自定义编码器来处理SQLAlchemy对象和关联字段:

import json
from sqlalchemy.ext.declarative import DeclarativeMeta

class AlchemyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj.__class__, DeclarativeMeta):
            # 处理SQLAlchemy模型对象
            fields = {}
            for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
                data = obj.__getattribute__(field)
                try:
                    json.dumps(data)
                    fields[field] = data
                except TypeError:
                    # 递归处理关联的列表对象
                    if isinstance(data, list):
                        fields[field] = [self.default(item) for item in data]
            return fields
        return super().default(obj)

使用时:

user_obj = get_data(session, User, {"email": "r@tester.com"})
return json.dumps(user_obj, cls=AlchemyEncoder)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 10:15:27