QT数据库(四):QSqlRelationalTableModel 类
关系数据库概念
例如下列departments、majors、studInfo 这 3 个数据表之间存在关系。

主键与外键
标记“**”的是主键字段,标记“*”的是外键字段。主键字段是一个数据表中表示记录唯一性的字段,例如 studInfo 数据表中的 studID 字段。外键字段是与其他数据表的主键存在关系的字段。
studInfo 数据表中的 departID 字段就是外键字段,它与 departments 数据表中的 departID 字段存在关系。studInfo 数据表中的 departID 存储的是编码,编码的含义需要查询departments 数据表中具有相同 departID 值的一条记录中的 department 字段才可知道。
编码与含义
studInfo 数据表中的 departID 和 majorID 字段一般称为编码字段,departments 数据表和 majors 数据表一般称为编码表。编码表的一条记录有编码字段和编码含义字段,例如 departments 数据表 中,departID 是编码字段,department 是编码含义字段。
在数据库设计中经常采用编码字段和编码表,一是可以减少数据表存储的数据量,例如在studInfo 数据表中不用存储每个学院的全名,而只需存储学院的编码;二是便于修改,例如如果某个学院的名称改变了,那么只需修改 departments 数据表中的一条记录。
主从关系
具有外键关联的两个数据表构成主从关系,主表中的一条记录关联从表中的多条记录。例如, departments 数据表是主表,studInfo 数据表是从表,通过 departments 数据表中一条记录的departID值可以查询出 studInfo 数据表中的多条记录。在删除departments表中的某个学院后,studInfo 表中隶属于该学院的记录也将会被删除。这种方式更新、修改删除都比较方便。
QSqlRelationalTableModel介绍
QSqlRelationalTableModel 是 QSqlTableModel 的子类,它可以作为关系数据表的模型类。
创建对象和设置数据表
QSqlRelationalTableModel 只有一种构造函数,其函数原型定义如下:
QSqlRelationalTableModel(QObject *parent = nullptr, const QSqlDatabase &db=QSqlDatabase())
在创建 QSqlRelationalTableModel 对象时,如果不指定参数 db 的值,就使用应用程序默认的
数据库连接。
创建数据库连接后,用函数 setTable()设置数据表:
tabModel= new QSqlRelationalTableModel(this);
tabModel->setTable("studInfo"); //设置数据表
设置外键关系
关键函数是 setRelation(),该函数原型定义如下:
void QSqlRelationalTableModel::setRelation(int column, const QSqlRelation &relation)
其中,参数 column 是外键字段的字段序号,例如 studInfo 数据表中 departID 字段的序号是 3;参 数 relation 是一个 QSqlRelation 类型的变量,用于表示外键字段关联的编码表、编码字段、编含义字段等信息。
QSqlRelation 类的构造函数原型定义如下:
QSqlRelation(const QString &tableName, const QString &indexColumn, const QString &displayColumn)
其中,tableName 是编码表名称,indexColumn 是外键字段名称,displayColumn 是编码表中代码含义字段的名称。
当使用函数 setRelation()为一个外键字段设置关系时,QSqlRelationalTableModel 内部实际上会创建一个 QSqlTableModel 对象作为编码表的数据模型。
示例程序解读

设置数据表关系
在打开数据表时,创建一个QSqlRelationalTableModel并连接到DB数据库,设置模型的数据表为studInfo,编辑策略为OnManualSubmit(需要手动调用submitAll来更新到数据库)。
void MainWindow::on_actOpenDB_triggered()
{QString aFile=QFileDialog::getOpenFileName(this,"选择文件","","SQLite数据库(*.db3)");if (aFile.isEmpty())return;//打开数据库DB=QSqlDatabase::addDatabase("QSQLITE"); //添加SQLITE数据库驱动DB.setDatabaseName(aFile); //设置数据库名称// DB.setHostName();// DB.setUserName();// DB.setPassword();if (DB.open())openTable(); //打开数据表elseQMessageBox::warning(this, "错误", "打开数据库失败");
}//打开数据表
void MainWindow::openTable()
{tabModel=new QSqlRelationalTableModel(this,DB);tabModel->setTable("studInfo"); //设置数据表tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit); //编辑策略tabModel->setSort(tabModel->fieldIndex("studID"),Qt::AscendingOrder);selModel=new QItemSelectionModel(tabModel,this); //创建选择模型connect(selModel,&QItemSelectionModel::currentChanged,this, &MainWindow::do_currentChanged);
// connect(selModel,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
// this,SLOT(do_currentChanged(QModelIndex,QModelIndex)));ui->tableView->setModel(tabModel);ui->tableView->setSelectionModel(selModel);tabModel->setHeaderData(tabModel->fieldIndex("studID"), Qt::Horizontal, "学号");tabModel->setHeaderData(tabModel->fieldIndex("name"), Qt::Horizontal, "姓名");tabModel->setHeaderData(tabModel->fieldIndex("gender"), Qt::Horizontal, "性别");tabModel->setHeaderData(tabModel->fieldIndex("departID"),Qt::Horizontal, "学院");tabModel->setHeaderData(tabModel->fieldIndex("majorID"), Qt::Horizontal, "专业");//设置代码字段的关系tabModel->setRelation(tabModel->fieldIndex("departID"),QSqlRelation("departments","departID","department")); //学院tabModel->setRelation(tabModel->fieldIndex("majorID"),QSqlRelation("majors","majorID","major")); //专业ui->tableView->setItemDelegate(new QSqlRelationalDelegate(ui->tableView)); //为关系型字段设置默认代理组件
// ui->tableView->setItemDelegateForColumn(tabModel->fieldIndex("departID"),
// new QSqlRelationalDelegate(ui->tableView)); //为关系型字段设置缺省代理组件// ui->tableView->setItemDelegateForColumn(tabModel->fieldIndex("majorID"),// new QSqlRelationalDelegate(ui->tableView)); //为关系型字段设置缺省代理组件tabModel->select(); //查询数据表的数据ui->actOpenDB->setEnabled(false);ui->actRecAppend->setEnabled(true);ui->actRecInsert->setEnabled(true);ui->actRecDelete->setEnabled(true);ui->actFields->setEnabled(true);
} 示例代码中,有两个点比较关键
1、设置外键字段的关系
tabModel->setRelation(tabModel->fieldIndex("departID"),QSqlRelation("departments","departID","department")); //学院tabModel->setRelation(tabModel->fieldIndex("majorID"),QSqlRelation("majors","majorID","major")); //专业
将studInfo数据表中departID外键字段设置为另一个编码表departments对应的字段,并设置字段的含义为编码表中department字段。这样在显示studInfo表中departID时,会自动映射到departments编码表中的department字段。
2、设置代理组件
ui->tableView->setItemDelegate(new QSqlRelationalDelegate(ui->tableView)); 设置后,假设在 tableView 中编辑“学院”和“专业”两个字段的数据时,就会出现一个下拉列表框,
下拉列表内容就是编码表中编码含义字段的所有记录的数据。
添加插入删除记录
这个不用多讲,可查看往期博客
void MainWindow::on_actRecAppend_triggered()
{//添加记录tabModel->insertRow(tabModel->rowCount(),QModelIndex()); //在末尾添加一行QModelIndex curIndex=tabModel->index(tabModel->rowCount()-1,1);selModel->clearSelection(); //清空选择项selModel->setCurrentIndex(curIndex,QItemSelectionModel::Select); //设置当前行
}void MainWindow::on_actRecInsert_triggered()
{//插入记录QModelIndex curIndex=ui->tableView->currentIndex(); //当前行的模型索引tabModel->insertRow(curIndex.row(),QModelIndex());selModel->clearSelection();selModel->setCurrentIndex(curIndex,QItemSelectionModel::Select);
}void MainWindow::on_actRecDelete_triggered()
{//删除当前行tabModel->removeRow(selModel->currentIndex().row());tabModel->submitAll(); //立即提交修改
} 保存和取消
在打开连接数据库后,设置的OnManualSubmit,因此界面的改变想要更新到数据库,需要手动调用submitAll()或者revertAll()来保存或撤销。submitAll()将界面的更改更新到数据库中,revertAll()将界面的更改撤销,也就是重新读取之前数据库中的数据还原界面组件的显示。
void MainWindow::on_actSubmit_triggered()
{//保存修改bool res=tabModel->submitAll();if (!res)QMessageBox::information(this, "消息", "数据保存错误,错误信息\n"+tabModel->lastError().text());else{ui->actSubmit->setEnabled(false);ui->actRevert->setEnabled(false);}
}void MainWindow::on_actRevert_triggered()
{//取消修改tabModel->revertAll();ui->actSubmit->setEnabled(false);ui->actRevert->setEnabled(false);
}
相关文章:
QT数据库(四):QSqlRelationalTableModel 类
关系数据库概念 例如下列departments、majors、studInfo 这 3 个数据表之间存在关系。 主键与外键 标记“**”的是主键字段,标记“*”的是外键字段。主键字段是一个数据表中表示记录唯一性的字段,例如 studInfo 数据表中的 studID 字段。外键字段是与其…...
蓝桥杯刷题——day5
蓝桥杯刷题——day5 题目一题干解题思路一代码解题思路二代码 题目二题干解题思路代码 题目一 题干 给定n个整数 a1,a2,⋯ ,an,求它们两两相乘再相加的和,即: 示例一: 输入: 4 1 3 6 9 输出: 117 题目链…...
YOLO11改进-模块-引入多尺度差异融合模块MDFM
遥感变化检测(RSCD)专注于识别在不同时间获取的两幅遥感图像之间发生变化的区域。近年来,卷积神经网络(CNN)在具有挑战性的 RSCD 任务中展现出了良好的效果。然而,这些方法未能有效地融合双时相特征&#x…...
vlan和vlanif
文章目录 1、为什么会有vlan的存在2、vlan(虚拟局域网)1、vlan原理1. 为什么这样划分了2、如何实现不同交换机相同的vlan实现互访呢3、最优化的解决方法,vlan不同交换机4、vlan标签和vlan数据帧 5、vlan实现2、基于vlan的划分方式1、基于接口的vlan划分方式2、基于m…...
Apache Kylin最简单的解析、了解
官网:Overview | Apache Kylin 一、Apache Kylin是什么? 由中国团队研发具有浓厚的中国韵味,使用神兽麒麟(kylin)为名 的一个OLAP多维数据分析引擎:(据官方给出的数据) 亚秒级响应ÿ…...
MySQL——连接
一.引入库 我们已经分享了很多通过命令行方式去操作数据库,那么数据库该怎么通过语言去访问呢? 这里分享怎么通过C/C来连接数据库。 首先需要到MySQL官网,下载专门用于C/C连接数据库的库,但是一般不需要再去官网下载。 因为在…...
前端微服务实战:大型应用的拆分与治理
"这个系统已经无法维护了..."周五的架构评审会上,我盯着屏幕上那张错综复杂的依赖关系图发愁。作为一个运行了三年的企业级中后台系统,代码量已经超过 50 万行,构建时间长达 40 分钟,任何修改都可能引发连锁反应。 更让人头疼的是,随着业务的快速发展,不同业务线之间…...
Linux shell的七大功能 --- history
1.直接输入“history” 这个命令可以显示出曾经使用过的命令(最近时间的500条) history 2.“history”命令也可以搭配其他命令一起使用。 例:history | grep "vim",找出所有包含“vim”的记录; 也可以搭配…...
C++ webrtc开发(非原生开发,linux上使用libdatachannel库)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、libdatachannel库的下载和build二、开始使用 1.2.引入库3.开始使用 总结 前言 使用c开发webrtc在互联网上留下的资料甚少,经过我一段时间的探…...
C语言刷题
1. 题目描述 根据给出的三角形3条边a:b.c(a.b,c<100.000),计算三角形的周长和面积。 输入描述: 一行,三角形3条边(能构成三角形),中间用一个空格隔开. 输出描述: 一行,三角形周长和面积保留两位小数,中问用一个空…...
LabVIEW实现RFID通信
目录 1、RFID通信原理 2、硬件环境部署 3、程序架构 4、前面板设计 5、程序框图设计 6、测试验证 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联网系…...
Linux 网络流量控制 - 实现概述
摘要 Linux 提供了一整套丰富的流量控制(traffic control)功能。本文档概述了相应的内核代码设计,描述了其结构,并通过描述一种新的排队策略来说明新元素的添加。 1 引言 最近的Linux内核提供了多种流量控制功能。Alexey Kuznetsov(kuznet…...
分布式 令牌桶算法 总结
前言 相关系列 《分布式 & 目录》《分布式 & 令牌桶算法 & 总结》《分布式 & 令牌桶算法 & 问题》 参考文献 《【算法】令牌桶算法》 概述 简介 TBA Token Bucket Algorithm 令牌桶算法是一种流行于网络通信领域的流量控制/频率限制算法。令牌…...
FFMPEG视频转图片
用FFMPEG视频转图片,并且for循环 import os import subprocess# 输入文件夹和输出文件夹路径 input_folder r"I:\xxx" output_base_folder r"D:\xxx\YOLO\data\video" output_subfolder_name "20240609"# 创建输出子文件夹 output…...
docker入门实践---虚拟机环境配置
文章目录 1.检查内核版本2.确定centos7可以上网3.关闭防火墙4.关闭防火墙5.更换阿里云6.安装gcc7.设置镜像仓库(阿里云)8更新软件包9.安装docket-ce10.启动docker11.普通用户权限设置 1.检查内核版本 2.确定centos7可以上网 3.关闭防火墙 下面的这个表示…...
java要防止重复序列化的问题JSON.toJSONString转义问题
要防止重复序列化的问题JSON.toJSONString(entity) 20241213 10:29 背景: 我在设计业务实现echart图标渲染,业务接口实时性查询耗时很长,为了提高系统可用性和用户体验,采用中间表的方案——即在中间表中存储大JSON。 但是在自测…...
TS的类型守卫、类型约束实践
类型守卫 // 基础类型判断 const arr [30, 50] console.log(typeof arr) // object const set new Set(arr) console.log(typeof set) // object const map new Map() console.log(typeof map) // objectclass Customer {constructor() {}buy(method:string) {console.log(…...
文件转曲,限制PDF文件编辑的最佳方案!
随着数字化进程的推进,PDF文件凭借其多样化的功能和优越的兼容性已经被广泛使用,成为了现代文档交流和存储的重要工具,满足了不同用户和行业的需求。 虽然PDF格式文件的功能很多,常见的比如阅读、编辑、加密、转换、还可用于印刷…...
MySQL系列之数据授权(安全)
导览 前言Q:如何对MySQL数据库进行授权管理一、MySQL的“特权”1. 权限级别2. 权限清单 二、授权操作1. 查看权限2. 分配权限3. 回收权限 结语精彩回放 前言 看过博主上一篇的盆友,可以Get到一个知识点:数据授权(eg:g…...
用 Python 实现经典的 2048 游戏:一步步带你打造属于你的小游戏!
用 Python 实现经典的 2048 游戏:一步步带你打造属于你的小游戏!(结尾附完整代码) 简介 2048 是一个简单而又令人上瘾的数字拼图游戏。玩家通过滑动方块使相同数字的方块合并,目标是创造出数字 2048!在这篇…...
如何用嘎嘎降AI处理心理学论文:心理学研究生毕业论文降AI4.8元完整操作教程
如何用嘎嘎降AI处理心理学论文:心理学研究生毕业论文降AI4.8元完整操作教程 关于心理学论文降AI教程,有几个细节提前知道能少走很多弯路。 核心用嘎嘎降AI(www.aigcleaner.com),4.8元,达标率99.26%。这篇…...
深入解析Ollama-for-amd:AMD GPU本地大模型部署实战指南
深入解析Ollama-for-amd:AMD GPU本地大模型部署实战指南 【免费下载链接】ollama-for-amd Get up and running with Llama 3, Mistral, Gemma, and other large language models.by adding more amd gpu support. 项目地址: https://gitcode.com/gh_mirrors/ol/ol…...
告别字幕与水印:LTX 2.3工作流,一键高效清除,附详细使用方法。
一、LTX2.3功能介绍 核心功能:一键去除视频字幕和水印 工作流程: 上传视频 设置参数 设置提示词(提示词固定不变) 点击运行,即可输出没有水印和字幕的视频 ⬇️⬇️⬇️ 1.核心模型 水印去除模型字幕去除模型 2.模型…...
RWKV vs Llama2:在论文审稿任务上,我们为什么第一版选了它?(附长上下文模型选型避坑指南)
RWKV与Llama2在论文审稿任务中的技术选型思考 当面对论文审稿这一知识密集型任务时,模型选型往往成为项目成败的关键。2023年第三季度,我们在构建首个论文审稿GPT系统时,曾在RWKV与Llama2之间面临艰难抉择。本文将深入剖析两种架构的核心差异…...
深入浅出ASoC:用一张图看懂Machine、Platform、Codec在Android音频驱动中的分工与协作
深入浅出ASoC:用一张图看懂Machine、Platform、Codec在Android音频驱动中的分工与协作 在Android音频系统的开发中,ASoC(ALSA System on Chip)框架扮演着至关重要的角色。对于刚接触这一领域的开发者来说,理解Machine、…...
Hyperf 高并发的庖丁解牛
它的本质是:**Hyperf 的高并发并非来自 PHP 语言本身的计算速度,而是来自对 I/O 等待时间 (I/O Wait Time) 的极致利用。它通过 Swoole/Swow 扩展 将传统的 同步阻塞 (Sync-Blocking) 模式转变为 异步非阻塞 (Async-Non-blocking) 模式,并利用…...
从Simulink模型到S32K3xx芯片:手把手教你玩转NXP官方MBD工具包(v1.4实战)
从Simulink模型到S32K3xx芯片:手把手教你玩转NXP官方MBD工具包(v1.4实战) 在汽车电子开发领域,时间就是竞争力。当传统手写代码遇上复杂的汽车MCU外设配置,工程师们常常陷入寄存器手册的海洋。而基于模型的设计&#x…...
P15906 [TOPC 2024] Business Magic 题解
P15906 [TOPC 2024] Business Magic Link: https://www.luogu.com.cn/problem/P15906 题目描述 沿街有 nnn 家商店,按从近到远的顺序编号为 111 到 nnn。上个月,商店 kkk 的净利润为 rkr_krk。如果 rkr_krk 为正,表示盈利 rkr_krk 美…...
保姆级教程:在Windows上用Python连接CoppeliaSim远程API(附避坑指南)
从零开始掌握CoppeliaSim与Python的远程控制:Windows环境实战指南 在机器人仿真领域,CoppeliaSim(原V-REP)因其强大的功能和友好的用户界面而广受欢迎。对于希望将Python的灵活性与CoppeliaSim的仿真能力结合的研究者和工程师来说…...
STM32F407 HAL库驱动42步进电机:从CubeMX配置到代码调试的完整避坑指南
STM32F407 HAL库驱动42步进电机:从CubeMX配置到代码调试的完整避坑指南 在嵌入式开发领域,步进电机控制一直是工业自动化、3D打印和机器人控制等应用中的核心技术。对于刚接触STM32系列微控制器的开发者来说,使用HAL库驱动42步进电机可能会遇…...
