代码之家  ›  专栏  ›  技术社区  ›  Lorenzo Castellino

QTableView QSqlTableModel中的行创建/删除问题

  •  0
  • Lorenzo Castellino  · 技术社区  · 2 年前

    我将PySide6与Python3.10结合使用,创建一个使用SQLite DB记录一些数据点的应用程序。

    我在使用QtSql模块时遇到了困难,尤其是在QTableView QSqlTableModel交互以及创建/编辑/删除数据库中存在的记录的能力方面。

    这是我试图实现的一个最低限度的重复性示例:

    import logging
    import sys
    
    from PySide6.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel
    from PySide6.QtWidgets import (
        QApplication,
        QHBoxLayout,
        QMainWindow,
        QPushButton,
        QTableView,
        QVBoxLayout,
        QWidget,
    )
    
    
    class CustomSqlModel(QSqlTableModel):
        def __init__(self, parent=None):
            QSqlTableModel.__init__(self, parent=parent)
            self.setTable("records")
            self.setEditStrategy(QSqlTableModel.OnFieldChange)
            self.select()
    
    
    # Subclass QMainWindow to customize your application's main window
    class MainWindow(QMainWindow):
        def __init__(self):
            super().__init__()
    
            main_layout = QVBoxLayout()
            buttons_layout = QHBoxLayout()
    
            self.view = QTableView()
            main_layout.addWidget(self.view)
            main_layout.addLayout(buttons_layout)
    
            self.add_button = QPushButton("+")
            self.add_button.pressed.connect(self.add_action)
    
            self.remove_button = QPushButton("-")
            self.remove_button.pressed.connect(self.remove_action)
    
            buttons_layout.addWidget(self.add_button)
            buttons_layout.addWidget(self.remove_button)
    
            widget = QWidget()
            widget.setLayout(main_layout)
            self.setCentralWidget(widget)
    
            self.connect_to_db()
    
        def add_action(self):
            new_record = self.model.record()
            new_record.setValue("point", 0)
            self.model.insertRecord(self.model.rowCount(), new_record)
            self.model.submitAll()
            self.model.select()
    
        def remove_action(self):
            selected = self.view.currentIndex()
            self.model.removeRow(selected.row())
            self.model.submitAll()
            self.model.select()
    
    
        def connect_to_db(self):
            self.database = QSqlDatabase.database()
            if not self.database.isValid():
                self.database = QSqlDatabase.addDatabase("QSQLITE")
            self.database.setDatabaseName(f"test.db")
            if not self.database.open():
                logging.error("Failed to open db")
                self.database = None
                return
    
            self.database.open()
    
            if "records" not in self.database.tables():
                query = QSqlQuery()
                if not query.exec(
                    """
                    CREATE TABLE IF NOT EXISTS records(
                    point REAL
                    )
                    """
                ):
                    logging.error("Failed to query db")
    
                if not query.exec(
                    """
                    INSERT INTO records VALUES (0);
                    """
                ):
                    logging.error("Failed to create initial data in db")
    
            self.model = CustomSqlModel()
            self.view.setModel(self.model)
    
    
    app = QApplication(sys.argv)
    
    window = MainWindow()
    window.show()
    
    app.exec()
    

    当应用程序启动时,我会查找现有的SQLite DB并加载它。如果它不存在,我创建它并添加一个表 records 添加到它(一个单独的列,其中包含一个floting point值)。

    界面中间的QTabLeVIEW控件显示加载的DB,并且可以直接从那里编辑记录。

    小部件底部有两个按钮: add_button remove_button ,用于将一行添加到 记录 表,分别删除当前选定的表。

    我的问题源于QTableView在使用我的UI创建/删除行时的行为:添加行一开始似乎工作正常,但删除一个新创建的行会删除所有行,而不仅仅是选定的行。此外,编辑其中一行,然后删除其中任何一行都不会导致任何删除,所有行都会获得与编辑行相同的值。

    这个问题似乎与我的实现有关,因为如果我使用SQL编辑器(如DBeaver或DB Browser for Sqlite)编辑表,创建/编辑的行将正确显示在thr应用程序中。

    我做错了什么?

    0 回复  |  直到 2 年前
        1
  •  2
  •   ekhumoro    2 年前

    这里的问题是数据库的结构。虽然创建没有主键的数据库是有效的,但这通常会导致问题,因为在通过视图编辑SQL模型时,它可能会迫使Qt尝试使用所有行的值的组合来标识行。在每一行没有唯一的值组合的情况下,这几乎肯定会产生不可预测的行为。

    在您的示例中,每一行都用值初始化 0 。如果保持不变,删除任何具有该值的行都将删除所有行,因为(就Qt而言)它们实际上都具有相同的键。

    为了修复您的示例,您只需添加一个主键,如下所示:

    class MainWindow(QMainWindow):
        ...
        def connect_to_db(self):
            ...
            if "records" not in self.database.tables():
                query = QSqlQuery()
                if not query.exec(
                    """
                    CREATE TABLE IF NOT EXISTS records(
                        ID INTEGER PRIMARY KEY,
                        point REAL
                    )
                    """
                ):
                    logging.error("Failed to query db")
    
                if not query.exec(
                    """
                    INSERT INTO records VALUES (NULL,0);
                    """
                ):
                    logging.error("Failed to create initial data in db")