When using QtableView + QSqlTableModel combination, QtableView fails to properly display the inserted row after insertion of a new one.
The id field is hidden. The EditStrategy is OnFieldChange. There is a button to insert a new empty row at the end of the model. After the user fills in all fields and hits enter or clicks on other rows, the expected behavior is for the row to be submitted to the database and the view keeping the new row. However, the QSqlTableModel succeeds to insert the data into the database table, but the tableview's newly inserted row becomes empty and fields uneditable.
The behavior changes to the desired one if the id field is not hidden and is explicitly provided. Yet if the id is not hidden and not populated manually, the behaviour fails again as mentioned above.
Why this happens?
Here is the minimum reproducible code. You will have to change database configuration fileds. And connect to the table with autoincrement id:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QModelIndex
from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
from PyQt5.QtWidgets import QPushButton
class Ui_main(object):
def setupUi(self, main):
main.setObjectName("main")
main.resize(781, 652)
main.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
self.verticalLayoutWidget = QtWidgets.QWidget(main)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 761, 631))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
# Replace values with your database configurations
# But the table you refer to must have autoincremented primary key
database = QSqlDatabase.addDatabase('QPSQL')
database.setHostName('localhost')
database.setPort(5432)
database.setDatabaseName('some_database')
database.setUserName('some_user')
database.setPassword('some_password')
database.open()
button_add = QPushButton("AddRow")
button_add.clicked.connect(self.addRow)
self.verticalLayout.addWidget(button_add)
self.tableView = QtWidgets.QTableView(self.verticalLayoutWidget)
self.tableView.setObjectName("tableView")
self.tableView.verticalHeader().setVisible(False)
self.verticalLayout.addWidget(self.tableView)
self.table_model = QSqlTableModel(self.tableView, database)
table_name = 'some_table'
self.table_model.setTable(table_name)
self.table_model.setEditStrategy(QSqlTableModel.OnFieldChange)
self.table_model.select()
self.tableView.setModel(self.table_model)
self.tableView.hideColumn(0)
self.retranslateUi(main)
QtCore.QMetaObject.connectSlotsByName(main)
def retranslateUi(self, main):
_translate = QtCore.QCoreApplication.translate
main.setWindowTitle(_translate("main", "main"))
def addRow(self):
count = self.table_model.rowCount(QModelIndex())
self.table_model.insertRow(count)
self.tableView.scrollToBottom()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main_window = QtWidgets.QWidget()
window = Ui_main()
window.setupUi(main_window)
main_window.show()
sys.exit(app.exec_())
Update:
The temporary workaround I am using right now is finding max id from the fetched table and append max_id + 1 to the newly inserted row. Thus, the row has an id on submission to the database. However, this seems to be not the right way as the database handles primary keys itself...
from qtableview fails to display the newly inserted row
No comments:
Post a Comment