QSqlTableModel является развитием класса QSqlQueryModel и для запросов к базе данных использует функционал именно QSqlQueryModel.
Все сказанное ниже справедливо для Qt 4, и скорее всего для Qt5,6,...
Зачем же нужен тогда QSqlTableModel? Что нового он добавляет пользователю?
Ну во первых QSqlTabelModel добавляет временный кэш при работе непосредственно с базой данных.
Чтобы не долбить в базу запросами каждый раз когда меняется каждое поле можно накопить и сделать один запрос побольше.
Представлена эта идея как три стратегии редактирования модели данных: OnFieldChanged, OnRowChanged, OnManualSubmit. стратегии редактирования
Во-вторых QSqlTableModel вводит механизм ролей. Для каждой ячейки таблицы может устанавливаться и хранится несколько значений данных в зависимости о их роли, см.роли в модели данных.
Что можно сказать по наполнению приватного класса QSqlTableModelPrivate, на что следует обратить внимание:
Переменная приватного класса editBuffer имеет тип QSqlRecord, и является по сути буфером текущей строки во время редактирования. Используется в стратегиях OnFieldChange и OnRowChange.
Имеется ввиду, что при изменении полей в текущей строке они попадают в editBuffer с пометкой generated yes.
Переменная isertIndex (тип int) показывает программе строку, которая находится в состоянии добавления к таблице, но ещё не вставлена в базу данных.
Переменная editIndex (тип int) указывает на редактируемую строку.
Контейнер cache тип QMap<int, ModifiedRow> предназначен для накопления кэша данными в режиме стратегии onManualSubmit. Потом при submitAll это все это будет скидываться в базу данных.
QSqlTableModelPrivate использует класс QSqlIndex (QSqlQueryModel с QSqlIndex не работает). QSqlIndex это просто набор индексных полей для таблицы, которые получают из драйвера базы данных.
И теперь мы готовы понять как работает функция 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(); // потому что при OnFieldChange изменение только одного поля пишется в базу, но вдруг еще какие-то поля в буфере установлены (generated yes)
// устанавливаем значение поля generated=yes
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
// поскольку только одно поле помечено будет как generated
// то updateRowInTable только одно поле изменит в базе.
isOk = updateRowInTable(index.row(), d->editBuffer);
if (isOk)
select(); // поскольку у нас OnFieldChange обновляем талицу заново
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(); // и очистить editBuffer для текущей строки
}
// устанавливаем значение поля в editBuffer
QSqlTableModelPrivate::setGeneratedValue(d->editBuffer, index.column(), value);
// выставляем в editIndex, что мы редактируем это строку
d->editIndex = index.row();
emit dataChanged(index, index); // сигнализируем, что поле изменено
// но еще не записано в базу данных (в отличии от OnFieldChange)
break;
case OnManualSubmit:
{
// читаем из кеша QMap текущую строку
QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()];
// тут важно, что если в кеше нет еще такой row
// то при использовании для доступа через квадратные скобки []
// новый пустой ModifiedRow будет создан !
if (row.op == QSqlTableModelPrivate::None)
{
// в кеше не было такой row , создали новый элемент ModifiedRow
row.op = QSqlTableModelPrivate::Update;
// запоминаем данные всех полей текущей строки в rec
row.rec = d->rec;
// на всякий случай очищаем поля для новой строки кеша
QSqlTableModelPrivate::clearGenerated(row.rec);
// QSqlRecord primaryValues заполнятеся данными из query
// тут еще надо разбираться....
row.primaryValues = d->primaryValues(indexInQuery(index).row());
qDebug() << " row.primaryValues " << row.primaryValues;
}
// устанавливаем знение поля в row.rec (строка кеша)
QSqlTableModelPrivate::setGeneratedValue(row.rec, index.column(), value);
emit dataChanged(index, index); // сигнализируем об изменении поля строки
// это еще только в кеше, в самой базе данных изменений еще нет
break;
}
}
return isOk;
}
На этом пока все. Смотрите наше интересное развитие библиотеки Qt Развиваем класс QSqlTableModel.