QSqlTableModel: use selectRow() for field and row edit strategies

Calling select refreshes the query data but disrupts view
navigation.

For OnFieldChange and OnRecordChange it makes sense to only
select the row in question. This does not disturb view navigation.

Assume disruption of view navigation is not a problem
for OnManualSubmit because the user or application decides
when submitAll is called.

Task-number: QTBUG-2875
Change-Id: I1e5f68668fb9102f6296d67d543e80daa403f1c4
Reviewed-by: Yunqiao Yin <charles.yin@nokia.com>
This commit is contained in:
Mark Brand 2012-02-21 09:22:26 +01:00 committed by Qt by Nokia
parent 291e2c7d54
commit 888fed8065
4 changed files with 108 additions and 26 deletions

8
dist/changes-5.0.0 vendored
View File

@ -406,6 +406,14 @@ ignore the rest of the range.
QSqlTableModel::indexInQuery() as example of how to implement in a QSqlTableModel::indexInQuery() as example of how to implement in a
subclass. subclass.
* QSqlTableModel edit strategies OnFieldChange/OnRowChange QTBUG-2875
Previously, after changes were submitted in these edit strategies, select()
was called which removed and inserted all rows. This ruined navigation
in QTableView. Now, with these edit strategies, there is no implicit select()
done after committing. This includes deleted rows which remain in
the model as blank rows until the application calls select(). Instead,
selectRow() is called to refresh only the affected row.
**************************************************************************** ****************************************************************************
* Database Drivers * * Database Drivers *
**************************************************************************** ****************************************************************************

View File

@ -663,8 +663,8 @@ bool QSqlTableModel::deleteRowFromTable(int row)
Returns false on error, detailed error information can be Returns false on error, detailed error information can be
obtained with lastError(). obtained with lastError().
On success the model will be repopulated. Any views In OnManualSubmit, on success the model will be repopulated.
presenting it will lose their selections. Any views presenting it will lose their selections.
Note: In OnManualSubmit mode, already submitted changes won't Note: In OnManualSubmit mode, already submitted changes won't
be cleared from the cache when submitAll() fails. This allows be cleared from the cache when submitAll() fails. This allows
@ -677,6 +677,8 @@ bool QSqlTableModel::submitAll()
{ {
Q_D(QSqlTableModel); Q_D(QSqlTableModel);
bool success = true;
for (QSqlTableModelPrivate::CacheMap::Iterator it = d->cache.begin(); for (QSqlTableModelPrivate::CacheMap::Iterator it = d->cache.begin();
it != d->cache.constEnd(); ++it) { it != d->cache.constEnd(); ++it) {
if (it.value().submitted()) if (it.value().submitted())
@ -684,25 +686,35 @@ bool QSqlTableModel::submitAll()
switch (it.value().op()) { switch (it.value().op()) {
case QSqlTableModelPrivate::Insert: case QSqlTableModelPrivate::Insert:
if (!insertRowIntoTable(it.value().rec())) success = insertRowIntoTable(it.value().rec());
return false;
break; break;
case QSqlTableModelPrivate::Update: case QSqlTableModelPrivate::Update:
if (!updateRowInTable(it.key(), it.value().rec())) success = updateRowInTable(it.key(), it.value().rec());
return false;
break; break;
case QSqlTableModelPrivate::Delete: case QSqlTableModelPrivate::Delete:
if (!deleteRowFromTable(it.key())) success = deleteRowFromTable(it.key());
return false;
break; break;
case QSqlTableModelPrivate::None: case QSqlTableModelPrivate::None:
Q_ASSERT_X(false, "QSqlTableModel::submitAll()", "Invalid cache operation"); Q_ASSERT_X(false, "QSqlTableModel::submitAll()", "Invalid cache operation");
break; break;
} }
it.value().setSubmitted();
if (success) {
it.value().setSubmitted();
if (d->strategy != OnManualSubmit)
success = selectRow(it.key());
}
if (!success)
break;
} }
return select(); if (success) {
if (d->strategy == OnManualSubmit)
success = select();
}
return success;
} }
/*! /*!
@ -719,8 +731,8 @@ bool QSqlTableModel::submitAll()
Returns true on success; otherwise returns false. Use lastError() Returns true on success; otherwise returns false. Use lastError()
to query detailed error information. to query detailed error information.
On success the model will be repopulated. Any views Does not automatically repopulate the model. Submitted rows are
presenting it will lose their selections. refreshed from the database on success.
\sa revert(), revertRow(), submitAll(), revertAll(), lastError() \sa revert(), revertRow(), submitAll(), revertAll(), lastError()
*/ */

View File

@ -542,6 +542,13 @@ void tst_QSqlRelationalTableModel::setRecord()
model.setSort(0, Qt::AscendingOrder); model.setSort(0, Qt::AscendingOrder);
QVERIFY_SQL(model, submit()); QVERIFY_SQL(model, submit());
if (model.editStrategy() != QSqlTableModel::OnManualSubmit) {
QCOMPARE(model.data(model.index(1, 0)).toInt(), 7);
QCOMPARE(model.data(model.index(1, 1)).toString(), QString("tester"));
QCOMPARE(model.data(model.index(1, 2)).toString(), QString("herr"));
QVERIFY_SQL(model, select());
}
QCOMPARE(model.data(model.index(3, 0)).toInt(), 7); QCOMPARE(model.data(model.index(3, 0)).toInt(), 7);
QCOMPARE(model.data(model.index(3, 1)).toString(), QString("tester")); QCOMPARE(model.data(model.index(3, 1)).toString(), QString("tester"));
QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr"));
@ -599,6 +606,8 @@ void tst_QSqlRelationalTableModel::insertWithStrategies()
QVERIFY_SQL(model, submitAll()); QVERIFY_SQL(model, submitAll());
model.setEditStrategy(QSqlTableModel::OnManualSubmit); model.setEditStrategy(QSqlTableModel::OnManualSubmit);
// The changes were submitted, but there was no automatic select to resort
QVERIFY_SQL(model, select());
QCOMPARE(model.data(model.index(0,0)).toInt(), 1); QCOMPARE(model.data(model.index(0,0)).toInt(), 1);
QCOMPARE(model.data(model.index(0,1)).toString(), QString("harry")); QCOMPARE(model.data(model.index(0,1)).toString(), QString("harry"));
@ -1401,6 +1410,8 @@ void tst_QSqlRelationalTableModel::whiteSpaceInIdentifiers()
QVERIFY_SQL(model, insertRecord(-1, rec)); QVERIFY_SQL(model, insertRecord(-1, rec));
model.submitAll(); model.submitAll();
if (model.editStrategy() != QSqlTableModel::OnManualSubmit)
QVERIFY_SQL(model, select());
QCOMPARE(model.data(model.index(0, 0)).toInt(), 3); QCOMPARE(model.data(model.index(0, 0)).toInt(), 3);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Washington")); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Washington"));

View File

@ -411,8 +411,11 @@ void tst_QSqlTableModel::setRecord()
} else if ((QSqlTableModel::EditStrategy)submitpolicy == QSqlTableModel::OnRowChange && i == model.rowCount() -1) } else if ((QSqlTableModel::EditStrategy)submitpolicy == QSqlTableModel::OnRowChange && i == model.rowCount() -1)
model.submit(); model.submit();
else { else {
// dataChanged() is not emitted when submitAll() is called // dataChanged() emitted by selectRow() as well as setRecord()
QCOMPARE(spy.count(), 1); if ((QSqlTableModel::EditStrategy)submitpolicy == QSqlTableModel::OnFieldChange)
QCOMPARE(spy.count(), 2);
else
QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).count(), 2); QCOMPARE(spy.at(0).count(), 2);
QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(0)), model.index(i, 0)); QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(0)), model.index(i, 0));
QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(1)), model.index(i, rec.count() - 1)); QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(1)), model.index(i, rec.count() - 1));
@ -471,8 +474,7 @@ void tst_QSqlTableModel::insertRow()
rec.setValue(0, 42); rec.setValue(0, 42);
rec.setValue(1, QString("francis")); rec.setValue(1, QString("francis"));
// FieldChange updates immediately and resorts // Setting record does not cause resort
// Row/Manual submit does not resort
QVERIFY(model.setRecord(2, rec)); QVERIFY(model.setRecord(2, rec));
QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
@ -482,8 +484,23 @@ void tst_QSqlTableModel::insertRow()
QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
// See comment above setRecord QCOMPARE(model.data(model.index(2, 0)).toInt(), 42);
if (submitpolicy == QSqlTableModel::OnFieldChange) { QCOMPARE(model.data(model.index(2, 1)).toString(), QString("francis"));
QCOMPARE(model.data(model.index(2, 2)).toInt(), 2);
QCOMPARE(model.data(model.index(3, 0)).toInt(), 3);
QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi"));
QCOMPARE(model.data(model.index(3, 2)).toInt(), 3);
QVERIFY(model.submitAll());
if (submitpolicy == QSqlTableModel::OnManualSubmit) {
// After the submit we should have the resorted view
QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); QCOMPARE(model.data(model.index(2, 0)).toInt(), 3);
QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
QCOMPARE(model.data(model.index(2, 2)).toInt(), 3); QCOMPARE(model.data(model.index(2, 2)).toInt(), 3);
@ -491,6 +508,13 @@ void tst_QSqlTableModel::insertRow()
QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis")); QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis"));
QCOMPARE(model.data(model.index(3, 2)).toInt(), 2); QCOMPARE(model.data(model.index(3, 2)).toInt(), 2);
} else { } else {
// Submit does not select, therefore not resorted
QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
QCOMPARE(model.data(model.index(2, 0)).toInt(), 42); QCOMPARE(model.data(model.index(2, 0)).toInt(), 42);
QCOMPARE(model.data(model.index(2, 1)).toString(), QString("francis")); QCOMPARE(model.data(model.index(2, 1)).toString(), QString("francis"));
QCOMPARE(model.data(model.index(2, 2)).toInt(), 2); QCOMPARE(model.data(model.index(2, 2)).toInt(), 2);
@ -499,9 +523,8 @@ void tst_QSqlTableModel::insertRow()
QCOMPARE(model.data(model.index(3, 2)).toInt(), 3); QCOMPARE(model.data(model.index(3, 2)).toInt(), 3);
} }
QVERIFY(model.submitAll()); QVERIFY(model.select());
// After the select we should have the resorted view in all strategies
// After the submit we should have the resorted view
QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
@ -514,7 +537,6 @@ void tst_QSqlTableModel::insertRow()
QCOMPARE(model.data(model.index(3, 0)).toInt(), 42); QCOMPARE(model.data(model.index(3, 0)).toInt(), 42);
QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis")); QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis"));
QCOMPARE(model.data(model.index(3, 2)).toInt(), 2); QCOMPARE(model.data(model.index(3, 2)).toInt(), 2);
} }
void tst_QSqlTableModel::insertRecord() void tst_QSqlTableModel::insertRecord()
@ -647,8 +669,7 @@ void tst_QSqlTableModel::removeRow()
QVERIFY_SQL(model, select()); QVERIFY_SQL(model, select());
QCOMPARE(model.rowCount(), 3); QCOMPARE(model.rowCount(), 3);
// headerDataChanged must be emitted by the model when the edit strategy is OnManualSubmit, // headerDataChanged must be emitted by the model since the row won't vanish until select
// when OnFieldChange or OnRowChange it's not needed because the model will re-select.
qRegisterMetaType<Qt::Orientation>("Qt::Orientation"); qRegisterMetaType<Qt::Orientation>("Qt::Orientation");
QSignalSpy headerDataChangedSpy(&model, SIGNAL(headerDataChanged(Qt::Orientation, int, int))); QSignalSpy headerDataChangedSpy(&model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)));
@ -673,7 +694,10 @@ void tst_QSqlTableModel::removeRow()
headerDataChangedSpy.clear(); headerDataChangedSpy.clear();
QVERIFY(model.removeRow(1)); QVERIFY(model.removeRow(1));
QCOMPARE(headerDataChangedSpy.count(), 0); QCOMPARE(headerDataChangedSpy.count(), 1);
QCOMPARE(model.rowCount(), 3);
QVERIFY_SQL(model, select());
QCOMPARE(model.rowCount(), 2); QCOMPARE(model.rowCount(), 2);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
@ -706,6 +730,11 @@ void tst_QSqlTableModel::removeRows()
QCOMPARE(beforeDeleteSpy.count(), 2); QCOMPARE(beforeDeleteSpy.count(), 2);
QVERIFY(beforeDeleteSpy.at(0).at(0).toInt() == 0); QVERIFY(beforeDeleteSpy.at(0).at(0).toInt() == 0);
QVERIFY(beforeDeleteSpy.at(1).at(0).toInt() == 1); QVERIFY(beforeDeleteSpy.at(1).at(0).toInt() == 1);
// deleted rows shown as empty until select
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString(""));
QVERIFY(model.select());
// deleted rows are gone
QCOMPARE(model.rowCount(), 1); QCOMPARE(model.rowCount(), 1);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("vohi")); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("vohi"));
model.clear(); model.clear();
@ -778,6 +807,14 @@ void tst_QSqlTableModel::removeInsertedRow()
model.submitAll(); model.submitAll();
if (model.editStrategy() != QSqlTableModel::OnManualSubmit) {
QCOMPARE(model.rowCount(), 4);
QCOMPARE(model.data(model.index(1, 0)).toInt(), 55);
QCOMPARE(model.data(model.index(1, 1)).toString(), QString("null columns"));
QCOMPARE(model.data(model.index(1, 2)).isNull(), true);
QVERIFY(model.select());
}
QCOMPARE(model.rowCount(), 4); QCOMPARE(model.rowCount(), 4);
QCOMPARE(model.data(model.index(3, 0)).toInt(), 55); QCOMPARE(model.data(model.index(3, 0)).toInt(), 55);
QCOMPARE(model.data(model.index(3, 1)).toString(), QString("null columns")); QCOMPARE(model.data(model.index(3, 1)).toString(), QString("null columns"));
@ -785,8 +822,17 @@ void tst_QSqlTableModel::removeInsertedRow()
QVERIFY(model.removeRow(3)); QVERIFY(model.removeRow(3));
model.submitAll(); model.submitAll();
QCOMPARE(model.rowCount(), 3);
if (model.editStrategy() != QSqlTableModel::OnManualSubmit) {
QCOMPARE(model.rowCount(), 4);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
QCOMPARE(model.data(model.index(3, 1)).toString(), QString(""));
QVERIFY(model.select());
}
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
@ -1129,6 +1175,11 @@ void tst_QSqlTableModel::insertRecordBeforeSelect()
buffer.setValue("title", 0); buffer.setValue("title", 0);
QVERIFY_SQL(model, insertRecord(1, buffer)); QVERIFY_SQL(model, insertRecord(1, buffer));
if (model.editStrategy() != QSqlTableModel::OnManualSubmit) {
QCOMPARE(model.rowCount(), 2);
QVERIFY_SQL(model, select());
}
int rowCount = model.rowCount(); int rowCount = model.rowCount();
model.clear(); model.clear();
QCOMPARE(model.rowCount(), 0); QCOMPARE(model.rowCount(), 0);