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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 11:21:43