QSqlTableModel

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.