insertRows

insertRows и ее вариация insertRow относятся к вставке строки в модель данных (не путайте с базой данных). 

Процесс вставки новой строки в модель данных заслуживает особого внимания. Например если первый раз вставить строку и далее никаких полей этой строки не изменять увидим примерно такое:

фотка 1

И это вроде бы нормально все выглядит, строка добавилась снизу правильно.

Но вот второй раз попытка добавить строку приводит уже к неправильным результатам:

фотка 2

Во-первых обратите внимание, что стратегия редактирования у нас выбрана OnFieldChange.

Во-вторых id поля двух добавленных снизу строк пустые, то есть submit/select не происходит. И это явно нарушает логику стратегии модели данных. Но как так получается?

Дело в том, что пока вы не установите хотя бы одному полю строки какое-то значение,то в editBuffer соответственно не появится флага generated yes ни у какого-поля. И это значит при submit эта строка по просту не будет вставлена в базу данных. А вот в в QTableView она останется (пока select в модели данных не произойдет).

А как такое может быть? Кстати это касается для всех стратегий модели данных и OnRowChange и OnManualSubmit (не важно).

Сразу можно сказать, что лечится это просто обработкой сигнала primeInsert (там по ссылке передается указатель на editBuffer) и установкой любому полю editBuffer флага genereted = yes (а значение можно и не устанавливать).

Вот код вызова QSqlTableModel::insertRows:

bool QSqlTableModel::insertRows(int row, int count, const QModelIndex &parent)
{
    Q_D(QSqlTableModel);
    if (row < 0 || count <= 0 || row > rowCount() || parent.isValid())
        return false;

    switch (d->strategy) {
    case OnFieldChange:
    case OnRowChange:
        if (count != 1)
            return false;
        beginInsertRows(parent, row, row);
        d->insertIndex = row;
        // ### apply dangling changes...
        d->clearEditBuffer();
        emit primeInsert(row, d->editBuffer);
        break;
    case OnManualSubmit:
        beginInsertRows(parent, row, row   count - 1);
        if (!d->cache.isEmpty()) {
            QMap::Iterator it = d->cache.end();
            while (it != d->cache.begin() && (--it).key() >= row) {
                int oldKey = it.key();
                const QSqlTableModelPrivate::ModifiedRow oldValue = it.value();
                d->cache.erase(it);
                it = d->cache.insert(oldKey   count, oldValue);
            }
        }

        for (int i = 0; i < count;   i) {
            d->cache[row   i] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Insert,
                                                                   d->rec);
            emit primeInsert(row   i, d->cache[row   i].rec);
        }
        break;
    }
    endInsertRows();
    return true;
}

Для стратегии OnFieldChange и OnRowChange сначала вызывается beginInsertRows(parent, row, row) базового абстрактного класса QAbstractItemModel и в конце вызывается endInsertRows(). 

void QAbstractItemModel::beginInsertRows(const QModelIndex &parent, int first, int last)
{
    Q_ASSERT(first >= 0);
    Q_ASSERT(last >= first);
    Q_D(QAbstractItemModel);
    d->changes.push(QAbstractItemModelPrivate::Change(parent, first, last));
    emit rowsAboutToBeInserted(parent, first, last);
    d->rowsAboutToBeInserted(parent, first, last);
}

В endInsertRows() в конце мы выпускаем сигнал insertRow(..). Этот сигнал модели данных связывается со слотом rowInserted модели представления (в методе setModel).

Таким образом модель представления например QTableView узнает, что ей надо вставить строки себе и отрисовать их и что после них заново.

Чтобы разобраться почему что-то не работает как надо логгируйте методы модели данных insertRowIntoTableupdateRowInTabledeleteRowFromTable. Это очень помогает быстро найти ошибку.  Правда для этого придется пересобрать ветку исходников qtsql.