Search code examples
pythonpostgresqlpyqt5qt5pyside2

QTableView based on QSqlTableModel editing new record in place doesn’t save edits in PyQt


The form where I have a QTableView has a button to add a new record. The record appears but any data I type in is not saved. In fact, once I add a record, no edits to the tableview are saved. I can edit in place as long as I do not add a new record. The underlying database is Postgresql.

The question is, how do I add a new record which can be edited immediately? What am I missing in the code below?

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5.QtWidgets import *
from phones import *

app=QApplication(sys.argv)

QTableView
QSqlTableModel

class Main(QMainWindow):

    def __init__(self, parent=None):

        QWidget.__init__(self, parent)
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.cont_id = '9'
        self.populate_phones()
        self.ui.new_phone.clicked.connect(self.add_phone)

    def populate_phones(self):
        self.phone_model = QSqlTableModel(self)
        self.phone_model.setTable("contact_phones")
        self.phone_model.setFilter("contact_id='{0}'".format(self.cont_id))
        self.phone_model.select()

        self.phone_view = self.ui.phone_view
        self.phone_view.setModel(self.phone_model)  
        self.phone_view.resizeColumnsToContents()
        
    def add_phone(self):
        self.phone_model.setEditStrategy(QSqlTableModel.OnManualSubmit)
        row = self.phone_model.rowCount()
        record = self.phone_model.record()
        record.setGenerated('id', False)            #primary key
        record.setValue('contact_id', self.cont_id) #foreign key
        self.phone_model.insertRecord(row, record)
        submit = self.phone_model.submitAll()
        #self.phone_model.select()
        phone_index_edit = QModelIndex(self.phone_model.index(row, self.phone_model.fieldIndex('phone_number'))) 
        self.ui.phone_view.edit(phone_index_edit)
        
if __name__=="__main__":

    db = QSqlDatabase.addDatabase("QPSQL");
    db.setHostName(server)
    db.setDatabaseName(database)
    db.setUserName(user)
    db.setPassword(pword)
    myapp = Main()
    myapp.show()
    sys.exit(app.exec_())

Solution

  • You are setting the edit strategy to OnManualSubmit:

    All changes will be cached in the model until either submitAll() or revertAll() is called.

    Once you start editing by calling edit() no change will be saved, but only cached. If you close the widget without calling submitAll() before, those changes will be lost; this also happens when you try to add a new field, because insertRecord will submit only the changes for the inserted record while discarding previous cached changes.

    You can just leave the default OnRowChange edit strategy, or use OnFieldChange.


    Further notes about your code:

    • the creation of the QApplication instance should happen within the if __name__ block (and those QTableView and QSqlTableModel lines at the beginning shouldn't be there, as they do nothing);
    • you are calling the __init__ of QWidget, but Main is a subclass of QMainWindow; you should either use QMainWindow.__init__(self) or subclass from QWidget;
    • since you won't be using the OnManualSubmit strategy, there is no need to submit after insertRecord;
    • the creation of a new instance of QModelIndex is unnecessary, since model.index() already returns a QModelIndex;