insertRows

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

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

фотка 1

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

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

фотка 2

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

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

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

А как такое может быть? Кстати это касается для всех стратегий модели данных и 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);
}