如何使用 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()