PyQt技术问询:QSql模型多表操作、插入及性能优化问题
问题1:在线数据处理场景优化方案
针对QSqlQueryModel每次调用setQuery()引发全量重绘、GUI性能下降的问题,可通过自定义子类实现增量更新,避免全量查询与重绘:
核心思路
放弃每次新增记录时全量执行setQuery(),改为手动向模型追加单条/批量记录,仅触发新增行的局部重绘,同时同步写入数据库。
自定义子类实现示例
#include <QSqlQueryModel> #include <QSqlRecord> #include <QSqlQuery> class CustomSqlQueryModel : public QSqlQueryModel { Q_OBJECT public: explicit CustomSqlQueryModel(QObject *parent = nullptr) : QSqlQueryModel(parent) {} // 追加单条记录(同时写入数据库) bool appendRecord(const QSqlRecord &record) { // 1. 先写入数据库 QSqlQuery query; QString insertSql = QString("INSERT INTO 你的表名 (%1) VALUES (%2)") .arg(record.fieldNames().join(",")) .arg(QStringList().fill("?", record.count()).join(",")); query.prepare(insertSql); for (int i = 0; i < record.count(); ++i) { query.addBindValue(record.value(i)); } if (!query.exec()) return false; // 2. 增量更新模型,仅重绘新增行 beginInsertRows(QModelIndex(), rowCount(), rowCount()); insertRow(rowCount()); for (int col = 0; col < record.count(); ++col) { setData(index(rowCount()-1, col), record.value(col)); } endInsertRows(); return true; } // 批量追加记录 bool appendRecords(const QList<QSqlRecord> &records) { if (records.isEmpty()) return true; // 1. 批量写入数据库(开启事务提升效率) QSqlDatabase::database().transaction(); bool allOk = true; for (const auto &record : records) { QSqlQuery query; QString insertSql = QString("INSERT INTO 你的表名 (%1) VALUES (%2)") .arg(record.fieldNames().join(",")) .arg(QStringList().fill("?", record.count()).join(",")); query.prepare(insertSql); for (int i = 0; i < record.count(); ++i) { query.addBindValue(record.value(i)); } if (!query.exec()) { allOk = false; break; } } if (allOk) { QSqlDatabase::database().commit(); } else { QSqlDatabase::database().rollback(); return false; } // 2. 批量更新模型 beginInsertRows(QModelIndex(), rowCount(), rowCount() + records.size() - 1); for (const auto &record : records) { insertRow(rowCount()); for (int col = 0; col < record.count(); ++col) { setData(index(rowCount()-1, col), record.value(col)); } } endInsertRows(); return true; } // 若需支持编辑,重写setData与flags bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override { if (!index.isValid() || role != Qt::EditRole) return false; bool ok = QSqlQueryModel::setData(index, value, role); if (ok) emit dataChanged(index, index); return ok; } Qt::ItemFlags flags(const QModelIndex &index) const override { Qt::ItemFlags flags = QSqlQueryModel::flags(index); flags |= Qt::ItemIsEditable; return flags; } };
使用说明
在线获取到新数据后,直接调用appendRecord()或appendRecords(),无需再执行setQuery():
// 假设从在线接口获取到一条QSqlRecord格式的新数据 QSqlRecord newRecord = getOnlineData(); customModel->appendRecord(newRecord);
问题2:多表数据读写场景解决方案
核心问题分析
QSqlTableModel仅支持单表操作:
- 用关联查询结果初始化模型后,模型元数据与单表不匹配,
insertRecord()无法识别目标表与字段,导致回写失败; - 调用
setTable()会重置模型元数据为单表结构,与之前关联查询的列不一致,引发视图显示异常。
针对性方案
方案1:用QSqlRelationalTableModel实现关联展示+单表写入
如果只需对其中一张主表进行写入操作(比如test表),可通过QSqlRelationalTableModel设置表关联,同时保留单表写入能力:
#include <QSqlRelationalTableModel> #include <QSqlRelation> QSqlRelationalTableModel *model = new QSqlRelationalTableModel(this); model->setTable("test"); // 设置关联:test表的`关联字段`关联test2表的`关联键`,展示test2表的`显示字段` model->setRelation(2, QSqlRelation("test2", "test_id", "name")); model->setEditStrategy(QSqlTableModel::OnFieldChange); // 实时写入 model->select(); // 插入记录(仅写入test表,关联字段需手动设置) QSqlRecord record = model->record(); record.setValue("test_field1", "value1"); record.setValue("test_id", 1); // 关联test2表的test_id model->insertRecord(-1, record);
方案2:手动用QSqlQuery处理多表写入+视图同步
如果需要同时写入两张表,需手动开启事务执行插入,之后刷新视图:
// 开启事务 QSqlDatabase::database().transaction(); // 插入test表 QSqlQuery query1; query1.prepare("INSERT INTO test (field1, field2) VALUES (?, ?)"); query1.addBindValue("val1"); query1.addBindValue("val2"); if (!query1.exec()) { QSqlDatabase::database().rollback(); return; } // 获取插入的主键(用于关联test2表) int testId = query1.lastInsertId().toInt(); // 插入test2表 QSqlQuery query2; query2.prepare("INSERT INTO test2 (test_id, field3) VALUES (?, ?)"); query2.addBindValue(testId); query2.addBindValue("val3"); if (!query2.exec()) { QSqlDatabase::database().rollback(); return; } // 提交事务并刷新视图 QSqlDatabase::database().commit(); model->select(); // 全量刷新,或用自定义模型的增量更新方法
关于QSqlQuery::exec()与视图同步
执行exec()修改数据库后,视图不会自动同步,需手动触发模型更新:
- 简单场景调用
model->select()全量刷新; - 性能敏感场景用自定义模型的增量更新方法(如问题1中的
appendRecord),避免全量重绘。
内容的提问来源于stack exchange,提问作者Arthur
相关产品推荐
相关产品推荐

