关系表格模型QSqlRelationalTableModel
QSqlRelationalTableModel类为单个数据库表提供了一个可编辑的数据模型,并支持外键。
数据库关系表格模型 QSqlRelationalTableModel 继承自QSqlTableModel,除具有QSqlTableModel 的方法外,它还提供了外键功能。
关系表格模型 QSqlRelationalTableModel 实现了SQL的 SELECT 命令中的 INNER JOIN LEFT JOIN 能。SELECT 命令中INNERJOIN和LEFTJOIN的格式如下。
SELECT * FROM table1 INNER JOIN table2 ON tablel.fleld1 = table2.field2
SELECT * FROM table1 LEFT JOIN teble2 0N tablel.field1 = table2.field2
数据库中的数据表格之间往往有一定的联系,例如学生考试成绩数据库中,用数据表格table1 来存储学号,姓名、语文成绩、数学成绩,而物理成绩和化学成绩只是用学号来标记,并没有给出成绩,用数据表格 table2 存储学号姓名、物理成绩和化学成绩如果想通过查询数据表格 tablel 得到语文成绩、数学成绩、物理成绩和化学成绩,就可利用QSqlRelationalTableModel模型的外键功能来实现。
关系表格模型QSglRelationalTableModel官方说明
QSqlRelationalTableModel的作用类似于QSqlTableModel,但允许将列设置为其他数据库表的外键。
左边的屏幕截图显示了一个QTableView中的普通QSqlTableModel。外键(城市和国家)无法解析为人类可读的值。右边的屏幕截图显示了一个QSqlRelationalTableModel,其中外键被解析为人类可读的文本字符串。
以下代码片段显示了如何设置QSqlRelationalTableModel:
model.setTable("employee")
model.setRelation(2, QSqlRelation("city", "id", "name"))
函数调用setRelation()在两个表之间建立关系。第一个调用指定employee表中的列2是一个外键,它与表city的字段id映射,并且视图应该向用户显示城市的名称字段。第二个调用对第3列执行类似的操作。
如果使用读写QSqlRelationalTableModel,则可能需要在视图上使用QSqlRelationsDelegate。与默认委托不同,QSqlRelationalDelegate为作为其他表外键的字段提供了一个组合框。要使用该类,只需在具有QSqlRelationalDelegate实例的视图上调用setItemDelegate():
std.unique_ptr<QTableView> view{QTableView()}
view.setModel(model)
view.setItemDelegate(QSqlRelationalDelegate(view.get()))
relationaltablemodel示例说明了如何将QSqlRelationalTableModel与QSqlRelationalDelegate结合使用,为表提供外键支持。
说明:
- 该表必须声明一个主键。
- 表的主键不能包含与另一个表的关系。
- 如果关系表包含引用被引用表中不存在的行的键,则包含无效键的行将不会通过模型公开。用户或数据库负责保持引用的完整性。
- 如果关系的显示列名也用作关系表中的列名,或者如果它在多个关系中用作显示列名,则它将被别名化。别名是关系的表名、显示列名和由下划线连接的唯一id(例如tablename_columnname_id)。
- fieldName()将返回别名列名。当检测到重复时,所有重复显示列名的出现都会产生别名,但不会对主表中的列名产生别名。别名不会影响QSqlRelation,因此displayColumn()将返回原始显示列名。
- 引用表名称是别名。别名是单词"relTblAl"和由下划线连接的相关列索引(例如relTblAl_2)。别名可用于筛选表(例如,setFilter(“relTblAl_2='Oslo’或relTblAl_3=‘USA’”)。
- 使用setData()时,角色应始终为EditRole,使用data()时角色应始终是DisplayRole。
关系表格模型QSglRelationalTableModel方法介绍
用QSqlRelationalTableModel类创建关系表格模型的方法如下所示
from PySide6.QtSql import QSqlRelationalTableModel
QSqlRelationalTableModel(parent: Union[PySide6.QtCore.QObject,NoneType] = None,db: PySide6.QtSql.QSqlDatabase = Default(QSqlDatabase))-> None
- 用QSqlRelationalTableModel 的 setRelation(column:int,relation: QSqlRelation)方法定义QSqlRelationalTableModel 当前数据表格(如 table1)的外键和映射关系,
- 其中参数column 是 tablel的字段编号,用于确定 tablel 中当作外键的字段 field1
- relation 参数是QSgIRelation的实例对象,用于确定另外一个数据表格(如 table2)和对应的字段 field2.
- QSqlRelation实例对象的创建方法是 QSqlRelation(tableName: str,indexCol;strdisplayCol:str)
- 其中tableName 用于确定第2个数据表格 table2,
- indexCol用于指定table2 的字段 feld2,
- displayCol是 table2 中用于显示在 tablel 的 field1位置处的字段 field3,用field3 的值显示在 field1 位置处,field1 的值不显示
- 另外用QSlRelationalTableModel 的setJoinMode(joinMode: QSgIRelationalTableModel,JoinMode)方法可设置两个数据表格的数据映射模式,参数joinMode可取 :
- QSqlRelationalTableModel.InnerJoin(内连接,值为0,只列出 tablel和 table2 中匹配的数据)
- QSqlRelationalTableModel.LeftJoin(左连接 值为1,即使 tablel和 table2 没有匹配的数据也列出 tablel中的数据)。
外键使用方法如下:
model = QSqlRelationalTableModel()
model.setTable("employee")
model.setRelation(2,QSqlRelation("city","id","name"))
model.setRelation(3,QSqlRelation("country","id","name"))
示例
本案例建立了3个表,第1个表包含本案例想要展示的所有内容,其他两个表都是用来对比的。下面对本案例的代码进行简要介绍。
本案例使用的数据库涉及 student2、sex 和 subject 这3个表的内容。其文件放在database.db中
第1个表和第2个表使用同一个模型实例,所以它们的变化完全同步。第1个表和第2个表的不同之处是,第1个表使用 QSqlRelationalDelegate 委托,并为视图创建了良好的编辑体验。这个委托的使用方式如下:
self.tableView.setItemDelegate(QSqlRelationalDelegate(self.tableView))
第1个表和第3个表使用同一个模型的不同实例对象,所以数据变化不会同步;因为第3个表的模型没有设置外键,所以该表就是一个普通表格,是第1个表原始的样子。第1个表的外键的使用方式如下:
self.model.setTable("student2")
self.model.setRelation(2,QSqlRelation("sex","id","name"))
self.model.setRelation(3,QSqlRelation("subject","id","name"))
下面对上述代码进行解释:以第2行为例,setRelation)函数指定第3列(列编号从0开始)作为 student2 表的外键,QSqlRelation 将这个外键与 sex表的id对应,但是它会显示为sex表的name,也就是会显示为"男"或"女"。
from PySide6.QtWidgets import *
from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtSql import QSqlDatabase,QSqlRelationalTableModel,QSqlRelation,QSqlRelationalDelegate
import sys
import os
os.chdir(os.path.dirname(__file__))
class SqlRelationalTableDemo(QMainWindow):
def __init__(self,parent=None):
super(SqlRelationalTableDemo,self).__init__(parent)
self.setWindowTitle("QSqlRelationalTableModel案例")
self.resize(550,600)
self.initModel()
self.createWindow()
def initModel(self):
self.model = QSqlRelationalTableModel()
self.model.setTable("student2")
self.model.setRelation(2,QSqlRelation("sex","id","name"))
self.model.setRelation(3,QSqlRelation("subject","id","name"))
self.model.setHeaderData(0,Qt.Horizontal,"编号")
self.model.setHeaderData(1,Qt.Horizontal,"姓名")
self.model.setHeaderData(2,Qt.Horizontal,"性别")
self.model.setHeaderData(3,Qt.Horizontal,"科目")
self.model.setHeaderData(4,Qt.Horizontal,"成绩")
self.model.select()
def createWindow(self):
self.tableView = QTableView()
self.tableView.setModel(self.model)
self.tableView.setItemDelegate(QSqlRelationalDelegate(self.tableView))
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.tableView2 = QTableView()
self.tableView2.setModel(self.model)
self.tableView2.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.tableView3 = QTableView()
model3 = QSqlRelationalTableModel()
model3.setTable('student2')
model3.select()
self.tableView3.setModel(model3)
self.tableView3.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
layout = QVBoxLayout()
layout.addWidget(self.tableView)
layout.addSpacing(10)
layout.addWidget(self.tableView2)
layout.addSpacing(10)
layout.addWidget(self.tableView3)
widget = QWidget()
self.setCentralWidget(widget)
widget.setLayout(layout)
if __name__ =="__main__":
app = QApplication(sys.argv)
db = QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('./db/database.db')
if db.open()is not True:
QMessageBox.critical(QWidget(),"警告","数据连接失败,程序即将退出")
exit()
demo = SqlRelationalTableDemo()
demo.show()
sys.exit(app.exec())