QTableView editável de consulta SQL complexa

Como posso criar um QTableView editável que exibe dados de uma consulta SQLite complexa?

Eu preciso preencher um QTableView com dados de várias tabelas SQLite. Isso precisa ser editável pelo usuário.

Como as consultas são um pouco complexas (incluindo JOINs e CASE WHEN etc.), estou fazendo isso por meio de um QSqlTableModel e um QSqlQuery. Disseram-me, no entanto, que não é assim que QSqlTableModels deve ser usado. Então, alguém pode me mostrar como obter um resultado como o mostrado aqui pela maneira correta?

Além disso, enquanto meus QTableViews são editáveis, os resultados não parecem ser armazenados no banco de dados SQLite. (Quando eu comento fill_tables, ainda recebo os resultados originais após reiniciar a GUI. Alterar a EditStrategy para OnFieldChange não ajuda.) Isso é porque estou lidando com QSqlTableModel errado?

#!/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()

Tentei usar um QSqlRelationalTableModel, mas não consigo obter a mesma complexidade de consultas e também não salva as alterações, como no código acima. Isto é o máximo que consegui nessa tentativa:

     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'")

Para responder às perguntas recebidas:

Editabilidade:

Neste exemplo, a única coluna que é definitivamenterequeridos para ser editável (de modo que as alterações cheguem ao banco de dados) é a coluna País (e as alterações devem afetar todas as outras linhas que compartilham o mesmo conteúdo; por exemplo, se você alterar 'Alemanha' para 'França' para VW- carro, ambos devem listar 'França' como país).

Se você souber uma maneira de editar o primeiro também, para que as colunas respectivas no banco de dados sejam atualizadas, seria realmente bacana de ver, mas não é um requisito. (Nas minhas tabelas reais, eu uso essas 'junções de coluna' para campos não editáveis). Nesse caso específico, eu esperaria uma alteração de 'VW Polo' para 'Marco Polo' para atualizar também 'VW Golf' para 'Marco Golf ', pois a coluna usada na junção da coluna é a manufacturer.company e não cars.company. (Na realidade, provavelmente, você usaria cars.company para a união, nesse caso, 'VW Golf' permaneceria inalterado. Mas vamos assumir a consulta como indicado acima.)

A terceira coluna pretende ser um exemplo de um resultado estatístico calculado, e estes geralmente são apenas para leitura (editá-los não faria sentido).

Ordem da coluna:

Eu gostariaaltamente aprecio poder escolher a ordem em que as colunas são exibidas, mesmo em tabelas unidas (como eu poderia fazer com a consulta).

questionAnswers(2)

yourAnswerToTheQuestion