Russian Qt Forum

Qt => Базы данных => Тема начата: Alex_C от Июль 23, 2012, 11:22



Название: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 23, 2012, 11:22
Странная несколько проблема. Таких же вопросов в инене видел много - но ответа на них нет. Может тут подскажут.
Есть наследник от QSqlQueryModel. Работает с движком SQLite.
Необходимо редактировать данные - это я решил так

Код
C++ (Qt)
bool LogModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
   QSqlTableModel::setData(index, value, role);
 
   if (index.column() < 1)
       return false;
 
   return updateDataForColumn(getFieldNameByIndex(index.column()), index, value);
}
 
bool LogModel::updateDataForColumn(QString columnName, const QModelIndex &index,
                                  const QVariant &value)
{
   QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
   int id = QSqlQueryModel::data(primaryKeyIndex).toInt();
 
   QSqlQuery query;
   if (index.column() > 1)
   {
       query.prepare("UPDATE " + globalVariable->logName +
                     " SET " + columnName +
                     " = ? WHERE KeyField = ?");
       query.addBindValue(value.toString());
       query.addBindValue(id);
   }
   bool b = query.exec();
   emit dataChanged(index, index);
   return b;
}
 
 

и необходимо вставить строку. В документации написано, что для этого нужно переопределить виртуальную ф-цию insertRows. А вот как ее переопределить для QSqlTableModel - не понятно. Пытался типа так

Код
C++ (Qt)
bool LogModel::insertRows(int position, int rows, const QModelIndex &index)
{
   Q_UNUSED(index);
 
   beginInsertRows(QModelIndex(), position, position + rows - 1);
 
   QSqlQuery query;
   query.prepare("INSERT INTO " + globalVariable->logName +
                 " (Name) VALUES (:Name)");
   query.bindValue(":Name", "");
   for (int row = 0; row < rows; row++)
   {
       query.exec();
   }
 
   endInsertRows();
   return true;
}
 

Не работает.
Для других абстрактных моделях примеров много, а вот конкретно для этого ничего найти не смог.


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 23, 2012, 14:38
Цитировать
Есть наследник от QSqlQueryModel.
......
А вот как ее переопределить для QSqlTableModel - не понятно. Пытался типа так
дык все-таки наследник от кого? QSqlQueryModel или QSqlTableModel? это принципиально....


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 23, 2012, 15:19
Наследник от QSqlTableModel - то, что принципиально понимаю. У наследников QSqlTableModel , как я понимаю, данные в памяти кешируются. И как я понимаю, данные отдельно ручками нужно вносить в базу на диске и в данные в самой модели.


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 23, 2012, 16:03
Тогда setData и InsertRows Вам НЕ нужно переопределять.....

читаем справку по QSqlTableModel
обращаем особое  внимание на InsertRecord,    setEditStrategy, revert, revertAll, submit, submitAll

метод setData, у Вас в корне неправильный, оригинальный и так сделает соответствующий update для таблицы, правильнее
переопределить метод flags
Код:
 
if(index.column()>=1)
      return Qt::ItemIsEditable;



Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 23, 2012, 16:26
Да я уже взад и вперед справку перечитал.
да есть у меня и

Код
C++ (Qt)
Qt::ItemFlags LogModel::flags(const QModelIndex &index) const
{
   Qt::ItemFlags flags = QSqlQueryModel::flags(index);
   if(isBoolField(index.column()))
       flags |= Qt::ItemIsUserCheckable;
   else if (index.column() >= 1)
       flags |= Qt::ItemIsEditable;
 
   return flags;
}
 

и

Код
C++ (Qt)
setEditStrategy(QSqlTableModel::OnManualSubmit);
 

убрал свои методы setData и insertRows.

Добавляю строку
Код
C++ (Qt)
   QSqlRecord record = logModel->record();
 
   record.setValue(... добавили все что надо);
   logModel->insertRecord(-1, record);
   logModel->submitAll();
 

в таблицу запись добавилась, но в вертикальном хедер около записи - *
Вышли из программы - зашли снова - записи нет.
И если редактируем - тоже изменения в базу на диск не вносятся.


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 23, 2012, 16:37
Код
C++ (Qt)
if( !logModel->insertRecord(-1,record) )
{
qDebug() << logModel->lastError().text();
logModel->revertAll();
}
 


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 23, 2012, 16:46
Никаких сообщений....
Фантазии и меня закончились :)
Перепробовал все варианты, что нашел.


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 23, 2012, 16:55
Пардон, вот так:
Код
C++ (Qt)
if( !logModel->submitAll())
{
qDebug() << logModel->lastError().text();
logModel->revertAll();
}
 


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 23, 2012, 17:01
О!
Спасибо большое! Хоть ошибку выдало -
"near "(": syntax error Unable to execute statement"
Пока правда непонятна что за скобка.


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 23, 2012, 17:10
Код
C++ (Qt)
if( !logModel->submitAll())
{
qDebug() << logModel->lastError().text();
       qDebug() << logModel->query().lastQuery();
logModel->revertAll();
}


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 24, 2012, 08:13
Вот что выдало (что собственно и должно было выдать)
"SELECT * FROM testtable ORDER BY DateIns ASC, TimeQIns ASC"
Пока подозрение только одно - я заполняю QSqlTableModel при помощи такого запроса:
 - это в ф-ции openModel моей модели:
Код
C++ (Qt)
qobject_cast<QSqlQueryModel*>(this)->setQuery(queryStr);
 
т.е. не средствами QSqlTableModel, а прямым запросом.
Может дело в этом?


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 24, 2012, 09:26
Спасибо большое за указание, где ошибки смотреть!
Разобрался, почему были ошибки.
1. Я сначала создавал QSqlTableModel - а уж потом подсоединялся к базе. Но это чисто моя ошибка - пока экспериментировал с программой, не заметил как это произошло.
2. Я считывал данные
qobject_cast<QSqlQueryModel*>(this)->setQuery(queryStr);
- данные считываются, но вот setTable все равно нужно задавать. Вот по этому у меня и не добавлялась запись.

Однако появились другие проблемы:
1. При добавлении записи в таблицу, вся таблица перечитывается с диска, что долго. Есть возможность это как то обойти?
2. При выполнении операции

Код
C++ (Qt)
   while(canFetchMore())
   {
       fetchMore();
       QCoreApplication::processEvents();
   }
 

как сказать QTableView (да и всем другим компонентам) отключить вывод отображение данных из модели (наналог в Дельфи TTable.EnableControls/DisableControls)?


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 24, 2012, 11:32
Цитировать
2. Я считывал данные
qobject_cast<QSqlQueryModel*>(this)->setQuery(queryStr);
- данные считываются, но вот setTable все равно нужно задавать. Вот по этому у меня и не добавлялась запись.

А вот так делать не надо! если вы хотите получить в итоге запрос типа:
SELECT * FROM testtable ORDER BY DateIns ASC, TimeQIns ASC
нужно перекрыть метод orderByClause()..., а для задания условий запроса - setFilter();
нельзя использовать внутренний query на прямую! через него QSqlTableModel  читает и изменяет информацию, и соответственно текст sql-пердолжения в каждый момент разный в зависимости от операции!!!

setTable("testtable"); // вытаскивает всю нужную информацию из системных таблиц (например название полей их тип и т.д.)
select(); // выполнить запрос "select * from testtable" , QSqlTableModel формирует его динамически!

Цитировать
Однако появились другие проблемы:
1. При добавлении записи в таблицу, вся таблица перечитывается с диска, что долго. Есть возможность это как то обойти?
2. При выполнении операции

1. Может быть следствием вышесказанного, во всяком случае это в Вашем коде, QSqlTableModel - при вставке новой записи сама ничего не пересчитывает....
2. tableView->blockSignals(true);


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 24, 2012, 16:31
Посмотрел исходники, поразберался.
Получается все немного не так:
Метод orderByClause() нельзя перекрыть - он virtual protected.
Да это и не надо. В принципе вполне пойдет и
qobject_cast<QSqlQueryModel*>(this)->setQuery(queryStr);
только перед этим нужно сделать
setTable - чтоб поля таблицы определились.
Ну вообще сортировка и выборка работают правильно - в интернете вроде как и советуют если нужна сложная сортировка напрямую setQuery вызывать.

Тут вот сейчас вопрос про вставку:
в таблице есть 10 тыс записей. При добавлении новой записи после submitAll происходит полное считывание записей с базы. Естественно медленно. Это так и должно быть? Специально для пробы все делаю по документации - открываю через select().


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 24, 2012, 16:49
Цитировать
Метод orderByClause() нельзя перекрыть - он virtual protected.
Да ну, с каких это пор....
вот кусок для PostgreSQL из реального проекта:
Код
C++ (Qt)
QString SqlTableModel::orderByClause() const
{
QString s = QSqlTableModel::orderByClause();
if(!s.isNull()) s.append(QLatin1String(" NULLS FIRST" ));
return s;
}

Цитировать
При добавлении новой записи после submitAll происходит полное считывание записей с базы. Естественно медленно. Это так и должно быть?
Да!
Код
C++ (Qt)
bool QSqlTableModel::submitAll()
{
   Q_D(QSqlTableModel);
.........
.........
..........
       d->clearCache();
       return select();
   }
   return false;
}
 

но можно перекрыть select()!


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 24, 2012, 21:42
Во первых - большое спасибо за ответы!
Во вторых - есть куча вопросов по PostgreSQL - но мучить вас я их не буду)))
Давайте наверное опишу саму задачу, чтоб суть проблемы стала ясна:
есть проект на Дельфи (мой проект). Проект очень большой, но большой не в плане работы с БД. Сейчас переделываю его на Qt , но 15 лет программирования на Дельфи сказываются - пытаюсь сделать как там))). Собственно все очень критичные части уже на Qt перенес. Осталась - работа с БД, которая в моем проекте достаточно простая. Хоть есть несколько критичных моментов. Но думал, что проблем не будет... Однако перечитал ВСЕ книги по Qt - все равно вопросы есть))) Это я к тому, что документацию я читаю.
Теперь конктетика.
1. Есть критичная ситуация - время вставки записи в таблицу. При том что в таблице порядка 200 тыс записей (это максимум). Они отображаются в табличном виде.  Вставленные новые записи нужно отображать в таблице. Естественно 200 тыс записей в память никто не считывает - в Дельфи в компоненте ADOQuery есть св-во - данные брать с сервера (постраничное отображение) или целиком скачивать. Тут я такое не нашел.
2. Продолжение предыдущего: в Qt считываются первые 450 записей. Почему первые? А если мне надо последние?
3. И все же мой код на редактирование:
Код
C++ (Qt)
bool LogModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
   QSqlTableModel::setData(index, value, role);
 
   if (index.column() < 1)
       return false;
 
   return updateDataForColumn(getFieldNameByIndex(index.column()), index, value);
}
 
bool LogModel::updateDataForColumn(QString columnName, const QModelIndex &index,
                                  const QVariant &value)
{
   QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
   int id = QSqlQueryModel::data(primaryKeyIndex).toInt();
 
   QSqlQuery query;
   if (index.column() > 1)
   {
       query.prepare("UPDATE " + globalVariable->logName +
                     " SET " + columnName +
                     " = ? WHERE KeyField = ?");
       query.addBindValue(value.toString());
       query.addBindValue(id);
   }
   bool b = query.exec();
   emit dataChanged(index, index);
   return b;
}
 
 

Работает  очень эффективно и быстро. Редактирование.
В книге Бланшета и Саммерфильда в главе 13 (в книге 2007 года) предлагается нечто подобное.
Вопрос собственно только в одном - как также быстро и эффективно вставлять строки)))


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: PavelVX от Июль 25, 2012, 05:38
Тоже присоединяюсь к теме, но маленько с другим вопросом: а как определить, что запись новая?
Ситуация: setEditStrategy(QSqlTableModel::OnManualSubmit); пользователь понавводил/поредактировал данные. Как мне отличить новая это строка или редактирование уже введенной?
Причем isDirty() несколько кривовато работает.
При смене строки в тейблвью смотрю состояние isDirty по предыдущему индексу. После редактирования, при сходе с редактируемой строки, isDirty == false!!!! Однако, если я потом вернусь на эту запись и опять с неё сойду, то isDirty == true :(.
Я переопределил в своей модели setData(). По идее, при редактировании isDirty() после setData должен сбрасываться? Или мне что-то надо прописать в этом методе?


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 25, 2012, 08:08
Добавление к предыдущему вопросу: а есть ли опять де как в дельфийском TTable определить State: dsInsert, dsEdit - тоже пока это не нашел.


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: PavelVX от Июль 25, 2012, 08:30
Добавление к предыдущему вопросу: а есть ли опять де как в дельфийском TTable определить State: dsInsert, dsEdit - тоже пока это не нашел.
Что есть dsInsert, dsEdit? Метки состояния рекорда?


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 25, 2012, 11:20
Да.

Теперь по теме... Прочитал информацию по данному вопросу в инете - не утешительная информация. Qt не умеет читать из БД постранично. Вопрос: может кто озадачивался данной проблемой? Как допустим в табличном виде просмотреть 100 тыс записей, как это сделано в том же MS Access?

В принципе проблему вставки записи/редактирования решил - отдельно вставляю в БД, отдельно в таблицу. Если делать submitAll() - происходит перечитывание всей таблицы, что неверно.


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 25, 2012, 11:59
2 Alex_C

ИМНО: у Вас в голове каша при использовании QSqlTableModel , по факту Вы пытаетесь из него сделать Редактируемую QSqlQueryModel..., в примерах Qt есть простенькая реализация такого класса, тока ее надо расширить и углубить 

Код
C++ (Qt)
bool LogModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
   QSqlTableModel::setData(index, value, role);
 
   if (index.column() < 1)
       return false;
 
   return updateDataForColumn(getFieldNameByIndex(index.column()), index, value);
}

    В Вашем случае: обновляет данные  в кэше, и sumitAll() - непосредственно в базе
    QSqlTableModel::setData(index, value, role);
    ........
    Еще раз обновляем те же данные сразу в базе.....
    return updateDataForColumn(getFieldNameByIndex(index.column()), index, value);

Цитировать
Вопрос собственно только в одном - как также быстро и эффективно вставлять строки)))

Вставка как раз происходит быстро! А вот чтение обновленных данных - медленно....
можно например так - setQuery("select * testtable order by ...... limit кол_во_строк offset строка_номер"
естественно "кол_во_строк" и "строка_номер" модель должна сама определять....


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 25, 2012, 12:07
Наследуйте свою модель от QSqlQueryModel, setData у Вас уже есть,
только вот эту строку выкинуть:    QSqlTableModel::setData(index, value, role);
Вам осталось только InsertRows реализовать, ну и с постраничным выводом разобраться (см. пост выше)


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 25, 2012, 12:20
Цитировать
Как допустим в табличном виде просмотреть 100 тыс записей

Я бы ослеп на второй тысяче...... ??? Неужели все данные нужны сразу, и их можно проанализировать только последовательно листая экранами по ххх строк, 100К записей ???


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: PavelVX от Июль 26, 2012, 05:01
А как определить, до сохранения в базу сабмитом, какие строки редактировались, а какие новые?
Как самому сбросить isDirty?


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: fte от Июль 26, 2012, 11:13
2 PavelVX
Цитировать
Как самому сбросить isDirty?
submit(),revert()

Цитировать
А как определить, до сохранения в базу сабмитом, какие строки редактировались, а какие новые?
например использовать сигналы.....
beforeInsert(),beforeUpdate(),beforeDelete()


Название: Re: QSqlTableModel - добавить строку не получается.
Отправлено: Alex_C от Июль 26, 2012, 11:34
Каши как раз в голове нет - просто пытался проблему решить что называется "малой кровью".
И в общем то решил, но решение самому не очень нравится.
Правильное решение вот:

можно например так - setQuery("select * testtable order by ...... limit кол_во_строк offset строка_номер"
естественно "кол_во_строк" и "строка_номер" модель должна сама определять....

однако допустим на том же Access'е я не нашел такого варианта SELECT. Есть только
SELECT TOP 100 ...
но это считывает верхние 100 строк. Хотя учитывая что в Дельфи ADO умеет так делать - решение есть.
Причем если понять как считывать группами из центра таблици, то реализовать подгруздку в таблицу динамически при скроллинге - не проблема.