-1

Edit: Repost with new code and clear question: in the code below you can add and save properly and the question is: I need to do the same but without submitting data to database on row change, I need to be able to add new contact record (master) and insert phones to table (details) without submitting with each record I insert and submitting only when click save

-------------old question -------------------

I want to create a form to show master / details data using QtSql and PyQt so below code is a sample of what I want to do, I need to be able to insert new phones under the contact and save it

I read the book of " Rapid GUI Programming with Python and Qt " but didn't find how to do so, so I posted this question with full working example which I need to be modified to work as desired

Edit: Actually I don't know why I got down vote?, is the question isn't clear?

import sys
from PySide.QtGui import *
from PySide.QtSql import *


class MainWidget(QWidget):

    def __init__(self):
        QWidget.__init__(self)
        self.setWindowTitle("Address Book Example")
        self.initUI()
        db = QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName("addressbook.db")
        ok = db.open()

        self.contacts_model = QSqlTableModel()
        self.contacts_model.setTable('contacts')
        self.contacts_model.setEditStrategy(QSqlTableModel.OnFieldChange)
        self.contacts_model.select()

        self.phones_model = QSqlRelationalTableModel()
        self.phones_model.setTable('phones')
        self.phones_model.setEditStrategy(QSqlTableModel.OnRowChange)
        self.phones_model.setRelation(1, QSqlRelation('contacts', 'id', 'full_name'))
        self.phones_model.select()

        self.conatacts_mapper = QDataWidgetMapper()
        self.conatacts_mapper.setModel(self.contacts_model)
        self.conatacts_mapper.addMapping(self.txtFullName, 1)
        self.conatacts_mapper.addMapping(self.txtAddress, 2)
        self.conatacts_mapper.toFirst()
        self.record_changed()

        self.tblPhones.setModel(self.phones_model)
        # self.tblPhones.setColumnHidden(0, True)
        # self.tblPhones.setColumnHidden(1, True)

        self.btnNext.clicked.connect(self.conatacts_mapper.toNext)
        self.btnPrev.clicked.connect(self.conatacts_mapper.toPrevious)
        self.btnAdd.clicked.connect(self.add_record)
        self.btSave.clicked.connect(self.save)
        self.btNew.clicked.connect(self.new_contact)
        self.conatacts_mapper.currentIndexChanged.connect(self.record_changed)

    def add_record(self):
        if self.phones_model.rowCount() == 0:
            index = self.conatacts_mapper.currentIndex()
            self.conatacts_mapper.submit()
            self.conatacts_mapper.setCurrentIndex(index)
            self.prepare_sub()
        row = self.phones_model.rowCount()
        self.phones_model.insertRow(row)
        index = self.phones_model.index(row, 1)
        self.tblPhones.setCurrentIndex(index)
        self.tblPhones.edit(index)
        self.phones_model.setData(self.phones_model.index(self.phones_model.rowCount() - 1, 1),
                                  self.contacts_model.record(self.conatacts_mapper.currentIndex()).value('id'))

    def record_changed(self):
        self.phones_model.setFilter("contact_id = {}".format(self.conatacts_mapper.currentIndex()+1))

    def save(self):
        self.contacts_model.submitAll()
        self.phones_model.submitAll()
        self.conatacts_mapper.submit()

    def new_contact(self):
        self.contacts_model.insertRow(self.contacts_model.rowCount())
        self.conatacts_mapper.setCurrentIndex(self.contacts_model.rowCount() - 1)

    def prepare_sub(self):
        self.phones_model.setTable('phones')
        self.phones_model.setRelation(1, QSqlRelation('contacts', 'id', 'full_name'))
        self.phones_model.select()
        self.phones_model.setFilter('contact_id=%s' % self.contacts_model.record(self.conatacts_mapper.currentIndex()).value('id'))

    def initUI(self):
        main_layout = QHBoxLayout()
        right_layout = QVBoxLayout()
        left_layout = QVBoxLayout()

        main_layout.addLayout(right_layout)
        main_layout.addLayout(left_layout)

        name_label = QLabel("Full Name")
        self.txtFullName = QLineEdit()
        address_label = QLabel("Address")
        self.txtAddress = QLineEdit()
        phones_label = QLabel("Phone Numbers")
        self.tblPhones = QTableView()

        right_layout.addWidget(name_label)
        right_layout.addWidget(self.txtFullName)
        right_layout.addWidget(address_label)
        right_layout.addWidget(self.txtAddress)

        right_layout.addWidget(phones_label)
        right_layout.addWidget(self.tblPhones)

        self.btnPrev = QPushButton("Previous")
        self.btnNext = QPushButton("Next")
        self.btnAdd = QPushButton("Add")
        self.btSave = QPushButton('Save')
        self.btNew = QPushButton('New')

        left_layout.addWidget(self.btNew)
        left_layout.addWidget(self.btnPrev)
        left_layout.addWidget(self.btnNext)
        left_layout.addStretch(1)
        left_layout.addWidget(self.btnAdd)
        left_layout.addWidget(self.btSave)

        self.setLayout(main_layout)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWidget()
    w.show()
    sys.exit(app.exec_())
Mohamed Yousof
  • 725
  • 1
  • 9
  • 38
  • If you float your mouse pointer over the downvote icon, you will see a tooltip: *This question does not show any research effort; it is unclear or not useful*. That's probably why someone downvoted you. – Tom Zych Dec 13 '15 at 23:36
  • Thanks tom, I will refine it – Mohamed Yousof Dec 14 '15 at 11:02

1 Answers1

0

you need to set the value for the foreign key column "contact_id" in the new row.

I have update your code as following:

1- Add new field to hold contact id and added to layout

    id_label = QLabel("Id")
    self.txtId = QLineEdit()
    self.txtId.setReadOnly(True)
    #....

    right_layout.addWidget(id_label)
    right_layout.addWidget(self.txtId)

2- Update the mapping to fill txtId

    self.conatacts_mapper.addMapping(self.txtId, 0)

3- Update add_record method to fill contact_id in phone model

 def add_record(self):
    indexModel = self.phones_model.rowCount()
    self.phones_model.insertRow(indexModel)
    self.phones_model.setData(self.phones_model.index(indexModel,1),self.txtId.text())
  • This was flagged VLQ, but I'm not sure... Neither the question nor this makes any sense to me, nor I see any connection between them. At second glance, since there is no problem statement, this looks like a random guess. – ivan_pozdeev Dec 13 '15 at 18:58
  • May my question is not clear, I already done like you say and added code to view phones when clicking on some contact and also save new phones and everything works fine except when I add new contact I can't then add new phones until I close the form and reopen it, otherwise I have to relink phones model to contacts model and populate it with data and submitting data OnRowChange – Mohamed Yousof Dec 13 '15 at 20:31
  • this is exactly what I do, and it works as long as the current contact record is already saved but if I just inserted a new contact, the phones model setData is not working until I repopulate it with data and set relation to contacts model again, which means I have to save each phones record and can't save on demand, if you didn't my point I can submit some full code to show you – Mohamed Yousof Dec 14 '15 at 10:49
  • I think what you want is manual submit. Than change **EditStrategy** to **QSqlTableModel::OnManualSubmit** for both model `self.contacts_model.setEditStrategy(QSqlTableModel.OnManualSubmit)`. for more information refer to [documentation](http://doc.qt.io/qt-4.8/qsqltablemodel.html#EditStrategy-enum) – Mohammed AL-Ramadhan Dec 15 '15 at 06:01