一、先说结论吧,QT官方应该是不完全支持这种操作的。
是不完全支持,不是完全不支持。
以下代码是官方文档的示例,在需要修改数据时,继承了一个QSqlQueryModel类,并定义了自己的setData函数,其中setData修改数据的时候,是直接操作数据库对数据源做修改的;而操作完之后调用的refresh()函数也是在此处现场定义的,是重新执行了一遍select。整个代码中不涉及对原有的model对象中的值的修改,而是直接把model通过查询整体刷新掉了。
在做比较粗放的查询的时候,这样自然是没有问题,但是如果你像我一样,需求中,查询是包含一些筛选条件的,而你要修改的值恰好是筛选条件之一,那么修改后重新执行原来的查询并不能让某一行的值发生变化,反而会让这一行从结果中被筛掉,这就会导致比较差的交互体验。那有没有办法解决这个问题呢?敢写文章出来自然是有的,只是办法比较笨,而且会损失一些性能。请继续往下看。
bool EditableSqlModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
if (index.column() < 1 || index.column() > 2)
return false;
QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
int id = data(primaryKeyIndex).toInt();
clear();
bool ok;
if (index.column() == 1) {
ok = setFirstName(id, value.toString());
} else {
ok = setLastName(id, value.toString());
}
refresh();
return ok;
}
void EditableSqlModel::refresh()
{
setQuery("select * from person");
setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
setHeaderData(1, Qt::Horizontal, QObject::tr("First name"));
setHeaderData(2, Qt::Horizontal, QObject::tr("Last name"));
}
bool EditableSqlModel::setFirstName(int personId, const QString &firstName)
{
QSqlQuery query;
query.prepare("update person set firstname = ? where id = ?");
query.addBindValue(firstName);
query.addBindValue(personId);
return query.exec();
}
bool EditableSqlModel::setLastName(int personId, const QString &lastName)
{
QSqlQuery query;
query.prepare("update person set lastname = ? where id = ?");
query.addBindValue(lastName);
query.addBindValue(personId);
return query.exec();
}
二、还是要从需求入手,我们的需求本质上不是修改model,而是要让当前页面的某些值被修改后,重新刷新页面时,整个页面要呈现的行与修改前是一样的。
所以笨办法是这样的,还是从refresh函数入手,在重新执行查询的时候,把当前页面的所有主键记录下来然后重新查询一遍,代码见下。
PS:这个函数随便叫什么,refresh不是qsqlquerymodel类里面声明过的。
PS2:没必要非得在setdata函数里面调用,事实上我修改值也是直接另起炉灶的,setdata函数直接注释掉了,反正要拼接sql语句,不如拼接点通用性高的出来,有几列写几个set实在太麻烦了(当然也可能是我段位太低)。修改完之后调用一下这个refresh就好。
PS3:其实这个函数甚至没必要写成成员函数,详见注释。
bool refresh()
{
bool ok;
QStringList ID_list;
int temp_count = this->rowCount();
if (temp_count < 1 )
return false;
else
{
QSqlQuery tmp;
//把当前model中页面内所有的主键记录下来,在我这里是mould_ID
for (int i = 0; i < temp_count; i++)
{
ID_list.insert(i, this->index(i, 0).data().toString());
}
string tmp_sql = "select * from Mould_main where mould_ID = '" + ID_list[0].toStdString() + "' ";
if (temp_count == 1)
{
tmp.prepare(QString::fromStdString(tmp_sql + " order by mould_serial"));
}
else if (temp_count > 1)
{
//通过循环将所有主键作为查询条件拼接成一句超长sql进行查询
//注意我后面拼接的排序语句,要使得页面相比原来不发生任何变化,排序规则要保持一致。
//如果存在其他排序或允许用户自己设置排序的话,这个函数也可以不写成成员函数,在需要传参的地方定义、调用也是可以的。
//题外话,也不知道sql语句有没有长度上限,拼接的时候string真的比QString好用
for (int j = 1; j < temp_count; j++)
{
tmp_sql = tmp_sql + " or mould_ID = '" + ID_list[j].toStdString() + "' ";
}
tmp.prepare(QString::fromStdString(tmp_sql + " order by mould_serial"));
}
ok = tmp.exec();
this->setQuery(tmp);
}
return ok;
}
三、