sqlite - How to restore back a null value on a column that has a relation? - Stack Overflow

In Qt 6.8.2 I subclassed QTableView in order to catch the pressing of Key_Delete:#include <QTableVi

In Qt 6.8.2 I subclassed QTableView in order to catch the pressing of Key_Delete:

#include <QTableView>
#include <QKeyEvent>

class TableView : public QTableView
{
    Q_OBJECT

public:
    explicit TableView(QWidget *parent = nullptr) : QTableView(parent) { }

protected:
    void keyPressEvent(QKeyEvent *event) override
    {
        if (event->key() == Qt::Key_Delete) emit keyCancelPressed(currentIndex());
        else QTableView::keyPressEvent(event);
    }

signals:
    void keyCancelPressed(const QModelIndex &current);
};

A QSqlRelationalTableModel is assigned to the TableView and the relations are set:

QSqlDatabase db = QSqlDatabase::database(DATABASE_NAME);
QSqlRelationalTableModel *model = new QSqlRelationalTableModel(this, db);
model->setTable(TABLE_NAME);
model->setEditStrategy(QSqlRelationalTableModel::OnRowChange);
model->setRelation(MODEL_COL_ID_HUE_MODE), QSqlRelation("hueModes", "id", "name"));    
model->select();
ui->myTableView->setModel(_model);
ui->myTableView->setItemDelegate(new QSqlRelationalDelegate(ui->myTableView));

then a lambda is created to set back to null a field if the delete key is pressed:

connect(ui->myTableView, &TableView::keyCancelPressed, this, [=](const QModelIndex &current)
{
    model->setData(current, QVariant());
});

The actual behavior is the following:

  • if the current cell contains a plain value the code works and a null value is restored back to the model

  • if the current cell contains a relation the code has no effect - the current value is still there

Even if it should not matter since we're still at model level, my SQL columns don't have the NOT NULL clause:

CREATE TABLE timelines \
    (idProgram INT NOT NULL, \
     time INT NOT NULL, \
     h REAL, \
     s REAL, \
     l REAL, \
     idHueMode INT, \
     PRIMARY KEY (idProgram, time), \
     FOREIGN KEY (idProgram) REFERENCES programs(id) ON DELETE CASCADE ON UPDATE CASCADE, \
     FOREIGN KEY (idHueMode) REFERENCES hueModes(id) ON DELETE SET DEFAULT ON UPDATE CASCADE);";

By the way, the code works even on the time column since we're playing with the model as said. Of course, when I try to submit I will receive an error in that case.

Trying to debug I changed the code to:

qDebug() << model->setData(current, QVariant());
qDebug() << model->lastError();

and the output is:

false
QSqlError("", "", "")

not very useful, indeed. How to restore back a null value on a column that has a relation?

In Qt 6.8.2 I subclassed QTableView in order to catch the pressing of Key_Delete:

#include <QTableView>
#include <QKeyEvent>

class TableView : public QTableView
{
    Q_OBJECT

public:
    explicit TableView(QWidget *parent = nullptr) : QTableView(parent) { }

protected:
    void keyPressEvent(QKeyEvent *event) override
    {
        if (event->key() == Qt::Key_Delete) emit keyCancelPressed(currentIndex());
        else QTableView::keyPressEvent(event);
    }

signals:
    void keyCancelPressed(const QModelIndex &current);
};

A QSqlRelationalTableModel is assigned to the TableView and the relations are set:

QSqlDatabase db = QSqlDatabase::database(DATABASE_NAME);
QSqlRelationalTableModel *model = new QSqlRelationalTableModel(this, db);
model->setTable(TABLE_NAME);
model->setEditStrategy(QSqlRelationalTableModel::OnRowChange);
model->setRelation(MODEL_COL_ID_HUE_MODE), QSqlRelation("hueModes", "id", "name"));    
model->select();
ui->myTableView->setModel(_model);
ui->myTableView->setItemDelegate(new QSqlRelationalDelegate(ui->myTableView));

then a lambda is created to set back to null a field if the delete key is pressed:

connect(ui->myTableView, &TableView::keyCancelPressed, this, [=](const QModelIndex &current)
{
    model->setData(current, QVariant());
});

The actual behavior is the following:

  • if the current cell contains a plain value the code works and a null value is restored back to the model

  • if the current cell contains a relation the code has no effect - the current value is still there

Even if it should not matter since we're still at model level, my SQL columns don't have the NOT NULL clause:

CREATE TABLE timelines \
    (idProgram INT NOT NULL, \
     time INT NOT NULL, \
     h REAL, \
     s REAL, \
     l REAL, \
     idHueMode INT, \
     PRIMARY KEY (idProgram, time), \
     FOREIGN KEY (idProgram) REFERENCES programs(id) ON DELETE CASCADE ON UPDATE CASCADE, \
     FOREIGN KEY (idHueMode) REFERENCES hueModes(id) ON DELETE SET DEFAULT ON UPDATE CASCADE);";

By the way, the code works even on the time column since we're playing with the model as said. Of course, when I try to submit I will receive an error in that case.

Trying to debug I changed the code to:

qDebug() << model->setData(current, QVariant());
qDebug() << model->lastError();

and the output is:

false
QSqlError("", "", "")

not very useful, indeed. How to restore back a null value on a column that has a relation?

Share Improve this question asked Mar 8 at 9:31 MarkMark 5,22511 gold badges73 silver badges152 bronze badges 2
  • 1 I don't know how it could be done on C++, but try to call the setData() of the QSqlTableModel (from which the relational model inherits). – musicamante Commented Mar 8 at 13:36
  • @musicamante I guess what I proposed in my own answer is what you suggested – Mark Commented Mar 9 at 9:25
Add a comment  | 

1 Answer 1

Reset to default 1

The source code provides the explanation:

/*!
    Sets the data for the \a role in the item with the specified \a
    index to the \a value given. Depending on the edit strategy, the
    value might be applied to the database at once, or it may be
    cached in the model.

    Returns \c true if the value could be set, or false on error (for
    example, if \a index is out of bounds).

    For relational columns, \a value must be the index, not the
    display value. The index must also exist in the referenced
    table, otherwise the function returns \c false.

    \sa editStrategy(), data(), submit(), revertRow()
*/
bool QSqlRelationalTableModel::setData(const QModelIndex &index, const QVariant &value,
                                       int role)
{
    Q_D(QSqlRelationalTableModel);
    if ( role == Qt::EditRole && index.column() > 0 && index.column() < d->relations.size()
            && d->relations.at(index.column())->isValid()) {
        auto relation = d->relations.at(index.column());
        if (!relation->isDictionaryInitialized())
            relation->populateDictionary();
        if (!relation->dictionary.contains(value.toString()))
            return false;
    }
    return QSqlTableModel::setData(index, value, role);
}

The behavior is actually by design:

For relational columns, [...] The index must also exist in the referenced table, otherwise the function returns false.

But you can change the behavior overriding the default setData() function calling the base function in QSqlTableModel as per @musicamante's comment:

bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
{
    if (value.isNull()) return QSqlTableModel::setData(index, value, role);
    else return QSqlRelationalTableModel::setData(index, value, role);
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744899002a4599866.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信