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

PyQt5中使用PandasModel显示SQL Server 2019表列表时出现'pyodbc.Cursor'对象无'index'属性错误的解决方法

解决PyQt5 PandasModel结合SQL Server时的'pyodbc.Cursor'无'index'属性错误

你遇到的错误核心原因非常清晰:你将pyodbc.Cursor对象直接传给了PandasModel,但PandasModel的设计是接收Pandas DataFrame作为数据源。

问题分析

当你执行df = sql_conn.execute(query_string)时,返回的是一个pyodbc.Cursor实例——它只是数据库查询结果的迭代器,并非DataFrame。而你的PandasModel类中,多个方法(比如rowCount里的len(self._df.index)、headerData里的self._df.columns)都依赖DataFrame的专属属性,游标对象根本没有这些属性,所以触发了'pyodbc.Cursor' object has no attribute 'index'错误。

你注释掉的pd.read_sql_query才是正确的方向,只是之前可能因为一些小细节没跑通,下面是修正后的完整方案:

修正步骤

  1. 用pd.read_sql_query生成标准DataFrame:这个方法会直接把SQL查询结果转换成符合PandasModel要求的DataFrame,是连接SQL和PyQt表格的正确方式。
  2. 移除不必要的事务提交:查询操作不需要调用commit(),只有修改数据的操作(如INSERT/UPDATE/DELETE)才需要提交事务。
  3. 优化SQL语句与连接逻辑:避免使用use语句,直接在连接字符串中指定目标数据库,减少游标上下文的潜在问题。

修正后的完整代码

from PyQt5 import QtCore, QtGui, QtWidgets
import pyodbc
import pandas as pd

class PandasModel(QtCore.QAbstractTableModel):
    def __init__(self, df = pd.DataFrame(), parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent=parent)
        self._df = df

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return None
        if orientation == QtCore.Qt.Horizontal:
            try:
                return self._df.columns.tolist()[section]
            except IndexError:
                return None
        elif orientation == QtCore.Qt.Vertical:
            try:
                return self._df.index.tolist()[section]
            except IndexError:
                return None

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return None
        if not index.isValid():
            return None
        return str(self._df.iloc[index.row(), index.column()])

    def setData(self, index, value, role):
        row = self._df.index[index.row()]
        col = self._df.columns[index.column()]
        if hasattr(value, 'toPyObject'):
            value = value.toPyObject()
        else:
            dtype = self._df[col].dtype
            if dtype != object:
                value = None if value == '' else dtype.type(value)
        self._df.at[row, col] = value  # 替换过时的set_value方法
        return True

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self._df.index)

    def columnCount(self, parent=QtCore.QModelIndex()):
        return len(self._df.columns)

    def sort(self, column, order):
        colname = self._df.columns.tolist()[column]
        self.layoutAboutToBeChanged.emit()
        self._df.sort_values(colname, ascending= order == QtCore.Qt.AscendingOrder, inplace=True)
        self._df.reset_index(inplace=True, drop=True)
        self.layoutChanged.emit()

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent=None)
        vLayout = QtWidgets.QVBoxLayout(self)
        hLayout = QtWidgets.QHBoxLayout()
        self.pathLE = QtWidgets.QLineEdit(self)
        hLayout.addWidget(self.pathLE)
        self.loadBtn = QtWidgets.QPushButton("load", self)
        hLayout.addWidget(self.loadBtn)
        vLayout.addLayout(hLayout)
        self.tableView = QtWidgets.QTableView(self)
        vLayout.addWidget(self.tableView)
        self.loadBtn.clicked.connect(self.loadData)
        self.tableView.setSortingEnabled(True)

    def loadData(self):
        dbName="AccountDatabase"
        server = '-----'  # 替换成你的实际服务器地址
        username = 'admin'
        password = 'admin#'
        # 直接在连接字符串中指定目标数据库,避免use语句的上下文问题
        sql_conn = pyodbc.connect(
            'DRIVER={ODBC Driver 17 for SQL Server};SERVER=' + server + 
            ';DATABASE=' + dbName + ';UID=' + username + ';PWD=' + password
        )
        query_string= "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'"
        # 用pd.read_sql_query将查询结果转为DataFrame
        df = pd.read_sql_query(query_string, sql_conn)
        sql_conn.close()  # 主动关闭连接,避免资源泄漏
        model = PandasModel(df)
        self.tableView.setModel(model)

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

额外优化说明

  • 替换了Pandas已过时的df.set_value方法为推荐的df.at,提升代码兼容性。
  • 把QtCore.QVariant()替换成None,在Python3版本的PyQt5中,返回None会自动处理为空的QVariant,代码更简洁。
  • 添加了数据库连接关闭操作,养成资源释放的好习惯。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 18:22:49