如何使用 PySide2 将具有 NULL 值的记录添加到我的 SQLite 数据库中?
How can I add records to my SQLite database, using PySide2, that have NULL values?
我有一个 PySide2 GUI,可以在我的 SQLite3 数据库中添加、更新和删除记录。我用QTableView查看数据,用QDataWidgetMapper编辑数据。
我的问题是,当我向数据库中添加一条在任何字段中都有空白值的记录时,它不会在数据库中注册为 NULL 值,只有一个空白字符串 ("")。我认为我没有以正确的方式实施委托。我应该在列上还是在 QDataWidgetMapper 上设置委托?
import sys
import os
from PySide2 import QtCore, QtGui, QtWidgets, QtSql
import PySide2.QtUiTools as QtUiTools
import sqlite3
class NullDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent=None):
super(NullDelegate, self).__init__(parent)
def createEditor(self, parent, options, index):
editor = QtWidgets.QLineEdit(parent)
return editor
def setEditorData(self, editor, index):
if index.siblingAtColumn(1).data() == "": # This is for testing purposes
print(index.siblingAtColumn(2).data())
if index.data():
editor.setText(str(index.data()))
editor.selectAll() # This is for testing purposes
def setModelData(self, editor, model, index):
value = editor.text()
if value == "":
print("null value")
model.setData(index, None, QtCore.Qt.EditRole)
else:
model.setData(index, value, QtCore.Qt.EditRole)
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
# load ui file
loader = QtUiTools.QUiLoader()
# The UI file must be a widget, NOT MainWindow, to load into a QMainWindow object
# otherwise you will have to use .show() to open in separate window
path = os.path.join(os.path.dirname(__file__), "main.ui")
self.main = loader.load(path, self)
# create SQLite Database and Table
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("customers_demo.db")
if db.open():
print('connection open')
query = QtSql.QSqlQuery(db=db)
query.prepare("CREATE TABLE IF NOT EXISTS Customers_demo (ID INT PRIMARY KEY, FEIN varchar(255), CustomerName varchar(255), Address varchar(255), City varchar(255), State varchar(255), Zip INT) ")
query.exec_()
# Set up QSqlTableModel
self.model = QtSql.QSqlTableModel(self)
self.model.setTable("customers_demo")
self.model.select()
# Set up QDataWidgetMapper
self.mapper = QtWidgets.QDataWidgetMapper()
self.mapper.setModel(self.model)
# Set up the delegate for null values
null_delegate = NullDelegate(self.main.tableView)
self.mapper.setItemDelegate(null_delegate)
# Configure QDataWidgetMapper
self.mapper.addMapping(self.main.id_lineEdit, 0)
self.mapper.addMapping(self.main.fein_lineEdit, 1)
self.mapper.addMapping(self.main.customer_lineEdit, 2)
self.mapper.addMapping(self.main.address_lineEdit, 3)
self.mapper.addMapping(self.main.city_lineEdit, 4)
self.mapper.addMapping(self.main.state_lineEdit, 5)
self.mapper.addMapping(self.main.zip_lineEdit, 6)
self.mapper.setSubmitPolicy(self.mapper.ManualSubmit)
# Set up QTableView
self.main.tableView.setModel(self.model)
# Set up the delegate for null values
# null_delegate = NullDelegate(self.main.tableView)
# self.main.tableView.setItemDelegateForColumn(1, null_delegate)
self.main.tableView.resizeColumnsToContents()
# QTableView properties
self.main.tableView.setSortingEnabled(True) # sort by clicking title
self.main.tableView.resizeColumnToContents(2) # "Customer Name" column
self.main.tableView.setSelectionBehavior(
QtWidgets.QAbstractItemView.SelectRows)
# add background color to every other row
self.main.tableView.setAlternatingRowColors(True)
# make table uneditable
self.main.tableView.setEditTriggers(
QtWidgets.QTableView.NoEditTriggers)
self.mapper.setCurrentIndex(0)
print(self.mapper.currentIndex())
self.RECORD_NUMBER()
self.handle_buttons()
def handle_buttons(self):
self.main.update_btn.clicked.connect(self.UPDATE)
self.main.delete_btn.clicked.connect(self.DELETE)
self.main.new_btn.clicked.connect(self.NEW)
self.main.first_btn.clicked.connect(self.mapper.toFirst)
self.main.first_btn.clicked.connect(self.RECORD_NUMBER)
self.main.previous_btn.clicked.connect(self.mapper.toPrevious)
self.main.previous_btn.clicked.connect(self.RECORD_NUMBER)
self.main.next_btn.clicked.connect(self.mapper.toNext)
self.main.next_btn.clicked.connect(self.RECORD_NUMBER)
self.main.last_btn.clicked.connect(self.mapper.toLast)
self.main.last_btn.clicked.connect(self.RECORD_NUMBER)
self.main.tableView.clicked.connect(self.SELECT_ROW)
def SELECT_ROW(self, item):
# Set text to the selected customer info
self.main.id_lineEdit.setText(
str(item.siblingAtColumn(0).data())) # convert int to str
self.main.customer_lineEdit.setText(item.siblingAtColumn(2).data())
self.main.fein_lineEdit.setText(item.siblingAtColumn(1).data())
self.main.address_lineEdit.setText(item.siblingAtColumn(3).data())
self.main.city_lineEdit.setText(item.siblingAtColumn(4).data())
self.main.state_lineEdit.setText(item.siblingAtColumn(5).data())
self.main.zip_lineEdit.setText(
str(item.siblingAtColumn(6).data())) # convert int to str
self.mapper.setCurrentIndex(item.row())
self.RECORD_NUMBER()
def UPDATE(self):
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
dialog = loader.load(path)
currentIndex = self.mapper.currentIndex()
print("Mapper", currentIndex)
self.main.tableView.selectRow(currentIndex)
customer = self.main.tableView.currentIndex().siblingAtColumn(2).data()
print(customer)
text = f"Are you sure you want to UPDATE {customer} info?"
dialog.label.setText(text)
dialog.buttonBox.accepted.connect(dialog.accept)
dialog.buttonBox.rejected.connect(dialog.reject)
dialog.show()
if dialog.exec_():
print("update")
te = self.mapper.submit()
print(te)
print(self.mapper.model())
self.model.select()
else:
pass
def DELETE(self):
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
dialog = loader.load(path)
currentIndex = self.mapper.currentIndex()
print("Mapper", currentIndex)
self.main.tableView.selectRow(currentIndex)
customer = self.main.tableView.currentIndex().siblingAtColumn(2).data()
print(customer)
text = f"Are you sure you want to DELETE {customer} info?"
dialog.label.setText(text)
dialog.buttonBox.accepted.connect(dialog.accept)
dialog.buttonBox.rejected.connect(dialog.reject)
dialog.show()
if dialog.exec_():
print("yes")
self.model.removeRow(currentIndex)
self.model.select()
else:
pass
self.mapper.setCurrentIndex(0)
print(self.mapper.currentIndex())
self.RECORD_NUMBER()
def NEW(self):
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
dialog = loader.load(path)
customer = self.main.customer_lineEdit.text()
text = f"Are you sure you want to ADD {customer} info?"
dialog.label.setText(text)
dialog.buttonBox.accepted.connect(dialog.accept)
dialog.buttonBox.rejected.connect(dialog.reject)
dialog.show()
if dialog.exec_():
row = self.model.rowCount()
record = self.model.record()
# record.setGenerated('id', False)
record.setValue('ID', self.main.id_lineEdit.text())
record.setValue('FEIN', str(self.main.fein_lineEdit.text()))
record.setValue("CustomerName",
self.main.customer_lineEdit.text())
record.setValue('Address', self.main.address_lineEdit.text())
record.setValue('City', self.main.city_lineEdit.text())
record.setValue('State', self.main.state_lineEdit.text())
record.setValue('Zip', self.main.zip_lineEdit.text())
self.model.insertRecord(row, record)
self.model.select()
else:
pass
self.mapper.setCurrentIndex(0)
print(self.mapper.currentIndex())
self.RECORD_NUMBER()
def RECORD_NUMBER(self):
currentIndex = self.mapper.currentIndex()
print(currentIndex)
rows = self.model.rowCount()
self.main.record_label.setText(
f"Customer {currentIndex+1} of {rows} customers")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
widget = Main()
widget.show()
app.exec_()
您可以下载 ui 文件和示例数据库 here.
我也在使用 Python 3.7.4
问题不在于委托,而在于添加行的代码,因为您直接传递 QLineEdits 的文本而没有实现空值逻辑。
def NEW(self):
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
dialog = loader.load(path)
customer = self.main.customer_lineEdit.text()
text = f"Are you sure you want to ADD {customer} info?"
dialog.label.setText(text)
dialog.buttonBox.accepted.connect(dialog.accept)
dialog.buttonBox.rejected.connect(dialog.reject)
if dialog.exec_():
mapping = {
"ID": self.main.id_lineEdit,
"FEIN": self.main.fein_lineEdit,
"CustomerName": self.main.customer_lineEdit,
"Address": self.main.address_lineEdit,
"City": self.main.city_lineEdit,
"State": self.main.state_lineEdit,
"Zip": self.main.zip_lineEdit,
}
record = self.model.record()
for fieldname, lineedit in mapping.items():
text = lineedit.text()
record.setValue(fieldname, text if text else None)
row = self.model.rowCount()
self.model.insertRecord(row, record)
self.model.select()
self.mapper.setCurrentIndex(0)
print(self.mapper.currentIndex())
self.RECORD_NUMBER()
我有一个 PySide2 GUI,可以在我的 SQLite3 数据库中添加、更新和删除记录。我用QTableView查看数据,用QDataWidgetMapper编辑数据。
我的问题是,当我向数据库中添加一条在任何字段中都有空白值的记录时,它不会在数据库中注册为 NULL 值,只有一个空白字符串 ("")。我认为我没有以正确的方式实施委托。我应该在列上还是在 QDataWidgetMapper 上设置委托?
import sys
import os
from PySide2 import QtCore, QtGui, QtWidgets, QtSql
import PySide2.QtUiTools as QtUiTools
import sqlite3
class NullDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent=None):
super(NullDelegate, self).__init__(parent)
def createEditor(self, parent, options, index):
editor = QtWidgets.QLineEdit(parent)
return editor
def setEditorData(self, editor, index):
if index.siblingAtColumn(1).data() == "": # This is for testing purposes
print(index.siblingAtColumn(2).data())
if index.data():
editor.setText(str(index.data()))
editor.selectAll() # This is for testing purposes
def setModelData(self, editor, model, index):
value = editor.text()
if value == "":
print("null value")
model.setData(index, None, QtCore.Qt.EditRole)
else:
model.setData(index, value, QtCore.Qt.EditRole)
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
# load ui file
loader = QtUiTools.QUiLoader()
# The UI file must be a widget, NOT MainWindow, to load into a QMainWindow object
# otherwise you will have to use .show() to open in separate window
path = os.path.join(os.path.dirname(__file__), "main.ui")
self.main = loader.load(path, self)
# create SQLite Database and Table
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("customers_demo.db")
if db.open():
print('connection open')
query = QtSql.QSqlQuery(db=db)
query.prepare("CREATE TABLE IF NOT EXISTS Customers_demo (ID INT PRIMARY KEY, FEIN varchar(255), CustomerName varchar(255), Address varchar(255), City varchar(255), State varchar(255), Zip INT) ")
query.exec_()
# Set up QSqlTableModel
self.model = QtSql.QSqlTableModel(self)
self.model.setTable("customers_demo")
self.model.select()
# Set up QDataWidgetMapper
self.mapper = QtWidgets.QDataWidgetMapper()
self.mapper.setModel(self.model)
# Set up the delegate for null values
null_delegate = NullDelegate(self.main.tableView)
self.mapper.setItemDelegate(null_delegate)
# Configure QDataWidgetMapper
self.mapper.addMapping(self.main.id_lineEdit, 0)
self.mapper.addMapping(self.main.fein_lineEdit, 1)
self.mapper.addMapping(self.main.customer_lineEdit, 2)
self.mapper.addMapping(self.main.address_lineEdit, 3)
self.mapper.addMapping(self.main.city_lineEdit, 4)
self.mapper.addMapping(self.main.state_lineEdit, 5)
self.mapper.addMapping(self.main.zip_lineEdit, 6)
self.mapper.setSubmitPolicy(self.mapper.ManualSubmit)
# Set up QTableView
self.main.tableView.setModel(self.model)
# Set up the delegate for null values
# null_delegate = NullDelegate(self.main.tableView)
# self.main.tableView.setItemDelegateForColumn(1, null_delegate)
self.main.tableView.resizeColumnsToContents()
# QTableView properties
self.main.tableView.setSortingEnabled(True) # sort by clicking title
self.main.tableView.resizeColumnToContents(2) # "Customer Name" column
self.main.tableView.setSelectionBehavior(
QtWidgets.QAbstractItemView.SelectRows)
# add background color to every other row
self.main.tableView.setAlternatingRowColors(True)
# make table uneditable
self.main.tableView.setEditTriggers(
QtWidgets.QTableView.NoEditTriggers)
self.mapper.setCurrentIndex(0)
print(self.mapper.currentIndex())
self.RECORD_NUMBER()
self.handle_buttons()
def handle_buttons(self):
self.main.update_btn.clicked.connect(self.UPDATE)
self.main.delete_btn.clicked.connect(self.DELETE)
self.main.new_btn.clicked.connect(self.NEW)
self.main.first_btn.clicked.connect(self.mapper.toFirst)
self.main.first_btn.clicked.connect(self.RECORD_NUMBER)
self.main.previous_btn.clicked.connect(self.mapper.toPrevious)
self.main.previous_btn.clicked.connect(self.RECORD_NUMBER)
self.main.next_btn.clicked.connect(self.mapper.toNext)
self.main.next_btn.clicked.connect(self.RECORD_NUMBER)
self.main.last_btn.clicked.connect(self.mapper.toLast)
self.main.last_btn.clicked.connect(self.RECORD_NUMBER)
self.main.tableView.clicked.connect(self.SELECT_ROW)
def SELECT_ROW(self, item):
# Set text to the selected customer info
self.main.id_lineEdit.setText(
str(item.siblingAtColumn(0).data())) # convert int to str
self.main.customer_lineEdit.setText(item.siblingAtColumn(2).data())
self.main.fein_lineEdit.setText(item.siblingAtColumn(1).data())
self.main.address_lineEdit.setText(item.siblingAtColumn(3).data())
self.main.city_lineEdit.setText(item.siblingAtColumn(4).data())
self.main.state_lineEdit.setText(item.siblingAtColumn(5).data())
self.main.zip_lineEdit.setText(
str(item.siblingAtColumn(6).data())) # convert int to str
self.mapper.setCurrentIndex(item.row())
self.RECORD_NUMBER()
def UPDATE(self):
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
dialog = loader.load(path)
currentIndex = self.mapper.currentIndex()
print("Mapper", currentIndex)
self.main.tableView.selectRow(currentIndex)
customer = self.main.tableView.currentIndex().siblingAtColumn(2).data()
print(customer)
text = f"Are you sure you want to UPDATE {customer} info?"
dialog.label.setText(text)
dialog.buttonBox.accepted.connect(dialog.accept)
dialog.buttonBox.rejected.connect(dialog.reject)
dialog.show()
if dialog.exec_():
print("update")
te = self.mapper.submit()
print(te)
print(self.mapper.model())
self.model.select()
else:
pass
def DELETE(self):
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
dialog = loader.load(path)
currentIndex = self.mapper.currentIndex()
print("Mapper", currentIndex)
self.main.tableView.selectRow(currentIndex)
customer = self.main.tableView.currentIndex().siblingAtColumn(2).data()
print(customer)
text = f"Are you sure you want to DELETE {customer} info?"
dialog.label.setText(text)
dialog.buttonBox.accepted.connect(dialog.accept)
dialog.buttonBox.rejected.connect(dialog.reject)
dialog.show()
if dialog.exec_():
print("yes")
self.model.removeRow(currentIndex)
self.model.select()
else:
pass
self.mapper.setCurrentIndex(0)
print(self.mapper.currentIndex())
self.RECORD_NUMBER()
def NEW(self):
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
dialog = loader.load(path)
customer = self.main.customer_lineEdit.text()
text = f"Are you sure you want to ADD {customer} info?"
dialog.label.setText(text)
dialog.buttonBox.accepted.connect(dialog.accept)
dialog.buttonBox.rejected.connect(dialog.reject)
dialog.show()
if dialog.exec_():
row = self.model.rowCount()
record = self.model.record()
# record.setGenerated('id', False)
record.setValue('ID', self.main.id_lineEdit.text())
record.setValue('FEIN', str(self.main.fein_lineEdit.text()))
record.setValue("CustomerName",
self.main.customer_lineEdit.text())
record.setValue('Address', self.main.address_lineEdit.text())
record.setValue('City', self.main.city_lineEdit.text())
record.setValue('State', self.main.state_lineEdit.text())
record.setValue('Zip', self.main.zip_lineEdit.text())
self.model.insertRecord(row, record)
self.model.select()
else:
pass
self.mapper.setCurrentIndex(0)
print(self.mapper.currentIndex())
self.RECORD_NUMBER()
def RECORD_NUMBER(self):
currentIndex = self.mapper.currentIndex()
print(currentIndex)
rows = self.model.rowCount()
self.main.record_label.setText(
f"Customer {currentIndex+1} of {rows} customers")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
widget = Main()
widget.show()
app.exec_()
您可以下载 ui 文件和示例数据库 here.
我也在使用 Python 3.7.4
问题不在于委托,而在于添加行的代码,因为您直接传递 QLineEdits 的文本而没有实现空值逻辑。
def NEW(self):
loader = QtUiTools.QUiLoader()
path = os.path.join(os.path.dirname(__file__), "dialog.ui")
dialog = loader.load(path)
customer = self.main.customer_lineEdit.text()
text = f"Are you sure you want to ADD {customer} info?"
dialog.label.setText(text)
dialog.buttonBox.accepted.connect(dialog.accept)
dialog.buttonBox.rejected.connect(dialog.reject)
if dialog.exec_():
mapping = {
"ID": self.main.id_lineEdit,
"FEIN": self.main.fein_lineEdit,
"CustomerName": self.main.customer_lineEdit,
"Address": self.main.address_lineEdit,
"City": self.main.city_lineEdit,
"State": self.main.state_lineEdit,
"Zip": self.main.zip_lineEdit,
}
record = self.model.record()
for fieldname, lineedit in mapping.items():
text = lineedit.text()
record.setValue(fieldname, text if text else None)
row = self.model.rowCount()
self.model.insertRecord(row, record)
self.model.select()
self.mapper.setCurrentIndex(0)
print(self.mapper.currentIndex())
self.RECORD_NUMBER()