通过 QTableView 编辑 QSqlTableModel 时行 creation/deletion 出现问题
Issues with row creation/deletion when editing QSqlTableModel via QTableView
我将 PySide6 与 Python 3.10 一起使用来创建一个使用 SQLite 数据库记录一些数据点的应用程序。
我在使用 QtSql 模块时遇到困难,特别是关于 QTableView-QSqlTableModel 交互以及 create/edit/delete 数据库中存在的记录的能力。
这是我要实现的最小可重现示例:
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 数据库并加载它。如果它不存在,我创建它并向其添加一个 table records
(一个包含浮点值的列)。
界面中间的 QTableView 小部件显示加载的数据库,可以从那里直接编辑记录。
小部件底部有两个按钮:add_button
和 remove_button
,用于向 records
table 添加一行和删除当前选择的一个。
我的问题来自 QTableView 的行为,当 creating/removing 行与我的 UI 时:添加行一开始似乎工作正常,但删除其中一个新创建的行会删除所有行只是一个选择。此外,编辑其中一行然后删除其中任何一行都不会导致任何删除,并且所有行都将获得与编辑后的行相同的值。
问题似乎与我的实现有关,因为如果我使用 SQL 编辑器(例如 DBeaver 或 DB Browser for Sqlite)编辑 table,则 created/edited 行会正确显示在申请中。
我做错了什么?
此处的问题与您的数据库结构有关。虽然创建没有主键的数据库是有效的,但这通常会导致问题,因为它可能会强制 Qt 在通过视图编辑 SQL 模型时尝试使用所有值的组合来识别行。如果每一行都没有唯一的值组合,这几乎肯定会产生不可预测的行为。
在您的示例中,每一行都使用值 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")
我将 PySide6 与 Python 3.10 一起使用来创建一个使用 SQLite 数据库记录一些数据点的应用程序。
我在使用 QtSql 模块时遇到困难,特别是关于 QTableView-QSqlTableModel 交互以及 create/edit/delete 数据库中存在的记录的能力。
这是我要实现的最小可重现示例:
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 数据库并加载它。如果它不存在,我创建它并向其添加一个 table records
(一个包含浮点值的列)。
界面中间的 QTableView 小部件显示加载的数据库,可以从那里直接编辑记录。
小部件底部有两个按钮:add_button
和 remove_button
,用于向 records
table 添加一行和删除当前选择的一个。
我的问题来自 QTableView 的行为,当 creating/removing 行与我的 UI 时:添加行一开始似乎工作正常,但删除其中一个新创建的行会删除所有行只是一个选择。此外,编辑其中一行然后删除其中任何一行都不会导致任何删除,并且所有行都将获得与编辑后的行相同的值。
问题似乎与我的实现有关,因为如果我使用 SQL 编辑器(例如 DBeaver 或 DB Browser for Sqlite)编辑 table,则 created/edited 行会正确显示在申请中。
我做错了什么?
此处的问题与您的数据库结构有关。虽然创建没有主键的数据库是有效的,但这通常会导致问题,因为它可能会强制 Qt 在通过视图编辑 SQL 模型时尝试使用所有值的组合来识别行。如果每一行都没有唯一的值组合,这几乎肯定会产生不可预测的行为。
在您的示例中,每一行都使用值 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")