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

如何在Flask SQLAlchemy的Player模型中直接访问嵌套属性?

问题:Flask-SQLAlchemy模型属性代理实现(避免递归错误)

我有一个名为Player的Flask模型,代码如下:

class Player:
    id = Column(Integer, primary_key=True)
    kills = Column(Integer)
    bot_id = Column(Integer,ForeignKey("bots.id"))
    bot = relationship("Bot",back_populates="player")
    user_id = Column(Integer,ForeignKey("users.id"))
    user = relationship("User",back_populates="player")
    player_type = Column(String) # BOT or USER

Bot和User模型各自拥有独立属性,由于前端依赖,我希望直接访问这些属性而非通过关联属性,例如用player.screen_name替代player.bot.screen_name或player.user.screen_name。

我可以通过以下代码实现,但因属性众多,不想逐个编写:

@property
def screen_name(self):
    if self.player_type == "BOT":
        return self.bot.screen_name
    elif self.player_type == "USER":
        return self.user.screen_name
    else:
        return None

于是我尝试了以下代码:

def __getattr__(self, attr):
    if hasattr(self, attr):
        return getattr(self, attr)
    elif self.player_type == "BOT" and hasattr(self.bot, attr):
        return getattr(self.bot, attr)
    elif self.player_type == "USER" and hasattr(self.user, attr):
        return getattr(self.user, attr)
    else:
        raise AttributeError(f"'{type(self).__name__}' object has no attribute '{attr}'")

但该代码因getattr导致递归错误,请问有没有其他实现方式?


解决方案

递归错误原因

__getattr__仅在实例找不到指定属性时才会被调用,而你代码里的hasattr(self, attr)会触发getattr检查,当属性不存在时,又会递归调用__getattr__,形成无限循环。

方法1:改进__getattr__避免递归

直接通过实例的__dict__和SQLAlchemy的模型映射器检查属性,跳过hasattr的触发:

def __getattr__(self, attr):
    # 检查实例自身已加载的属性或模型定义的列
    if attr in self.__dict__ or attr in self.__mapper__.columns:
        return object.__getattribute__(self, attr)
    # 根据player_type获取目标关联模型
    target = self.bot if self.player_type == "BOT" else self.user
    # 检查目标模型是否存在该属性
    if target and hasattr(target, attr):
        return getattr(target, attr)
    # 抛出标准属性错误
    raise AttributeError(f"'{type(self).__name__}' object has no attribute '{attr}'")

注意:添加target的非空判断,避免关联未加载时引发异常;self.__mapper__.columns用来检测模型定义的所有列属性。

方法2:批量生成属性代理

如果不想用__getattr__全局转发,可批量生成@property代理,只代理指定属性:

def proxy_property(attr_name):
    @property
    def prop(self):
        if self.player_type == "BOT":
            return getattr(self.bot, attr_name) if self.bot else None
        elif self.player_type == "USER":
            return getattr(self.user, attr_name) if self.user else None
        return None
    return prop

class Player:
    # 原有模型定义...
    id = Column(Integer, primary_key=True)
    kills = Column(Integer)
    bot_id = Column(Integer,ForeignKey("bots.id"))
    bot = relationship("Bot",back_populates="player")
    user_id = Column(Integer,ForeignKey("users.id"))
    user = relationship("User",back_populates="player")
    player_type = Column(String) # BOT or USER

# 列出需要代理的属性列表
proxy_attrs = ["screen_name", "level", "avatar_url"]
# 批量给Player类添加代理属性
for attr in proxy_attrs:
    setattr(Player, attr, proxy_property(attr))

这种方式更可控,避免意外转发无关属性。

方法3:SQLAlchemy混合属性(支持查询场景)

如果需要在SQL查询中也能使用这些代理属性,用hybrid_property实现实例访问和SQL表达式的统一:

from sqlalchemy.ext.hybrid import hybrid_property

class Player:
    # 原有模型定义...
    id = Column(Integer, primary_key=True)
    kills = Column(Integer)
    bot_id = Column(Integer,ForeignKey("bots.id"))
    bot = relationship("Bot",back_populates="player")
    user_id = Column(Integer,ForeignKey("users.id"))
    user = relationship("User",back_populates="player")
    player_type = Column(String) # BOT or USER

    @hybrid_property
    def screen_name(self):
        if self.player_type == "BOT":
            return self.bot.screen_name if self.bot else None
        elif self.player_type == "USER":
            return self.user.screen_name if self.user else None
        return None

    @screen_name.expression
    def screen_name(cls):
        from sqlalchemy import case
        # 编写SQL层面的条件映射
        return case(
            [(cls.player_type == "BOT", Bot.screen_name)],
            else_=User.screen_name
        )

这种方式既支持实例直接访问,也能在Player.query.filter(Player.screen_name == "xxx")这类查询中生效。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:27:39