Итак, что надо сразу понимать. Что все изменения данных в модели данных QSqlTableModel идут только через setData.
Функция setData принадлежит классу QSqlTableModel, в классе предке QSqlQueryModel setData нет. См.QSqlQueryModel
Установка значений происходит непосредственно уже в базу данных через функцию updateRowInTable.или insertRowIntoTable или deleteRowFromTable, тут тоже больше вариантов нет.Это все в модели данных QSqlTableModel.
Посмотрим QSqlTableModel::setData(..).
bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
Q_D(QSqlTableModel);
if (role != Qt::EditRole)
return QSqlQueryModel::setData(index, value, role);
if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount())
return false;
bool isOk = true;
switch (d->strategy) {
case OnFieldChange: {
if (index.row() == d->insertIndex) {
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
return true;
}
d->clearEditBuffer();
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
isOk = updateRowInTable(index.row(), d->editBuffer);
if (isOk)
select();
emit dataChanged(index, index);
break; }
case OnRowChange:
if (index.row() == d->insertIndex) {
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
return true;
}
if (d->editIndex != index.row()) {
if (d->editIndex != -1)
submit();
d->clearEditBuffer();
}
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
d->editIndex = index.row();
emit dataChanged(index, index);
break;
case OnManualSubmit: {
QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()];
if (row.op == QSqlTableModelPrivate::None) {
row.op = QSqlTableModelPrivate::Update;
row.rec = d->rec;
QSqlTableModelPrivate::clearGenerated(row.rec);
row.primaryValues = d->primaryValues(indexInQuery(index).row());
}
QSqlTableModelPrivate::setGeneratedValue(row.rec, index.column(), value);
emit dataChanged(index, index);
break; }
}
return isOk;
}
submit при OnRowChange
Очень важным нюансом является наличие вызова функции submit в коде функции setData.это при варианте модели редактирование OnRowChange. В чём тут смысл: если вы меняете поле уже другой строки надо сохранить изменения ранее изменённой строки. Идея так себе.
Это как раз тот случай, когда возможно произойдет вызов нового select к базе данных. OnRowChange.
И все бы ничего, но нюанс в том, что после submit слетают все текщие присвоенные индексы. То есть, если вы планировали вызвать еще какой-то второй setData, то в результате первого setData может произойти submit, потом произойдет select и как следствие все ранее установленные индексы (QModelIndex) протухнут. Буквально их row/column станут -1/-1.
Кстати именно это и будет являться признаком, что произошел select. И это можно использовать в своих целях.
Теперь немного анализа кода setData
В зависимости от выбранной стратегии редактирования setData пишет данные в разные хранилища.
OnFieldChange
Может показаться, что при стратегии OnFieldChange данные сразу записываются в базу данных. И это правильно с одной оговоркой: если это не вставка новой строки.
При вставке новой строки и потом редактировании поля этой строки запись в базу сразу может и не произойти. Это например зависит от клавиши, которой вы завершаете редактирование.
Например если вы жмете Enter после редактирования - вызывается submit.
А если вы жмете tab после редактирования и переходите к другому полю этой вставляемой строки, то в функции setData функция submit не вызывется. А данные сохранятся в editBuffer. Тут речь была о визуальных делегатах для модели представления типа QTableView, о его делегатах. Дело в том, что делегаты всегда существуют по умолчанию.
OnRowChange
При стратегии OnRowChange данные сохраняются во временном буфере editBuffer (обычный QSqlRecord).
OnManualSubmit
При использовании стратегии OnManualSubmit данные пишутся в кэш модели данных (контейнер cache). То есть данные в базу данных не пишутся, все копится в cache.Для записи в базу данных надо в конце всех изменений вызывать submitAĺl.
Может показаться , что можно записывать данные от роли отличной от EditRole. Но изначально это не так, смотрите роли в модели данных.
Логгируем работу модели данных QSqlRelationalTableModel и ее представления QTableView.Смотрите логирование запросов.
setData вызывается обычно интерактивным элементом управления в QTableView при установке нового значения в поле таблицы, но естественно можно и программно вызывать из кода, надо только указать индекс ячейки.
Например что делает ComboBoxDelegate в setModelData индекс [1x6] newValue : 0. То есть тут и вызывается setData модели данных с целью установить новое значение.
В модели данных выпускается сигнал dataChanged (конкретно в QSqlTableModel) , который можно связать со своим слотом, чтобы проверять устанавливаемое значение на валидность. Но на самом деле проверять что-то уже поздно. Это только для информирования, что ячейка изменилась.
Может показаться, что обрабатывая dataChanged можно как сделать submit, так и revert установки нового значения, но по нашему мнению это вредное решение.
Не надо лишний раз без четкого понимания зачем вы это делаете вызывать submit.
ComboBoxDelegate::createEditor [1x6] parent objName qt_scrollarea_viewport option " QStyleOption( SO_ViewItem , LeftToRight , QStyle::State( "Active | Enabled | HasFocus" ) , QRect(439,30 90x29) )
"ComboBoxDelegate::updateEditorGeometry [1x6] option " option : QStyleOption( SO_ViewItem , LeftToRight , QStyle::State( "Active | Enabled | HasFocus" ) , QRect(439,30 90x29) )
ComboBoxDelegate::slot_currentIndexChanged commitData currentIndex() : -1
ComboBoxDelegate::slot_currentIndexChanged commitData currentIndex() : 0
"ComboBoxDelegate::setModelData [1x6] newValue : 0"
ComboBoxDelegate::setModelData model->setData(index, newValue, Qt::EditRole)
my_TableView_Purchases::dataChanged return
testCheck::purch_dataChanged
"taxCode" : "0"
testCheck::slot_beforeUpdatePurch field : "taxCode" isGenerated : true newVal QVariant(int, 0)
table_QSqlRelationalTableModel::updateRowInTable row 1 true
testCheck::purch_dataChanged submitAll true
ComboBoxDelegate::setModelData model->setData(index, newValue, Qt::EditRole) : true
table_QSqlRelationalTableModel::updateRowInTable row -1 false
ComboBoxDelegate::setModelData model->submit() false newValue 0
Примечание: setData и далее submit в итоге используют те же sql команды типа updateRowInTable, insertRowIntoTable.
Теперь пару слов OnRowChange.
Может случится такая ситуация при стратегии редактирования OnRowChange, что вы открываете QTableView изменяете значение ячейки таблицы, закрываете таблицу и изменения пропадают. Что это значит: submit не был вызван, потому вы не перешли к редактированию ячейки другой строки. И это правда, но что делать? К сожалению модель данных QSqlTableModel предлагает только обходить все ячейки таблицы и проверять их через isDirty и это не удобно, но вы можете унаследоваться от QSqlTableModel и при setData помечать какая строка осталась последняя изменённая и при закрытии таблицы проверять это и если надо вызывать submit.
А можете поступить ещё так: взять наш класс PblSqlRelationalTableModel, унаследованный от QSqlTableModel и взять нашу таблицу PblTableView, унаследованную от QTableView, там эта проблема уже решена и ещё есть очень много нового полезного функционала, смотрите здесь: Развиваем класс QSqlTableModel.