在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
相关产品推荐
相关产品推荐

