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

