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

PyQt6中QSqlQueryModel返回正确行数但内容为空及递归错误问题排查

解决QSqlQueryModel自定义data方法的两个问题:值为None与递归错误

咱们把问题拆解开,一步步来解决:

一、为什么最初的代码会出现RecursionError?

这是个典型的循环调用陷阱:QSqlQueryModel的record()方法内部会调用data()方法。你在自定义的data()里调用self.record(r),相当于触发了无限递归——data调用record,record又调用data,循环往复最终抛出递归错误。很多人第一次重写QSqlQueryModel的data方法都会踩这个坑。

二、为什么自定义data方法后返回值都是None?

这个问题和递归陷阱直接相关:用record()获取数据的方式本身就会干扰模型的查询状态,再加上递归导致的逻辑混乱,自然取到的都是None。另外还有个细节:重写data()时如果没有处理所有未覆盖的role,也会影响模型的正常数据读取。

正确的实现方式

咱们换一种安全的方式获取数据,避开record()方法,下面给你两种可行方案:

方案1:直接操作底层QSqlQuery

class ListModel(QSqlQueryModel):
    def __init__(self):
        super().__init__()
        self.setQuery("SELECT id, name, age FROM people")

    def data(self, index, role):
        # 处理我们关心的角色
        if role == Qt.ItemDataRole.DisplayRole:
            query = self.query()
            query.seek(index.row())
            name = query.value("name")
            print(f"row {index.row()} displayrole = {name}")
            return name
        elif role == Qt.ItemDataRole.ToolTipRole:
            query = self.query()
            query.seek(index.row())
            age = query.value("age")
            print(f"row {index.row()} tooltiprole = {age}")
            return age
        # 所有未处理的角色交给父类处理,这一步非常重要!
        return super().data(index, role)

方案2:利用父类data方法获取指定列

这种方式更简洁,不需要直接操作查询对象:

class ListModel(QSqlQueryModel):
    def __init__(self):
        super().__init__()
        self.setQuery("SELECT id, name, age FROM people")
        # 提前获取列索引,避免重复查找
        self.name_col = self.record().indexOf("name")
        self.age_col = self.record().indexOf("age")

    def data(self, index, role):
        if role == Qt.ItemDataRole.DisplayRole:
            # 获取当前行name列的索引对象
            name_index = index.sibling(index.row(), self.name_col)
            name = super().data(name_index, Qt.ItemDataRole.DisplayRole)
            print(f"row {index.row()} displayrole = {name}")
            return name
        elif role == Qt.ItemDataRole.ToolTipRole:
            age_index = index.sibling(index.row(), self.age_col)
            age = super().data(age_index, Qt.ItemDataRole.DisplayRole)
            print(f"row {index.row()} tooltiprole = {age}")
            return age
        # 其他角色交给父类处理
        return super().data(index, role)

关键注意事项

  • 永远不要在data()方法里调用record(),这必然会触发递归。
  • 重写data()时,一定要返回父类对未覆盖角色的处理结果,否则模型的默认行为(比如选中状态、样式渲染)会失效。
  • 方案1中query.seek()的操作是安全的,只要setQuery执行成功,就能定位到有效行。

测试这两种方案后,你会发现ListView能正常显示名称,鼠标悬停也能看到年龄提示,print输出的都是正确数据,不会再出现None或递归错误了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 06:39:23