Как создать редактируемый QTableView, отображающий данные из сложного запроса SQLite?
Мне нужно заполнить QTableView данными из нескольких таблиц SQLite. Это должно быть доступно для редактирования пользователем.
Поскольку запросы немного сложны (включая JOIN и CASE WHEN и т. д.), я делаю это с помощью QSqlTableModel и QSqlQuery. Однако мне сказали, что это не то, как следует использовать QSqlTableModels. Итак, может ли кто-нибудь показать мне, как правильно получить результат, подобный показанному здесь?
Кроме того, хотя мои QTableView доступны для редактирования, результаты, похоже, не сохраняются в базе данных SQLite. (Когда я комментирую fill_tables, я все еще получаю исходные результаты после перезапуска графического интерфейса. Изменение EditStrategy на OnFieldChange не помогает.) Это потому, что я неправильно обрабатываю QSqlTableModel?
#!/usr/bin/python3
from PyQt5.QtSql import (QSqlDatabase, QSqlQuery, QSqlTableModel,
QSqlRelationalTableModel, QSqlRelation)
from PyQt5.QtWidgets import QTableView, QApplication
from PyQt5.Qt import QSortFilterProxyModel
import sys
db_file = "test.db"
def create_connection(db_file):
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(db_file)
if not db.open():
print("Cannot establish a database connection to {}!".format(db_file))
return False
return db
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 CarTable(QTableView):
def __init__(self):
super().__init__()
self.init_UI()
self.create_model()
def create_model(self):
query = """
SELECT (comp.company || " " || cars.model) as Car,
comp.Country,
(CASE WHEN cars.Year > 2000 THEN 'yes' ELSE 'no' END) as this_century
from manufacturers comp left join cars
on comp.company = cars.company
"""
raw_model = QSqlTableModel()
q = QSqlQuery()
q.exec_(query)
self.check_error(q)
raw_model.setQuery(q)
self.model = QSortFilterProxyModel()
self.model.setSourceModel(raw_model)
self.setModel(self.model)
# filtering:
self.model.setFilterKeyColumn(0)
self.model.setFilterFixedString('VW')
def init_UI(self):
self.resize(500,300)
def check_error(self, q):
lasterr = q.lastError()
if lasterr.isValid():
print(lasterr.text())
exit(1)
def main():
mydb = create_connection(db_file)
if not mydb:
sys.exit(-1)
fill_tables()
app = QApplication(sys.argv)
ex = CarTable()
ex.show()
result = app.exec_()
if (mydb.open()):
mydb.close()
sys.exit(result)
if __name__ == '__main__':
main()
Вместо этого я попытался использовать QSqlRelationalTableModel, но я не могу выполнить ту же сложность запросов, и он также не сохраняет изменения, как в приведенном выше коде. Это то, что я получил в этой попытке:
def create_model_alternative(self):
self.model = QSqlRelationalTableModel()
self.model.setTable("Cars")
self.model.setRelation(0, QSqlRelation("Manufacturers", "Company",
"Company, Country"))
self.setModel(self.model)
self.model.select()
# filtering:
self.model.setFilter("cars.Company = 'VW'")
Чтобы ответить на входящие вопросы:
Редактируемость:
В этом примере единственным столбцом, который определенно обязательно должен быть редактируемым (таким образом, чтобы изменения поступали в базу данных), является столбец Country (и изменения в нем должны влиять на все другие строки, которые имеют одинаковые значения). содержание; например, если вы замените «Германия» на «Франция» для любого автомобиля VW, оба должны указать «Франция» в качестве страны).
Если вы знаете способ сделать первый из них доступным для редактирования, чтобы соответствующие столбцы в базе данных обновлялись, это было бы неплохо увидеть, но это не обязательное требование. (В моих реальных таблицах я использую такие «объединения столбцов» для нередактируемых полей.) В этом конкретном случае я ожидаю, что изменение «VW Polo» на «Marco Polo» также обновит «VW Golf» на «Marco Golf». ', так как столбец, используемый в объединении столбцов, — это Manufacturers.company, а не cars.company. (В действительности для объединения, вероятно, следует использовать cars.company, и в этом случае «VW Golf» останется без изменений. Но давайте предположим, что запрос приведен выше.)
Третий столбец предназначен для примера вычисляемого результата статистики, и они, как правило, только для чтения (редактировать их не имеет смысла).
Порядок столбцов:
Я был бы очень признателен за возможность выбирать порядок отображения столбцов даже между объединенными таблицами (как я мог бы сделать с запросом).