QSqlRelationalTableModel with multiple Joins?



  • Hi.

    I have a database which has 2 many to many relations. This means 2 intermediate tables.

    • Employee
    • Position (like programmer, 3d artist...)
    • Resource (like printer, plotter...)
    • EmployeePosition (many to many: N employees assume N positions)
    • EmployeeResource (many to many: N employees have access to N resources)

    In my Table View I only see Employee's fields:

    _relationalModel = new QSqlRelationalTableModel(ui->tableView, *_db);
    _relationalModel->setTable("Employee");
    _relationalModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
    _relationalModel->setRelation(3, QSqlRelation("Country", "id", "Name"));    // Employee.BirthplaceID
    _relationalModel->setRelation(5, QSqlRelation("Country", "id", "Name"));    // Employee.ResidenceID
    _relationalModel->select();
    
    ui->tableView->setModel(_relationalModel);
    ui->tableView->setItemDelegate(new QSqlRelationalDelegate(ui->tableView));
    

    The result is just the Employee table with its fields:

    0_1518010848861_b2411ef1-c0d4-4ea7-a66b-47b5a12125b7-image.png

    However, I need something more.

    I want to join the Employee table with EmployeePosition and EmployeeResource so that I can also see, for each employee, their positions and resources (if they have any!)

    What I need, hence, is a full outer join

    SELECT * 
      FROM Employee
      LEFT JOIN EmployeePosition ON Employee.id = EmployeePosition.idEmployee
      LEFT JOIN Position ON EmployeePosition.idPosition = Position.id
      
      LEFT JOIN EmployeeResource ON Employee.id = EmployeeResource.idEmployee
      LEFT JOIN Resource ON Resource.id = EmployeeResource.idResource
      
      UNION ALL
      
     SELECT * 
      FROM Employee
      RIGHT JOIN EmployeePosition ON Employee.id = EmployeePosition.idEmployee
      RIGHT JOIN Position ON EmployeePosition.idPosition = Position.id
    
      RIGHT JOIN EmployeeResource ON Employee.id = EmployeeResource.idEmployee
      RIGHT JOIN Resource ON Resource.id = EmployeeResource.idResource
      WHERE Employee.id IS NULL
    

    In MySQL there's no Full Outer Join, so I needed to write it another way

    The query above gives me everything I want to know and see on the Table View

    But I also want to be able to edit the fields (like I do now, but this time with this larger table).

    Have you got any suggestions about how to achieve this result?

    Thanks in advance!



  • @devhobby
    So far as I know, you can only do the simple stuff through QSqlRelationalTableModel, i.e. just look up values by foreign key. It's pretty limited, and that's it.

    I shall watch this thread to see if you receive any better ideas....



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby
    So far as I know, you can only do the simple stuff through QSqlRelationalTableModel, i.e. just look up values by foreign key. It's pretty limited, and that's it.

    I shall watch this thread to see if you receive any better ideas....

    Yes! It's what I've been noticing...

    You can view it the way you want -> not editable
    You can edit the records -> queries are limited!

    I'm in front of this scary riddle, and can't find a compromise!



  • You'll need to do it manually:

    • create a QSqlQuery that fetches what you want
    • iterate over the results and fill a QStandardItemModel with them
    • connect the QAbstractItemModel::dataChanged signal to a slot that runs an UPDATE/INSERT query on the db


  • @devhobby

    QSqlRelationalTableModel is only QSqlTableModel with the addition of foreign key lookup of given columns into other tables, so that for example you can display comboboxes/labels for the looked-up values. So as I see it your issue is really at the QSqlTableModel level?

    QSqlTableModel really wants to map to an actual table (not just a SELECT statement) so that it can INSERT?DELETE/UPDATE. That's why editing can't be as flexible as SELECTing.

    I've just seen @VRonin has posted. To the effect that you do indeed need to do it manually --- you can only go the generic QSqlQuery level, not the QSqlTableModel one, and hence not QSqlRelationalTableModel either. Which is about what I found... :(



  • @VRonin

    You'll need to do it manually
    ...

    You are right, but he loses the QSqlRelationalTableModel foreign key look up features, and then has to write those for himself. As per my post above. Do you agree?

    [EDIT: Actually, the point is once you go down to QSqlQuery you lose all QSqlTableModel level of functionality, and that's what is irritating.]



  • That's correct. Although providing labels/comboboxes is easily implemented via a separate QSqlTableModel/QSqlQueryModel that has all the options and a delegate (in fact that's what QSqlRelationalDelegate does for QSqlRelationalTableModel).

    You'd need something like

    class RelationDelegate : public QStyledItemDelegate{
        Q_OBJECT
        Q_PROPERTY(QAbstractItemModel* relationModel READ relationModel WRITE setRelationModel NOTIFY relationModelChanged)
        Q_PROPERTY(int keyColumn READ keyColumn WRITE setKeyColumn NOTIFY keyColumnChanged)
        Q_PROPERTY(int valueColumn READ valueColumn WRITE setValueColumn NOTIFY valueColumnChanged)
        Q_PROPERTY(const QPersistentModelIndex& relationModelRoot READ relationModelRoot WRITE setRelationModelRoot NOTIFY relationModelRootChanged)
        Q_DISABLE_COPY(RelationDelegate)
    public:
        explicit RelationDelegate(QObject* parent = Q_NULLPTR)
            :QStyledItemDelegate(parent)
            , m_relModel(Q_NULLPTR)
            , m_keyCol(0)
            , m_valueCol(1)
        {
            QObject::connect(this, &RelationDelegate::relationModelChanged, this, &RelationDelegate::viewNeedUpdate);
            QObject::connect(this, &RelationDelegate::keyColumnChanged, this, &RelationDelegate::viewNeedUpdate);
            QObject::connect(this, &RelationDelegate::valueColumnChanged, this, &RelationDelegate::viewNeedUpdate);
            QObject::connect(this, &RelationDelegate::relationModelRootChanged, this, &RelationDelegate::viewNeedUpdate);
        }
        Q_SIGNAL void relationModelRootChanged(const QPersistentModelIndex& rootIdx);
        const QPersistentModelIndex& relationModelRoot() const { return m_relModelRoot; }
        void setRelationModelRoot(const QPersistentModelIndex& rootIdx)
        {
            if (m_relModelRoot == rootIdx)
                return;
            Q_ASSERT(!rootIdx.isValid() || rootIdx.model()==m_relModel);
            m_relModelRoot = rootIdx;
            relationModelRootChanged(m_relModelRoot);
        }
        Q_SIGNAL void viewNeedUpdate();
        QAbstractItemModel* relationModel() const { return m_relModel; };
        Q_SIGNAL void relationModelChanged(QAbstractItemModel* relModel);
        void setRelationModel(QAbstractItemModel* relModel)
        {
            if (relModel == m_relModel) 
                return;
            m_relModel = relModel;
            relationModelChanged(m_relModel);
        }
        int keyColumn() const { return m_keyCol; }
        Q_SIGNAL void keyColumnChanged(int keyCol);
        void setKeyColumn(int keyCol)
        {
            if (keyCol == m_keyCol)
                return;
            m_keyCol = keyCol;
            keyColumnChanged(m_keyCol);
        }
        int valueColumn() const { return m_valueCol; }
        Q_SIGNAL void valueColumnChanged(int valueCol);
        void setValueColumn(int valueCol)
        {
            if (valueCol == m_valueCol) 
                return;
            m_valueCol = valueCol;
            valueColumnChanged(m_valueCol);
        }
        QString displayText(const QVariant& value, const QLocale& locale) const Q_DECL_OVERRIDE{
            if (m_relModel) {
                const int relModelRows = m_relModel->rowCount(m_relModelRoot);
                for (int i = 0; i < relModelRows; ++i) {
                    if (m_relModel->index(i, m_keyCol, m_relModelRoot).data() == value)
                        return QStyledItemDelegate::displayText(m_relModel->index(i, m_valueCol, m_relModelRoot).data(), locale);
                }
            }
            return QStyledItemDelegate::displayText(value, locale);
        }
         QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{
            if (!m_relModel) 
                return QStyledItemDelegate::createEditor(parent, option, index);
            QComboBox* editor = new QComboBox(parent);
            editor->setModel(m_relModel);
            editor->setRootModelIndex(m_relModelRoot);
            editor->setModelColumn(m_valueCol);
            return editor;
        }
        void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE{
            if (!m_relModel) 
                return QStyledItemDelegate::setEditorData(editor, index);
            QComboBox* editCombo = qobject_cast<QComboBox*>(editor);
            Q_ASSERT(editCombo);
            const int relModelRows = m_relModel->rowCount(m_relModelRoot);
            for (int i = 0; i < relModelRows; ++i) {
                if (m_relModel->index(i, m_keyCol, m_relModelRoot).data() == index.data()) {
                    editCombo->setCurrentIndex(i);
                    return;
                }
            }
        }
        void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE{
            if (!m_relModel)
                return QStyledItemDelegate::setModelData(editor, model, index);
            QComboBox* editCombo = qobject_cast<QComboBox*>(editor);
            Q_ASSERT(editCombo);
            const int currIdx = editCombo->currentIndex();
            model->setData(index, m_relModel->index(currIdx, m_keyCol, m_relModelRoot).data());
        }
        void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{
            if (!m_relModel)
                return QStyledItemDelegate::updateEditorGeometry(editor, option, index);
            editor->setGeometry(option.rect);
        }
        
    private:
        QAbstractItemModel* m_relModel;
        int m_keyCol;
        int m_valueCol;
        QPersistentModelIndex m_relModelRoot;
    };
    

    and connect the viewNeedUpdate to the update of the view



  • @VRonin
    Yes. I appreciate this. Like I edited my previous, it's actually the QSqlTableModel level I miss, QSqlRelationalTableModel doesn't add that much. I realize each one is written on top of the next one in Qt, so of course you can design them on top of QSqlQuery.

    That's a bit of code you've written there! It'll be even more to model QSqlTableModel too :)



  • I wanted to take a different approach:

    Send a custom query to the table view in read-only mode

    Then, a right click (or another method) will change the content of the record through code.

    The problem is: I can't even send a custom query!

    My query (similar to the one I posted above) has some joins in it.

    But why can't I use the table returned by a query as the table to display on the Table View?

    Is all this really so limited?



  • @devhobby
    You are using a QTableView, right? You can use that against a QSqlQueryModel, which is a model based against a query rather than an actual table.



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby
    You are using a QTableView, right? You can use that against a QSqlQueryModel, which is a model based against a query rather than an actual table.

    No, I'm using a QSqlQueryModel

            _queryModel = new QSqlQueryModel(ui->tableView);
    
           _queryModel->setQuery("SELECT Employee.id, Employee.Name, Employee.Surname, Birthplace.Name as 'Birthplace', Employee.Birthdate, Residence.Name as 'Residence', Employee.Qualification, Position.Name as 'Position', EmployeePosition.EngagementDate, Resource.Name as 'Resource', EmployeeResource.AcquisitionDate"
                        "FROM Employee"
                        "LEFT JOIN EmployeePosition ON Employee.id = EmployeePosition.idEmployee"
                        "LEFT JOIN Position ON EmployeePosition.idPosition = Position.id"
    
                        "LEFT JOIN EmployeeResource ON Employee.id = EmployeeResource.idEmployee"
                        "LEFT JOIN Resource ON Resource.id = EmployeeResource.idResource"
    
                        "LEFT JOIN Country AS Birthplace ON Employee.BirthplaceID = Birthplace.id"
                        "LEFT JOIN Country AS Residence ON Employee.ResidenceID = Residence.id", *_db);
    
            ui->tableView->setModel(_queryModel);
            ui->tableView->show();
    

    And the Table View is blank

    I also tried with QSqlTableModel

            _tableModel = new QSqlTableModel(ui->tableView, *_db);
    
            QString myQuery = "SELECT Employee.id, Employee.Name, Employee.Surname, Birthplace.Name as 'Birthplace', Employee.Birthdate, Residence.Name as 'Residence', Employee.Qualification, Position.Name as 'Position', EmployeePosition.EngagementDate, Resource.Name as 'Resource', EmployeeResource.AcquisitionDate"
                            "FROM Employee"
                            "LEFT JOIN EmployeePosition ON Employee.id = EmployeePosition.idEmployee"
                            "LEFT JOIN Position ON EmployeePosition.idPosition = Position.id"
    
                            "LEFT JOIN EmployeeResource ON Employee.id = EmployeeResource.idEmployee"
                            "LEFT JOIN Resource ON Resource.id = EmployeeResource.idResource"
    
                            "LEFT JOIN Country AS Birthplace ON Employee.BirthplaceID = Birthplace.id"
                            "LEFT JOIN Country AS Residence ON Employee.ResidenceID = Residence.id";
    
            _tableModel->setTable(myQuery );
            _tableModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
            _tableModel->select();
    
            ui->tableView->setModel(_tableModel);
            ui->tableView->show();
    

    Same issue: Table View is blank.



  • @devhobby
    I was asking you to confirm that in ui->tableView->setModel(_queryModel); the type of your ui->tableView is a QTableView?

    Anyway, assuming it is, what's this:

     _queryModel = new QSqlQueryModel(ui->tableView);
    

    What's that parent doing there? (Haven't seen this before, just checking.)

    Does QSqlQueryModel::lastError() tell you anything after setQuery() or later?

    Hang on! I don't do C++, but the way you've written your query, does C++ join the strings with a space between? Because if not (and I don't think it does) your words are all touching each other, and you have a SQL error!! Make sure your query is right, and check that lastError() thing, e.g.

    QSqlQueryModel model;
    model.setQuery("select *"
                   "from MyTable");
    if (model.lastError().isValid())
        qDebug() << model.lastError();
    

    Because I think the principle should be working fine otherwise.



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:
    does C++ join the strings with a space between? Because if not (and I don't think it does) your words are all touching each other, >and you have a SQL error!! Make sure your query is right, and check that lastError() thing, e.g.
    Because I think the principle should be working fine otherwise.

    Oh gosh! I completely forgot to put the spaces before each line! Thanks

    By the way, once that I get all of this setup... now I need to find a way to customize a cell (item).
    I successfully retrieve its index... but I'd like to change its background color, for instance

    And since I'm using a Table View and not a Table Widget, I only see item delegates.

    How can I achieve what I want?





  • @VRonin said in QSqlRelationalTableModel with multiple Joins?:

    See https://forum.qt.io/topic/85973/how-to-simply-change-the-background-colour-of-a-cell-inside-a-tableview

    Thanks but before doing that, I was wondering if there's a way to change the content of the cell by directly typing into it.

    I just want to change the data of the cell and, once changed, color that cell in a different color to alert the user of the change of that particular item/cell.

    Seems a lot of hard-coding work... maybe I should find another convenient way... but for now let's see what comes out



  • @devhobby
    If you want to edit in a QTableView (right?), what have you set your http://doc.qt.io/qt-5/qabstractitemview.html#editTriggers-prop to?



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby
    If you want to edit in a QTableView (right?), what have you set your http://doc.qt.io/qt-5/qabstractitemview.html#editTriggers-prop to?

    QAbstractItemView::DoubleClicked

    ui->tableView->setModel(_queryModel);
    ui->tableView->verticalHeader()->hide();
    ui->tableView->setEditTriggers(QAbstractItemView::DoubleClicked);
    ui->tableView->show();
    


  • @devhobby
    And you are saying that when you double-click...? Nothing at all happens?

    If that is the case, I can only imagine your model is read-only, to do with it being a SELECT and not a table? To be clear, you won't be "changing the data of the cell" per se, you'll be changing the data in the model that cell is displaying.



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby
    And you are saying that when you double-click...? Nothing at all happens?

    If that is the case, I can only imagine your model is read-only, to do with it being a SELECT and not a table?

    Yes, as stated above I used my custom query to do all the joins.

    By the way, I don't expect to change the database directly.

    I just want to visually edit the cell without applying any changes to the database.

    Once the user changes the text of the cell, the new text is immediately visible and I store it in a QString

    When the user presses the button "Commit changes" I will have a series of changes that the user wants to apply to the database -> I now need to make them real sending a custom query to the database.



  • @devhobby
    I'm a little lost. You wrote:

    Thanks but before doing that, I was wondering if there's a way to change the content of the cell by directly typing into it.

    I thought you were saying when double-click it does not let you edit, nothing happens, or whatever. Now I think you're saying it does let you edit? I don't know if you have a question here?



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby
    I'm a little lost. You wrote:

    Thanks but before doing that, I was wondering if there's a way to change the content of the cell by directly typing into it.

    I thought you were saying when double-click it does not let you edit, nothing happens, or whatever. Now I think you're saying it does let you edit? I don't know if you have a question here?

    Yes I asked if there's a way to change the content of the cell by directly typing into it

    Because now, when I double click, nothing happens.

    Once the cell is edited, I'd also like to change its background color... but that's another story



  • @devhobby said in QSqlRelationalTableModel with multiple Joins?:

    Once the user changes the text of the cell, the new text is immediately visible and I store it in a QString

    Because now, when I double click, nothing happens.

    Sorry, but if "nothing happens" when you double-click cell to edit, how come you talk about "Once the user changes the text of the cell"? Maybe I'm being dumb, but I just don't get it!



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby said in QSqlRelationalTableModel with multiple Joins?:

    Once the user changes the text of the cell, the new text is immediately visible and I store it in a QString

    Because now, when I double click, nothing happens.

    Sorry, but if "nothing happens" when you double-click cell to edit, how come you talk about "Once the user changes the text of the cell"? Maybe I'm being dumb, but I just don't get it!

    Don't worry! I'm sorry, I'm probably using the wrong tenses to express myself.

    "Once the user changes the text of the cell, the new text is immediately visible and I store it in a QString"

    Is the prediction of what I want to happen... and can't manage to make it happen actually



  • @devhobby

    Is the prediction of what I want to happen... and can't manage to make it happen actually

    Ohhhh...! :)

    OK, I would expect you've done the right stuff. I believe that's what our code does. I can only think of what I suggested: that the SELECT makes the model read-only, and editing is not allowed.

    I shall step aside and you need an expert here to guide you further....



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby

    Is the prediction of what I want to happen... and can't manage to make it happen actually

    Ohhhh...! :)

    OK, I would expect you've done the right stuff. I believe that's what our code does. I can only think of what I suggested: that the SELECT makes the model read-only, and editing is not allowed.

    I shall step aside and you need an expert here to guide you further....

    Yes, the custom query is most likely keeping my Table View read-only.

    I just want to visually edit the cells...

    This is far more complicated than I thought...



  • @devhobby
    http://doc.qt.io/qt-5/qitemdelegate.html#details must be to do with it.

    I could easily be wrong(!), but I thought the idea of the model/view would be that if you edit it would save the value back to the model, not "just give you some string". You can doubtless play with the delegate to do something else...

    P.S.
    Look at http://www.qtcentre.org/threads/38338-Can-t-edit-my-QTableView-cells:

    Check if the item is actually editable: (MyTable->model()->flags(idx) & Qt::ItemIsEditable). If not, make it so.

    https://stackoverflow.com/questions/28186118/how-to-make-qtableview-to-enter-the-editing-mode-only-on-double-click:

    Setting a Qt.ItemIsEnabled flag makes the QTableView items editable. To enter the item's editing mode the user can simply double-click it.

    EDIT: You have to loop over every item in your table view to make it individually editable....



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby
    http://doc.qt.io/qt-5/qitemdelegate.html#details must be to do with it.

    I could easily be wrong(!), but I thought the idea of the model/view would be that if you edit it would save the value back to the model, not "just give you some string". You can doubtless play with the delegate to do something else...

    P.S.
    Look at http://www.qtcentre.org/threads/38338-Can-t-edit-my-QTableView-cells:

    Check if the item is actually editable: (MyTable->model()->flags(idx) & Qt::ItemIsEditable). If not, make it so.

    https://stackoverflow.com/questions/28186118/how-to-make-qtableview-to-enter-the-editing-mode-only-on-double-click:

    Setting a Qt.ItemIsEnabled flag makes the QTableView items editable. To enter the item's editing mode the user can simply double-click it.

    I'd like to change Table View's flag to Qt::ItemIsEditable... but can't find a way to do it

    There's no setFlags() method here...



  • @devhobby
    You have to loop over every item in your table view to make it individually editable (if you're not creating the items yourself, or unless someone suggests a way to cause that to happen as it binds to your model, e.g. https://stackoverflow.com/a/28226056/489865)....



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby
    You have to loop over every item in your table view to make it individually editable (if you're not creating the items yourself, or unless someone suggests a way to cause that to happen as it binds to your model)....

    Suppose I'm inside a for loop where index is a QModelIndex

    ui->tableView->model()->data(index). ???
    

    There's no setFlags here still



  • @devhobby

    I think we use QTableWidget, not just QTableView.

    http://www.qtcentre.org/threads/46245-QTableWidgetItem-setflags-strange-behavior?p=209282#post209282 shows you accessing QTableWidgetItem. http://doc.qt.io/qt-5/qtablewidget.html#item

    http://doc.qt.io/qt-5/qtablewidget.html#details:

    The QTableWidget class provides an item-based table view with a default model.
    Table widgets provide standard table display facilities for applications. The items in a QTableWidget are provided by QTableWidgetItem.
    If you want a table that uses your own data model you should use QTableView rather than this class.

    But for QTableView I previously gave you http://www.qtcentre.org/threads/38338-Can-t-edit-my-QTableView-cells?p=176235#post176235:

    Qt::ItemFlags MyTableModel::flags (const QModelIndex &index) const
    {
       return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
    }
    

    http://doc.qt.io/qt-5/qabstractitemmodel.html#flags, http://doc.qt.io/qt-5/qabstracttablemodel.html#flags works off the model, so presumably your ui->tableView->model()->flags(index).

    Ah ha!! Here's what we wanted to know:
    http://doc.qt.io/qt-5/qsqlquerymodel.html#details

    The model is read-only by default. To make it read-write, you must subclass it and reimplement setData() and flags(). Another option is to use QSqlTableModel, which provides a read-write model based on a single database table.



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby

    I think we use QTableWidget, not just QTableView.

    http://www.qtcentre.org/threads/46245-QTableWidgetItem-setflags-strange-behavior?p=209282#post209282 shows you accessing QTableWidgetItem. http://doc.qt.io/qt-5/qtablewidget.html#item

    http://doc.qt.io/qt-5/qtablewidget.html#details:

    The QTableWidget class provides an item-based table view with a default model.
    Table widgets provide standard table display facilities for applications. The items in a QTableWidget are provided by QTableWidgetItem.
    If you want a table that uses your own data model you should use QTableView rather than this class.

    But for QTableView I previously gave you http://www.qtcentre.org/threads/38338-Can-t-edit-my-QTableView-cells?p=176235#post176235:

    Qt::ItemFlags MyTableModel::flags (const QModelIndex &index) const
    {
       return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
    }
    

    At this point I don't even know which of the 2 (table view/table widget) is the most appropriate for my situation.

    The last post you linked says to reimplement the method flags() but I don't understand: after inheriting from AbstractItemModel (and reimplementing the method), what am I supposed to do?



  • @devhobby
    Look at the last bit I just added to my post above.

    To make it [QSqlQueryModel] read-write, you must subclass it and reimplement setData() and flags().

    (And BTW when you've done that there won't be any "iterating over items and setting the editable flag", your items will be editable through your reimplementation of QSqlQueryModel::flags().)

    You understand how to "subclass", and how to "reimplement [override] virtual methods", don't you?



  • @devhobby
    I'm going back to your very original post and thinking about what you're actually trying to achieve anyway. The code we're talking about is all well & good, if you want to proceed, but I'm wondering about your expectations of the interface.

    In your pic of Employee table only, you have 1 row per employee. When you say:

    I want to join the Employee table with EmployeePosition and EmployeeResource so that I can also see, for each employee, their positions and resources (if they have any!)

    what are you expecting the table interface to be for the multiple positions & resources you say employees have?

    It's all very well to say:

    What I need, hence, is a full outer join
    The query above gives me everything I want to know and see on the Table View

    [I'm trusting that your code implements FULL OUTER JOIN] but left to its own devices this will mean you have many rows per employee, for each position/resource variant. Is that what you intend??

    One thing to understand: when you're using QSqlRelationalTableModel so that you can "look up" position/resource names, the combobox you'll get will only ever allow single selection, if you're imagining that it might provide multiple selection it won't.



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby
    Look at the last bit I just added to my post above.

    To make it [QSqlQueryModel] read-write, you must subclass it and reimplement setData() and flags().

    (And BTW when you've done that there won't be any "iterating over items and setting the editable flag", your items will be editable through your reimplementation of QSqlQueryModel::flags().)

    You understand how to "subclass", and how to "reimplement [override] virtual methods", don't you?

    I have my CustomModel class now deriving from QSqlQueryModel

    #ifndef CUSTOMMODEL_H
    #define CUSTOMMODEL_H
    #include <QSqlQueryModel>
    
    class CustomModel : public QSqlQueryModel
    {
        Q_OBJECT
        
    public:
        bool setData(const QModelIndex &index, const QVariant &value, int role) override;
        Qt::ItemFlags flags(const QModelIndex &index) const override;
    };
    
    #endif
    

    The problem is...

    0_1518117868934_4f8b1d3f-c5cf-43c9-b900-be0b15d1ac81-image.png

    The linker somehow can't match the signatures... weird.

    I checked this link for example [ https://code.woboq.org/qt5/qtbase/src/corelib/itemmodels/qabstractitemmodel.h.html ]

    and both setData() and flags() are the same way I overloaded them...



  • @devhobby
    Now you're talking C++ esotericism, and I'm a Python Qt guy anyway. This one is your problem! :) Sometimes signatures don't match if you don't get your consts just right? What about the Q_INVOKABLE?

    https://stackoverflow.com/questions/33781346/how-to-write-setdata-in-qsqlquerymodelqabstractitemmodel is an example of what claims to work? (Oohh, it uses Q_DECL_OVERRIDE for your override?)



  • @JonB said in QSqlRelationalTableModel with multiple Joins?:

    @devhobby
    Oohh, it uses Q_DECL_OVERRIDE for your override?

    Yes, it is ALSO that.

    Something strange is going on here...

    I'll try tagging someone @SGaist @VRonin

    --- HEADER FILE ---

    #ifndef MYMODEL_H
    #define MYMODEL_H
    
    #include <QObject>
    #include <QSqlQueryModel>
    
    class MyModel : public QSqlQueryModel
    {
        Q_OBJECT
    
    public:
        MyModel(QObject *parent = 0);
    
        Qt::ItemFlags flags(const QModelIndex &index) const override;
        bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
    };
    
    #endif // MYMODEL_H
    

    --- CPP FILE ---

    #include "mymodel.h"
    
    MyModel::MyModel(QObject *parent) : QSqlQueryModel(parent)
    {
    }
    
    Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
    {
    
    }
    
    bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
    
    }
    

    0_1518119620573_1720abc4-b3c2-4451-9dda-e5b727282cf3-image.png


  • Lifetime Qt Champion

    Your CPP file contains an error, the default value should be in the declaration of the function.

    If you are using Qt >= 5.7, you can simply use the override keyword as C++11 support is mandatory since that version. Q_DECL_OVERRIDE was used to allow compatibly with non-C++11 enabled compilers.



  • @SGaist You're a hero ;-) It takes you like 1 minute to spot it & reply!



  • @SGaist said in QSqlRelationalTableModel with multiple Joins?:

    Your CPP file contains an error, the default value should be in the declaration of the function.

    If you are using Qt >= 5.7, you can simply use the override keyword as C++11 support is mandatory since that version. Q_DECL_OVERRIDE was used to allow compatibly with non-C++11 enabled compilers.

    I hoped it was just that...

    0_1518120023429_f174cbae-ee6a-42fa-b713-287d8d891eff-image.png

    I moved the default value in the header file, same issue

    [ I will update the code posted above so that I don't repost it everytime ]


  • Lifetime Qt Champion

    Did you try re-running qmake before building ?


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.