Yet another QSqlTableModel removeRows issue
-
Here I read:
Removes count rows starting at row. Since this model does not support hierarchical structures, parent must be an invalid model index.
When the edit strategy is OnManualSubmit, deletion of rows from the database is delayed until submitAll() is called.
Before a row is deleted from the database, the beforeDelete() signal is emitted.In my application I do:
void DialogTechnologies::on_btnRemove_clicked() { if (ui->table->selectionModel()->hasSelection()) { int row = ui->table->selectionModel()->currentIndex().row(); _model->removeRow(row); _model->submitAll(); } } void DialogTechnologies::on_btnRemoveAll_clicked() { _model->removeRows(0, _model->rowCount()); _model->submitAll(); }
Both
remove*
methods returntrue
but nothing happens on the database.
Questions:- what does actually mean that "parent must be an invalid model index"?
- what signal should I use to refresh the view, after the row(s) deletion? Is
dataChanged
emitted?
As side note: in the help integrated in the IDE I read instead:
The base class implementation does nothing and returns false.
This makes no sense to me. First, it returs
true
and then why they would have implemented theinsertRows
function but not theremoveRows
one? By the way, also forinsertRows
the help says the "base class implementation does nothing and returns false", but this is not true - it works and it returnstrue
! I'm confused... -
Both remove* methods return true
Have to take your word on that, because it's not in your code....
but nothing happens on the database.
How do you check that?
The base class implementation does nothing and returns false.
That's for whatever the base class is, maybe
QAbstractItemModel
, I haven't looked.QSqlTableModel
does have an implementation.what does actually mean that "parent must be an invalid model index"?
bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent = QModelIndex())
has a defaultQModelIndex()
which is "an invalid model index", you are calling it right, don't worry about it.what signal should I use to refresh the view, after the row(s) deletion? Is dataChanged emitted?
dataChanged
is only for updates. For insertion/deletion it'sbeginRemoveRows
/rowsAboutToBeRemoved
/rowsRemoved()
etc. ButQSqlTableModel
does this for you. You don't have to raise signals, just ensure your view is properly connected to model.Why does it (apparently) not work? I don't know, but I do know you need to check the returns result of the
_model->submitAll();
because maybe it's failing at database side.... When you write code like this, please look at return results and always check all of them, especially while developing/you don't know what is going on.... -
@JonB said in Yet another QSqlTableModel removeRows issue:
@Mark81
Have to take your word on that, because it's not in your code....I printed the return values with
qDebug()
.How do you check that?
With "DB Browser for SQLite".
That's for whatever the base class is, maybe
QAbstractItemModel
, I haven't looked.QSqlTableModel
does have an implementation.Good.
dataChanged
is only for updates. For insertion/deletion it'sbeginRemoveRows
/rowsAboutToBeRemoved
/rowsRemoved()
etc. ButQSqlTableModel
does this for you. You don't have to raise signals, just ensure your view is properly connected to model.Got it, thanks.
Why does it (apparently) not work? I don't know, but I do know you need to check the returns result of the
_model->submitAll();
because maybe it's failing at database side.... When you write code like this, please look at return results and always check all of them, especially while developing/you don't know what is going on....Good shot, the problem is
sumbitAll()
that returnsfalse
.
And inspecting thelastError()
I found the root cause of my problem:QSqlError("19", "Unable to fetch row", "FOREIGN KEY constraint failed")
It's correct, indeed.
I didn't think about it becase I was afraid I didn't understand the documentation! -
@Mark81
Which is what I suspected would be the reason :) Just be aware: if you use a Qt function which returns a result, especially if it's calling some operation like "updating a database", when you first read the documentation to use it put it code to capture/test the result! :) I can see you have then used the docs to calllastError()
, which is excellent! -
As a general rule, if you have naked statement that return a boolean and your code just assumes it returns true then it's a good idea to wrap it in
Q_ASSUME()
. This will trigger a debug assertion if the method actually returned false and does nothing in release mode.
e.g.Q_ASSUME(_model->submitAll());
-
@Mark81
And what @VRonin has written is certainly better then no test at all! However, to be clear, in a case like this where an operation could genuinely return false, you must/should not just do this, you must provide proper error handling. Otherwise it works fine in your own development tests, but silently fails and does not check when you send out your release code, and you/the user have no idea it did fail or why! You probably already know that, just saying for other readers.... -
@JonB said in Yet another QSqlTableModel removeRows issue:
@Mark81
And what @VRonin has written is certainly better then no test at all! However, to be clear, in a case like this where an operation could genuinely return false, you must/should not just do this, you must provide proper error handling. Otherwise it works fine in your own development tests, but silently fails and does not check when you send out your release code, and you/the user have no idea it did fail or why! You probably already know that, just saying for other readers....Yes of course. As said I was a step before that - due to the doubts on the docs.
Now, I've already connected a signal to show an error message to the user.