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

在SQLAlchemy ORM中使用__getattr__引发RecursionError的解决咨询

问题根源

递归错误的核心原因是:SQLAlchemy的关联属性(比如raw_whatsit)是通过动态机制生成的,并非AppWhatsit类的直接属性。当你重写__getattr__后,所有未找到的属性访问都会触发这个方法——包括访问self.raw_whatsit的时候。这就导致在__getattr__里调用self.raw_whatsit时,又会再次触发__getattr__,形成无限递归循环。

修复方案

在__getattr__中优先处理raw_whatsit属性的访问,避免它被递归拦截;同时仅当属性不存在于当前实例时,才转发到关联的raw_whatsit对象。

修复后的完整代码

from sqlalchemy import Column, Integer, String, ForeignKey, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker

Base = declarative_base()

class RawWhatsit(Base):
    __tablename__ = 'raw_whatsit'
    id = Column(Integer, primary_key=True)
    some_attribute = Column(String(50))
    another_attribute = Column(String(50))

class AppWhatsit(Base):
    __tablename__ = 'app_whatsit'
    id = Column(Integer, primary_key=True)
    raw_whatsit_id = Column(Integer, ForeignKey('raw_whatsit.id'))
    raw_whatsit = relationship("RawWhatsit")
    cached_calculation = Column(String(100))

    def __getattr__(self, name):
        # 优先处理raw_whatsit本身,避免递归调用
        if name == 'raw_whatsit':
            return super().__getattribute__(name)
        
        # 尝试从关联的原始对象获取属性
        try:
            return getattr(self.raw_whatsit, name)
        except AttributeError:
            # 原始对象也无该属性时,抛出标准错误
            raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

# 测试逻辑
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()

# 插入测试数据
raw = RawWhatsit(some_attribute="test_value", another_attribute="extra_value")
session.add(raw)
session.commit()

app = AppWhatsit(raw_whatsit_id=raw.id, cached_calculation="precomputed_result")
session.add(app)
session.commit()

# 直接访问原始对象属性
print(app.some_attribute)  # 输出: test_value
print(app.another_attribute)  # 输出: extra_value
# 正常访问自身属性和关联对象
print(app.cached_calculation)  # 输出: precomputed_result
print(app.raw_whatsit.some_attribute)  # 输出: test_value

额外优化

如果需要避免误转发SQLAlchemy内部属性(以下划线开头的属性),可以在__getattr__开头增加判断:

def __getattr__(self, name):
    # 跳过内部属性,避免干扰SQLAlchemy机制
    if name.startswith('_'):
        raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
    
    if name == 'raw_whatsit':
        return super().__getattribute__(name)
    
    try:
        return getattr(self.raw_whatsit, name)
    except AttributeError:
        raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 16:03:19