QSqlQueryModel如何实现行记录的添加与删除操作
QSqlQueryModel实现行增删的方案
QSqlQueryModel默认是只读模型,基类提供的insertRow()、removeRow()方法没有内置数据库持久化和视图刷新逻辑,直接调用不会生效,需要先在自定义模型中重写核心方法,再绑定按钮交互逻辑即可。
第一步:补全自定义模型的实现
先在SqlQueryModelBorrowingUsers类中重写必要的虚函数,实现数据操作、权限控制、数据刷新逻辑:
// sqlquerymodelborrowingusers.h #include <QSqlQueryModel> #include <QSqlDatabase> #include <QModelIndex> class SqlQueryModelBorrowingUsers : public QSqlQueryModel { Q_OBJECT public: explicit SqlQueryModelBorrowingUsers(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase::database()); Qt::ItemFlags flags(const QModelIndex &index) const override; bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; void refreshModel(const QString &customQuery = QString()); private: QSqlDatabase m_db; QString m_baseQuery; };
// sqlquerymodelborrowingusers.cpp #include "sqlquerymodelborrowingusers.h" #include <QSqlQuery> #include <QSqlError> #include <QDebug> SqlQueryModelBorrowingUsers::SqlQueryModelBorrowingUsers(QObject *parent, QSqlDatabase db) : QSqlQueryModel(parent), m_db(db) { // 替换为你自己业务的基础查询语句 m_baseQuery = "SELECT id, user_name, book_id, borrow_date FROM borrowing_records"; refreshModel(); } Qt::ItemFlags SqlQueryModelBorrowingUsers::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QSqlQueryModel::flags(index); if (index.isValid()) { return defaultFlags | Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled; } return defaultFlags; } bool SqlQueryModelBorrowingUsers::insertRows(int row, int count, const QModelIndex &parent) { if (count < 1 || row < 0 || row > rowCount()) return false; beginInsertRows(parent, row, row + count - 1); QSqlQuery insertQuery(m_db); // 替换为你自己表的INSERT语句,初始值按业务需求设置默认值 bool opOk = insertQuery.exec("INSERT INTO borrowing_records (user_name, book_id, borrow_date) " "VALUES ('', 0, NOW())"); if (!opOk) { qDebug() << "插入记录失败:" << insertQuery.lastError().text(); endInsertRows(); return false; } endInsertRows(); refreshModel(); return true; } bool SqlQueryModelBorrowingUsers::removeRows(int row, int count, const QModelIndex &parent) { if (count < 1 || row < 0 || row + count > rowCount()) return false; beginRemoveRows(parent, row, row + count - 1); for (int i = row; i < row + count; i++) { // 按你的表结构,取当前行主键值,示例中第一列为id int recordId = data(index(i, 0)).toInt(); QSqlQuery delQuery(m_db); // 替换为你自己表的DELETE语句 delQuery.prepare("DELETE FROM borrowing_records WHERE id = ?"); delQuery.addBindValue(recordId); bool opOk = delQuery.exec(); if (!opOk) { qDebug() << "删除第" << i << "行失败:" << delQuery.lastError().text(); endRemoveRows(); return false; } } endRemoveRows(); refreshModel(); return true; } bool SqlQueryModelBorrowingUsers::setData(const QModelIndex &index, const QVariant &value, int role) { if (role != Qt::EditRole || !index.isValid()) return false; int recordId = data(index(index.row(), 0)).toInt(); QString colName; // 按你的查询返回列顺序,匹配对应数据库字段名 switch (index.column()) { case 1: colName = "user_name"; break; case 2: colName = "book_id"; break; case 3: colName = "borrow_date"; break; default: return false; } QSqlQuery updateQuery(m_db); updateQuery.prepare(QString("UPDATE borrowing_records SET %1 = ? WHERE id = ?").arg(colName)); updateQuery.addBindValue(value); updateQuery.addBindValue(recordId); bool opOk = updateQuery.exec(); if (opOk) { refreshModel(); emit dataChanged(index, index, {role}); return true; } qDebug() << "更新数据失败:" << updateQuery.lastError().text(); return false; } void SqlQueryModelBorrowingUsers::refreshModel(const QString &customQuery) { if (!customQuery.isEmpty()) m_baseQuery = customQuery; setQuery(m_baseQuery, m_db); // 自定义表头显示文本,按需修改 setHeaderData(0, Qt::Horizontal, "记录ID"); setHeaderData(1, Qt::Horizontal, "借阅用户"); setHeaderData(2, Qt::Horizontal, "书籍ID"); setHeaderData(3, Qt::Horizontal, "借阅日期"); }
第二步:绑定窗口按钮的交互逻辑
在BorrowingUsersWindow的初始化逻辑中,给两个按钮绑定点击槽,直接调用模型继承来的insertRow()、removeRow()即可:
// 在BorrowingUsersWindow构造函数中,完成控件初始化后添加以下逻辑 BorrowingUsersWindow::BorrowingUsersWindow(QWidget *parent) : QDialog(parent) { // 原有界面初始化逻辑:创建控件、设置布局等 // ... myModel = new SqlQueryModelBorrowingUsers(this); tblViewUserData->setModel(myModel); // 可选:隐藏业务不需要展示的主键列 tblViewUserData->hideColumn(0); // 新增记录按钮逻辑 connect(bttAddRecord, &QPushButton::clicked, this, [this](){ int insertPos = myModel->rowCount(); // 直接调用基类的insertRow,内部会自动触发重写的insertRows逻辑 myModel->insertRow(insertPos); // 可选:插入后自动定位到新行并进入编辑状态 QModelIndex newEditIndex = myModel->index(insertPos, 1); tblViewUserData->scrollTo(newEditIndex); tblViewUserData->edit(newEditIndex); }); // 删除记录按钮逻辑 connect(bttRemoveRecord, &QPushButton::clicked, this, [this](){ QModelIndexList selected = tblViewUserData->selectionModel()->selectedRows(); if (selected.isEmpty()) { QMessageBox::warning(this, "提示", "请先选择要删除的记录"); return; } // 从后往前删除,避免行号偏移导致删错数据 for (int i = selected.size() - 1; i >= 0; i--) { int delRow = selected[i].row(); // 直接调用基类的removeRow,内部会自动触发重写的removeRows逻辑 myModel->removeRow(delRow); } }); }
注意事项
- 示例中的SQL语句、表名、字段名需要替换为你自己项目的实际结构,示例仅做逻辑演示。
- 如果需要做输入校验、批量提交/撤销的编辑缓冲,可以把数据库写入逻辑移到
submitAll()方法中,insertRow先插入内存临时行,用户确认后再持久化到数据库。 - 批量删除行时必须从最大行号往最小行号遍历删除,否则删除前序行后剩余行的索引会偏移,导致越界或删错数据。
- 所有数据库操作建议保留错误日志输出,方便排查问题。
内容的提问来源于stack exchange,提问作者happ
相关产品推荐
相关产品推荐

