This is a follow-up to this question. In there, we created an editable subclass of QSqlQueryModel, to use with complex queries. Now I need to add a functionality like QTableModel's setEditStrategy so I can cache all changes and accept or revert them using buttons. PyQt apparently doesn't allow multiple inheritance and I cannot find sufficient documentation to re-implement this method in my custom model, therefor here's the question:
How can I re-implement QSqlTableModel.setEditStragety (or something like it) including RevertAll() and SubmitAll() in an editable QSqlQueryModel?
Here's a CVME: (I have out-commented the parts of of my Example class I would like to get working)
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel, QSqlTableModel
from PyQt5.QtWidgets import QApplication, QTableView, QWidget, QGridLayout
from PyQt5.Qt import QPushButton
db_file = "test.db"
def create_connection(file_path):
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(file_path)
if not db.open():
print("Cannot establish a database connection to {}!".format(file_path))
return False
return True
def fill_tables():
q = QSqlQuery()
q.exec_("DROP TABLE IF EXISTS Manufacturers;")
q.exec_("CREATE TABLE Manufacturers (Company TEXT, Country TEXT);")
q.exec_("INSERT INTO Manufacturers VALUES ('VW', 'Germany');")
q.exec_("INSERT INTO Manufacturers VALUES ('Honda' , 'Japan');")
q.exec_("DROP TABLE IF EXISTS Cars;")
q.exec_("CREATE TABLE Cars (Company TEXT, Model TEXT, Year INT);")
q.exec_("INSERT INTO Cars VALUES ('Honda', 'Civic', 2009);")
q.exec_("INSERT INTO Cars VALUES ('VW', 'Golf', 2013);")
q.exec_("INSERT INTO Cars VALUES ('VW', 'Polo', 1999);")
class SqlQueryModel_editable(QSqlQueryModel):
"""a subclass of QSqlQueryModel where individual columns can be defined as editable
"""
def __init__(self, editables):
"""editables should be a dict of format:
{INT editable_column_nr : (STR update query to be performed when changes are made on this column
INT model's column number for the filter-column (used in the where-clause),
)}
"""
super().__init__()
self.editables = editables
def flags(self, index):
fl = QSqlQueryModel.flags(self, index)
if index.column() in self.editables:
fl |= Qt.ItemIsEditable
return fl
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.EditRole:
mycolumn = index.column()
if mycolumn in self.editables:
(query, filter_col) = self.editables[mycolumn]
filter_value = self.index(index.row(), filter_col).data()
q = QSqlQuery(query.format(value, filter_value))
result = q.exec_()
if result:
self.query().exec_()
else:
print(self.query().lastError().text())
return result
return QSqlQueryModel.setData(self, index, value, role)
def setFilter(self, myfilter):
text = (self.query().lastQuery() + " WHERE " + myfilter)
self.setQuery(text)
class Example(QWidget):
def __init__(self):
super().__init__()
self.resize(400, 150)
self.createModel()
self.initUI()
def createModel(self):
editables = {1 : ("UPDATE Manufacturers SET Country = '{}' WHERE Company = '{}'", 2)}
self.model = SqlQueryModel_editable(editables)
query = '''
SELECT (comp.company || " " || cars.model) as Car,
comp.Country,
cars.company,
(CASE WHEN cars.Year > 2000 THEN 'yes' ELSE 'no' END) as this_century
from manufacturers comp left join cars
on comp.company = cars.company
'''
q = QSqlQuery(query)
self.model.setQuery(q)
self.model.setFilter("cars.Company = 'VW'")
# self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
def initUI(self):
self.layout = QGridLayout()
self.setLayout(self.layout)
self.view = QTableView()
self.view.setModel(self.model)
self.view.hideColumn(2)
self.layout.addWidget(self.view,0,0,1,2)
self.accept_btn = QPushButton("Accept Changes")
# self.accept_btn.clicked.connect(self.model.submitAll)
self.layout.addWidget(self.accept_btn, 1,0)
self.reject_btn = QPushButton("Reject Changes")
# self.reject_btn.clicked.connect(self.model.revertAll)
self.layout.addWidget(self.reject_btn, 1,1)
if __name__ == '__main__':
app = QApplication(sys.argv)
if not create_connection(db_file):
sys.exit(-1)
fill_tables()
ex = Example()
ex.show()
sys.exit(app.exec_())
Edit to clarify:
I need an editable QSqlQueryModel, on which I can use submitAll()
and revertAll()
, so that changes to the model's data are only accepted after an Accept-button is clicked, or can be reverted using a "Reject" button.