如何使用 QSqlTableModel 在可编辑的 QTableView 中将值设置为 NULL
Posted
技术标签:
【中文标题】如何使用 QSqlTableModel 在可编辑的 QTableView 中将值设置为 NULL【英文标题】:How to set value to NULL in editable QTableView with QSqlTableModel 【发布时间】:2021-04-19 09:36:11 【问题描述】:我有一个QTableView
,它显示 SQLite 数据库中表的数据子集。该表只能对可为空的数值列进行编辑。我创建了两个代表 - 一个用于只读列:
class ReadOnlyDelegate(QtWidgets.QItemDelegate):
def editorEvent(self, *args, **kwargs):
return False
def createEditor(self, *args, **kwargs):
return None
还有一个用于可编辑列:
class LabelDelegate(QtWidgets.QItemDelegate):
def createEditor(self, parent, options, index):
self.le = QtWidgets.QLineEdit(parent)
return self.le
表格由自定义的QSqlTableModel
提供,我覆盖了 submitAll 方法:
class mysqlTableModel(QtSql.QSqlTableModel):
def submitAll(self):
for row in range(self.rowCount()):
for col in range(self.columnCount()):
if self.isDirty(self.index(row, col)):
val = self.record(row).value(col)
if val == '':
self.record(row).setNull(col)
else:
try:
self.record(row).setValue(col, float(val))
except (TypeError, ValueError):
display_error_msg('Can not convert to float',
f'The value val could not be converted to float')
raise
super().submitAll()
预期的行为是 (1) 在发送到数据库之前将值转换为浮点数,(2) 拒绝无法转换为浮点数的输入,以及 (3) 将空字符串转换为 NULL。 (1)和(2)按预期工作,但最后一位不起作用。在调试方法.submitAll()
时,它不会在self.record(row).setNull(col)
行上引发异常,但它似乎也没有效果。一个空字符串被发送并保存在数据库中。任何想法为什么以及如何解决它?
【问题讨论】:
QSqlTableModel.record 是const
。您需要使用setRecord。
我会试一试,但self.record(row).setValue(col, float(val))
有效。只是self.record(row).setNull(col)
没有将值设置为NULL
,而是一个空字符串。实际上,我错误地指出它似乎没有效果。它也可以,只需将值设置为空字符串。如果我通过删除数字条目来编辑记录,它将保留在数据库中,但作为空字符串而不是NULL
。
这可能是 SQLite 特有的,因为它没有“真正的”数据类型。您可以将任何数据类型插入任何列。我在其他地方没有遇到这个问题,因为我正在使用 sqlalchemy 来解决这个问题。在其他后端,无法将字符串插入数字列。
我从不使用记录来设置值,所以我只是假设setRcord
是必要的,因为其他几个 Qt API 都是这样工作的。你试过setValue(col, None)
吗?
是的,但是结果和setNull
一模一样
【参考方案1】:
我认为不需要重写submitAll()
方法,而是可以在setModelData()
方法中实现逻辑:
import sys
from PySide2 import QtCore, QtWidgets, QtSql
TABLENAME = "t1"
def create_connection():
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("database.db")
if not db.open():
QtWidgets.QMessageBox.critical(
None,
QtWidgets.QApplication.instance().tr("Cannot open database"),
QtWidgets.QApplication.instance().tr(
"Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information "
"how to build it.\n\n"
"Click Cancel to exit."
),
QtWidgets.QMessageBox.Cancel,
)
return False
if TABLENAME in db.tables():
return True
query = QtSql.QSqlQuery()
if not query.exec_(f"CREATE TABLE IF NOT EXISTS TABLENAME(a REAL, b text);"):
print(query.lastError().text())
queries = (
f"INSERT INTO TABLENAME VALUES(1, '1');",
f"INSERT INTO TABLENAME VALUES(NULL, '2');",
f"INSERT INTO TABLENAME VALUES(3, '3');",
f"INSERT INTO TABLENAME VALUES(NULL, '4');",
f"INSERT INTO TABLENAME VALUES(5, '4');",
f"INSERT INTO TABLENAME VALUES(NULL, '5');",
f"INSERT INTO TABLENAME VALUES(NULL, '7');",
)
for query in queries:
q = QtSql.QSqlQuery()
if not q.exec_(query):
print(q.lastError().text(), query)
return False
return True
class ReadOnlyDelegate(QtWidgets.QStyledItemDelegate):
def editorEvent(self, *args, **kwargs):
return False
def createEditor(self, *args, **kwargs):
return None
class LabelDelegate(QtWidgets.QStyledItemDelegate):
def createEditor(self, parent, options, index):
le = QtWidgets.QLineEdit(parent)
return le
def setModelData(self, editor, model, index):
value = editor.text()
if not value:
model.setData(index, None, QtCore.Qt.EditRole)
else:
try:
number = float(value)
except (TypeError, ValueError):
print(
f"Can not convert to float, The value value could not be converted to float'"
)
else:
model.setData(index, number, QtCore.Qt.EditRole)
def main(args):
app = QtWidgets.QApplication(args)
if not create_connection():
sys.exit(-1)
view = QtWidgets.QTableView()
model = QtSql.QSqlTableModel()
model.setTable(TABLENAME)
model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
model.select()
view.setModel(model)
label_delegate = LabelDelegate(view)
view.setItemDelegateForColumn(0, label_delegate)
readonly_delegate = ReadOnlyDelegate(view)
view.setItemDelegateForColumn(1, readonly_delegate)
button = QtWidgets.QPushButton("Submit all")
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
lay.addWidget(button)
lay.addWidget(view)
widget.resize(640, 480)
widget.show()
button.clicked.connect(model.submitAll)
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main(sys.argv)
在 Linux 上测试:
PyQt5 5.15.4 PySide2 5.15.2 Python 3.9.4【讨论】:
以上是关于如何使用 QSqlTableModel 在可编辑的 QTableView 中将值设置为 NULL的主要内容,如果未能解决你的问题,请参考以下文章
在可编辑网格中,如何使 Ext 组合框在选择项目时立即完成编辑模式?