Got QSqlError(-1, "No Fields to update", "") on model->submitAll()
-
Hello all
I am trying to learned Qt for the past few weeks.
In a form that I created, I have 2 Qtableview(parentView), I named the first as tvPo to display records from Database (PGSQL), supposedly the parent table. The second was named tvPoItems(childView) which is supposed to display rows of the child table and I have a couple of QLineedits and a QCombobox in between these Tables.
I created a model(parentModel & childModel) and a mapper for the QLineedits and to a QCombobox. I also have 3 QCheckbox with code in click event to dynamically change the parentModel->filter according to the checkbox's label which is supposed to select/toggle between field "status" values "For Approval, Denied & Approved". I got this working according to what I wanted;On the QCombobox I use the Activated event to update a field on the parentModel (i.e. status="For Approval | Denied | Approved"). Each time I would run the form, I programatically select row 0 of the parentView and update the childView according to a key from the parentView.
Scenario:
parentView row(0) selected programatically. Whenever i change the QCombobox value without touching/clicking on any rows of the parentView, the field "status" is updated and then a i issued a code again to select row(0) for the reason that the updated row might be gone on the view because of the model filter that i set. Repeating this (QCombobox) process trough out the entire rows is working well.My problem is, whenever i will always select lets say the last row of the parentView, the code to update the model in the QCombobox Activated event will only succeed on the first try. It gives me a QSqlError(-1, "No Fields to update", "") on the parentModel->lastError().
Below is a copy of my code:
@
void listuapo::on_cbStatus_activated(const QString &arg1)
{
if (ui->tvPo->selectionModel()->hasSelection()) {
int thisRow = ui->tvPo->currentIndex().row();
parentModel->setData(parentModel->index(thisRow, parentModel->fieldIndex("status")), arg1);
qDebug() << thisRow << parentModel->record(thisRow);
if (parentModel->submitAll()) {
qDebug() << "changes saved";
parentModel->select();
parentView->setModel(parentModel);
parentView->selectRow(0);
parentView->resizeColumnsToContents();
setChildFilter();
} else
qDebug() << parentModel->lastError();
}
}void listuapo::setChildFilter()
{
if (ui->tvPo->selectionModel()->hasSelection())
{
QModelIndex index = parentModel->index(mapper->currentIndex(), 0, QModelIndex());
keyS = index.data().toString();
} else
keyS = "noParentSelection";childModel->setFilter("ponumber = '" + keyS + "'"); childModel->select(); childView->setModel(childModel); childView->resizeColumnsToContents(); parentView->setFocus();
}
@And here is the output of the included qDebug() << thisRow << parentModel->record(thisRow); in QCombobox Activated event:
@
at this point i have 4 rows: row 3(last row) is selected:3 QSqlRecord( 15 )
" 0:" QSqlField("ponum", qlonglong, length: 8, generated: yes, typeID: 20) "104"
[i have to cut this part, max post char reach]
changes savedat this point, 3 rows left, last row is selected (row 2):
2 QSqlRecord( 15 )
" 0:" QSqlField("ponum", qlonglong, length: 8, generated: yes, typeID: 20) "103"
" 1:" QSqlField("orderdate", QDate, length: 4, generated: yes, typeID: 1082) "2011-12-04"
" 2:" QSqlField("description", QString, length: 100, generated: yes, typeID: 1043) "SC JOHNSON & SONS, INC."
" 3:" QSqlField("notes", QString, generated: yes, typeID: 25) "..."
" 4:" QSqlField("discount", double, length: 8, generated: yes, typeID: 701) "500"
" 5:" QSqlField("status", QString, length: 20, generated: yes, typeID: 1043) "Denied"
" 6:" QSqlField("isapproved", QString, length: 3, generated: yes, typeID: 1043) ""
" 7:" QSqlField("totalamount", double, length: 786434, generated: yes, typeID: 1700) "5500"
" 8:" QSqlField("isdelivered", QString, length: 3, generated: yes, typeID: 1043) ""
" 9:" QSqlField("deliverydate", QDate, length: 4, generated: yes, typeID: 1082) ""
"10:" QSqlField("hasreceived", QString, length: 3, generated: yes, typeID: 1043) ""
"11:" QSqlField("hasreturned", QString, length: 3, generated: yes, typeID: 1043) ""
"12:" QSqlField("iscomplete", QString, length: 3, generated: yes, typeID: 1043) ""
"13:" QSqlField("preparedby", QString, length: 20, generated: yes, typeID: 1043) "jaosimt"
"14:" QSqlField("approvedby", QString, length: 20, generated: yes, typeID: 1043) ""
QSqlError(-1, "No Fields to update", "")
@I am stuck in this particular problem. Any help will be very much appreciated.
[EDIT: code formatting, Volker]
-
It is really hard to read your code, please format it using appropriate tags.
It sounds to me that the submit code is called more than once within the same slot. -
Thank you for your reply fluca1978... sorry bout the copy paste thing there... wasn't expecting that kind of result...
anyway, here it is.
MODERATOR's NOTE
I removed the double post and formatted the original code. In the future, please edit your original post (the edit link is right to the post, just below the username and avatar). Volker -
Uhm, it is quite strange, I don't see anything strange in this code. The only thing I note is that you use alternatively the
@ui->tvPo->currentIndex().row();@
and
@parentModel->index(mapper->currentIndex(), 0, QModelIndex());@
While this should not interfere, you should check what is happening inside the master callback.
Having a look at the source code of the qsqltablemodel, the error above is raised when there are no rows and/or the where clause of the sql query is empty. Could it be that something else has changed your query in the meantime? Probably the select performed after the submitall dirties your data in a way I cannot understand right now. -
thanks again for you quick response fluca1978...
like i said i am just learning this language for the past couple of days and i was curious as to what was the difference between the two.
anyway, what puzzles me is that, the error only appears during the second and succeeding clicks on the QCombobox Activated event if i have to manually select a row in the QTableview (otherwise it worked like a charm). What i did each time in the QCombobox Activated event is that the parentModel is refreshed (if that is the correct term by issuing model->select again) the view linked to that model is also refreshed as well as the mapper to the QLineEdit's. And, the debug shows that I am currently on a record (row) and infact, values are there for the updating... but still it gives the said error.
What puzzles me more is that when I changed the code in the QCombobox Activated event to...
@void listuapo::on_cbStatus_activated(const QString &arg1)
{
if (parentView->selectionModel()->hasSelection()) {
int thisRow = parentView->currentIndex().row();
parentModel->setData(parentModel->index(thisRow, parentModel->fieldIndex("status")), arg1);
if (parentModel->submitAll()) {
parentModel->select();
parentView->setModel(parentModel);
parentView->selectRow(0);
parentView->resizeColumnsToContents();
getFilter();
setChildFilter();
} else
qDebug() << parentModel->lastError();
}
}@the getFilter() & setChildFilter is generaly used in the QCheckbox [For Approval | Denied | Approved] to filter the parentModel data selection.
@void listuapo::getFilter()
{
filter = "";
if (ui->cbForApproval->isChecked())
filter = "status = 'For Approval'" ;
if (ui->cbDenied->isChecked()) {
if (filter.isEmpty())
filter = "status = 'Denied'";
else
filter += " OR status = 'Denied'";
}
if (ui->cbApproved->isChecked()) {
if (filter.isEmpty())
filter = "status = 'Approved'";
else
filter += " OR status = 'Approved'";
}
if (filter.isEmpty())
filter = "noSelection";
parentModel->setFilter(filter);
parentModel->select();
parentView->setModel(parentModel);
parentView->selectRow(0);
parentView->resizeColumnsToContents();
}void listuapo::setChildFilter()
{
if (parentView->selectionModel()->hasSelection())
{
QModelIndex index = parentModel->index(parentView->selectionModel()->currentIndex().row(), 0, QModelIndex());
keyS = index.data().toString();
} else
keyS = "noParentSelection";childModel->setFilter("ponumber = '" + keyS + "'"); childModel->select(); childView->setModel(childModel); childView->resizeColumnsToContents(); parentView->setFocus();
}@
... problem is gone! but, i could not tell the difference from the previous code because each time i call the submitAll, I refreshes the parentMode, parentView and selecting its first row, and I have a signal triggered everytime the row is changed and that setChildFilter() is called...
@void listuapo::updateChildFilter(QItemSelection, QItemSelection)
{
if (parentView->selectionModel()->hasSelection())
{
mapper->setCurrentIndex(parentView->selectionModel()->currentIndex().row());
setChildFilter();
}
}@so why would setChildFilter needs to be called twice? I also dont think the getFilter() is necessary because obviously there hasn't been any change in the check box's. But anyway, it worked! Perhaps, explanation would be dealt with later... If you could help me figure it out, it would be much nicer because i dont really like seeing things work without knowing why :D ... and, thank you for your time, i really appreciate it.
-
You are using models in a not efficient way. The idea behind the model is that it already knows your data, and can flush it back to the storage (database). Therefore you don't need to issue a select after each submitall, or you will load again the same data. Of course this is true for a single client application, while it can be required for a multi-client and multi-user application.
Now, my suspect is that somewhere in your code there is an untying between the model and the mapper, and this is what makes the second trial to fail. I will place clear debug statements in the begin and exit points of each method to try to figure out if there is a nesting of method calls. Second I will remove the "noselection" case from the parent/child. While it is correct, since it produces a query with no result, it requires the database to work and it will be simpler to remove all elements from the model.
Finally, you are issuing a select after the submitall and then do another filtered select with getFilter. Could it be that this two calls mi-align your internal models (in the case you have more than one)? -
yes i have to models... parentModel set to table purchaseorder, and childmodel to table purchaseorderdtl. mapper is on the parentModel.
and true, i read a few about the behavior of a model. i tried to do that on my previous attempt, i did not put select after submit all and thats where the problem occured. In each problem occurrence i noticed that if i would click any of the check boxes that i mentioned above (which click event contains getFilter() & setChildFilter()) the problem would be gone or at least the combobox work on one row then problem occurred again on the other row right after one row is updated. that's why i tried to put it after the submit all and indeed it somehow worked.
and regarding the noselection in the case, i intend to put it because if not, the filter will be reseted by the "", and as a result all data in the table will be displayed which will contradict the purpose of the checkboxes as it meant to filter the data that i wanted to be displayed.
-
for what its worth, and if it doesnt bother you much... here is the entire code. Truly this is my first attempt to create QT/PGSQL application and i am still searching and learning the correct ways to successfully build a QT application.
@#include "listuapo.h"
#include "ui_listuapo.h"#include "gloVars.h"
#include <QDebug>
#include <QSqlError>
#include <QMessageBox>
#include <QModelIndexList>
#include <QSqlQuery>
#include <QSqlRelationalTableModel>
#include <QSqlRelationalDelegate>
#include <QSqlRecord>listuapo::listuapo(QWidget *parent) :
QWidget(parent),
ui(new Ui::listuapo)
{
ui->setupUi(this);
dbConnect();
if (isAdmin | isManager)
ui->cbForApproval->setChecked(true);
else {
ui->cbForApproval->setChecked(false);
ui->cbForApproval->setEnabled(false);
ui->cbApproved->setChecked(true);
}
setupModel();
setupView();
getFilter();
setChildFilter();
connect(ui->pbClose, SIGNAL(clicked()), this, SIGNAL(triggerRefresh()));
QItemSelectionModel *selectionModel = ui->tvPo->selectionModel();
connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateChildFilter(QItemSelection,QItemSelection)));
}listuapo::~listuapo()
{
delete ui;
}void listuapo::on_cbForApproval_clicked()
{
getFilter();
setChildFilter();
}void listuapo::on_cbDenied_clicked()
{
getFilter();
setChildFilter();
}void listuapo::on_cbApproved_clicked()
{
getFilter();
setChildFilter();
}void listuapo::on_tvPo_doubleClicked(const QModelIndex &index)
{}
void listuapo::on_pbClose_clicked()
{
closeIsOk = true;
this->close();
}void listuapo::dbConnect()
{
if(!dbInventory.open()) {
qDebug() << "Error: Unable to connect!\n" + dbInventory.lastError().text();
QMessageBox::critical(0, QWidget::tr("Inventory"), "Unable to Open Database 'Inventory'\n" + dbInventory.lastError().text(), QMessageBox::Ok);
this->deleteLater();
return;
}
}void listuapo::setupModel()
{
parentModel = new QSqlRelationalTableModel(this);
parentModel->setTable("purchaseorder");
parentModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
parentModel->setHeaderData(0, Qt::Horizontal, tr("P.O. #"));
parentModel->setHeaderData(1, Qt::Horizontal, tr("P.O Date"));
parentModel->setHeaderData(2, Qt::Horizontal, tr("Supplier"));
parentModel->setHeaderData(3, Qt::Horizontal, tr("Notes"));
parentModel->setHeaderData(4, Qt::Horizontal, tr("Discount"));
parentModel->setHeaderData(5, Qt::Horizontal, tr("Status"));
parentModel->setHeaderData(6, Qt::Horizontal, tr("Approved"));
parentModel->setHeaderData(7, Qt::Horizontal, tr("P.O. Amount"));
parentModel->setHeaderData(8, Qt::Horizontal, tr("Delivered"));
parentModel->setHeaderData(9, Qt::Horizontal, tr("Delivery Date"));
parentModel->setHeaderData(10, Qt::Horizontal, tr("Received"));
parentModel->setHeaderData(11, Qt::Horizontal, tr("Returned"));
parentModel->setHeaderData(12, Qt::Horizontal, tr("Complete"));
parentModel->setHeaderData(13, Qt::Horizontal, tr("Prepared by"));
parentModel->setHeaderData(14, Qt::Horizontal, tr("Updated by"));
parentModel->setRelation(2, QSqlRelation("supplier", "supplierid", "description"));
parentModel->setSort(0, Qt::AscendingOrder);childModel = new QSqlRelationalTableModel(this); childModel->setTable("purchaseorderdtl"); childModel->setEditStrategy(QSqlRelationalTableModel::OnManualSubmit); childModel->setHeaderData(0, Qt::Horizontal, tr("P.O. #")); childModel->setHeaderData(1, Qt::Horizontal, tr("Seq")); childModel->setHeaderData(2, Qt::Horizontal, tr("Stock Item")); childModel->setHeaderData(3, Qt::Horizontal, tr("Order Qty")); childModel->setHeaderData(4, Qt::Horizontal, tr("Unit Price")); childModel->setHeaderData(5, Qt::Horizontal, tr("Discount")); childModel->setHeaderData(6, Qt::Horizontal, tr("Qty Delivered")); childModel->setHeaderData(7, Qt::Horizontal, tr("Qty Returned")); childModel->setHeaderData(8, Qt::Horizontal, tr("Date Created")); childModel->setHeaderData(9, Qt::Horizontal, tr("Date Returned")); childModel->setRelation(2, QSqlRelation("stock", "stockid", "description")); mapper = new QDataWidgetMapper(this); mapper->setItemDelegate(new QSqlRelationalDelegate(this)); mapper->setModel(parentModel); mapper->addMapping(ui->lePoNumber, parentModel->fieldIndex("ponum")); mapper->addMapping(ui->lePoDate, parentModel->fieldIndex("orderdate")); mapper->addMapping(ui->leSupplier, 2); mapper->addMapping(ui->teNotes, parentModel->fieldIndex("notes")); mapper->addMapping(ui->leTotalDiscount, parentModel->fieldIndex("discount")); mapper->addMapping(ui->cbStatus, parentModel->fieldIndex("status")); mapper->addMapping(ui->leTotalAmount, parentModel->fieldIndex("totalamount")); mapper->addMapping(ui->lePreparedBy, parentModel->fieldIndex("preparedby"));
}
@ -
@void listuapo::setupView()
{
parentView = ui->tvPo;
parentView->setModel(parentModel);
parentView->setSelectionMode(QAbstractItemView::SingleSelection);
parentView->setEditTriggers(QAbstractItemView::NoEditTriggers);
parentView->setSelectionBehavior(QAbstractItemView::SelectRows);
parentView->hideColumn(3);
parentView->hideColumn(6);
parentView->hideColumn(8);
parentView->hideColumn(9);
parentView->hideColumn(10);
parentView->hideColumn(11);
parentView->hideColumn(12);
parentView->resizeColumnsToContents();
parentView->show();childView = ui->tvPoItems; childView->setModel(childModel); childView->setSelectionMode(QAbstractItemView::SingleSelection); childView->setEditTriggers(QAbstractItemView::NoEditTriggers); childView->setSelectionBehavior(QAbstractItemView::SelectRows); childView->hideColumn(6); childView->hideColumn(7); childView->hideColumn(8); childView->hideColumn(9); childView->resizeColumnsToContents(); childView->show(); parentView->verticalHeader()->hide(); childView->verticalHeader()->hide();
}
void listuapo::getFilter()
{
filter = "";
if (ui->cbForApproval->isChecked())
filter = "status = 'For Approval'" ;
if (ui->cbDenied->isChecked()) {
if (filter.isEmpty())
filter = "status = 'Denied'";
else
filter += " OR status = 'Denied'";
}
if (ui->cbApproved->isChecked()) {
if (filter.isEmpty())
filter = "status = 'Approved'";
else
filter += " OR status = 'Approved'";
}
if (filter.isEmpty())
filter = "noSelection";
parentModel->setFilter(filter);
parentModel->select();
parentView->setModel(parentModel);
parentView->selectRow(0);
parentView->resizeColumnsToContents();
}void listuapo::setChildFilter()
{
if (parentView->selectionModel()->hasSelection())
{
QModelIndex index = parentModel->index(parentView->selectionModel()->currentIndex().row(), 0, QModelIndex());
keyS = index.data().toString();
} else
keyS = "noParentSelection";childModel->setFilter("ponumber = '" + keyS + "'"); childModel->select(); childView->setModel(childModel); childView->resizeColumnsToContents(); parentView->setFocus();
}
void listuapo::updateChildFilter(QItemSelection, QItemSelection)
{
if (parentView->selectionModel()->hasSelection())
{
mapper->setCurrentIndex(parentView->selectionModel()->currentIndex().row());
setChildFilter();
}
}void listuapo::closeEvent(QCloseEvent *event)
{
if (!closeIsOk) {
qDebug() << "closing form via window x button is disabled for functionality purposes";
event->ignore();
}}
void listuapo::on_cbStatus_activated(const QString &arg1)
{
if (parentView->selectionModel()->hasSelection()) {
int thisRow = parentView->currentIndex().row();
parentModel->setData(parentModel->index(thisRow, parentModel->fieldIndex("status")), arg1);
if (arg1=="Approved")
parentModel->setData(parentModel->index(thisRow, parentModel->fieldIndex("isapproved")), "Yes");
else
parentModel->setData(parentModel->index(thisRow, parentModel->fieldIndex("isapproved")), "No");
parentModel->setData(parentModel->index(thisRow, parentModel->fieldIndex("approvedby")), userID);
if (parentModel->submitAll()) {
parentModel->select();
parentView->setModel(parentModel);
parentView->selectRow(0);
parentView->resizeColumnsToContents();
getFilter();
setChildFilter();
} else
qDebug() << parentModel->lastError();
}
}
@
this one worked. -
in cbStatus_activated....
@...
if (parentModel->submitAll()) {
parentModel->select();
parentView->setModel(parentModel);
parentView->selectRow(0);
parentView->resizeColumnsToContents();
getFilter();
setChildFilter();
} else
qDebug() << parentModel->lastError();
...@i tried to remove the...
@parentModel->select();
parentView->setModel(parentModel);
parentView->selectRow(0);
parentView->resizeColumnsToContents();
@
because it is also in getFilter, but the problem comes back. -
thanks for the heads up volker... apologies from a newbie here, please accept.
Thank you.