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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/490556.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人工智能原理实验四:智能算法与机器学习

一、实验目的 本实验课程是计算机、智能、物联网等专业学生的一门专业课程,通过实验,帮助学生更好地掌握人工智能相关概念、技术、原理、应用等;通过实验提高学生编写实验报告、总结实验结果的能力;使学生对智能程序、智能算法等…

免费生成AI PPT产品推荐?

要完全免费几乎是没有的,要知道AI还是非常烧钱的。 不过免费蹭还是有很多方法的,这里收集了一些: 下面分享我自己免费蹭过的几款AI制作PPT的工具。 1 金山-WPS PPT对我们来说并不陌生,而微软的PowerPoint与金山的WPS也是我们最常…

WPF 消息循环(二)

们已经知道,win32/MFC/WinForm/WPF 都依靠消息循环驱动,让程序跑起来。 这里就介绍 WPF 中是如何使用消息循环来驱动程序的。 1. 背景 只听说过 Dispatcher ,哪里来的消息循环? WPF 启动运行堆栈: > WpfApp1.…

PostgreSQL 常用运维SQL整理

一、查询并杀会话 -- 查询会话 select pid,usename,client_addr,client_port,query_start,query,wait_event from pg_stat_activity; -- 杀会话 select pg_terminate_backend(pid号); -- 使用如下命令自动生成杀会话语句 select datid,datname,pid,usesysid,usename,applicat…

JS中的Promise用法大全

目录 一、相关概念介绍1.什么是Promise2.Promise状态3.创建Promise4.then()方法5.catch方法6.链式调用7.异步编程 二、使用1.构造Promise2.executor 函数3.then() 方法4.then() 方法返回的Promise的状态 三、Async/Await语法糖四、Promise应用场景五、总结 一、相关概念介绍 1…

【新界面】基于卷积神经网络的垃圾分类(Matlab)

基于CNN的垃圾识别与分类GUI【新界面】 有需要可直接联系我,基本都在在线,能秒回!可加我看演示视频,不懂可以远程教学 1.此项目设计包括两份完整的源代码,有GUI界面的代码和无GUI界面系统的代码。 (以下部…

pytorch bilstm crf的教程,注意 这里不支持批处理,要支持批处理 用torchcrf这个。

### Bi-LSTM Conditional Random Field ### pytorch tutorials https://pytorch.org/tutorials/beginner/nlp/advanced_tutorial.html ### 模型主要结构: ![title](sources/bilstm.png) pytorch bilstm crf的教程,注意 这里不支持批处理 Python version…

bugku-simple MQTT-wp解析

1.下载题目打开题目,是一个流量包,题目说是MQTT,然后打开流量之后的流量都是MQTT,我们来搜一下MQTT是什么流量 MQTT流量: 是一种基于发布订阅模式的轻量级的通讯协议,并且该协议构建于TCP/IP协议之上&…

HCIA-Access V2.5_2_2网络通信基础_TCP/IP协议栈报文封装

TCP/IP协议栈的封装过程 用户从应用层发出数据先会交给传输层,传输层会添加TCP或者UDP头部,然后交给网络层,网络层会添加IP头部,然后交给数据链路层,数据链路层会添加以太网头部和以太网尾部,最后变成01这样…

Windows 与 Linux 下 Ping IPv6 地址 | 常用网络命令

注:本文为网络命令相关文章合辑。 未整理去重。 一、IPv6 概述 IPv6 即 “Internet 协议版本 6”,因 IPv4 地址资源面临耗尽问题而被引入以替代 IPv4。IPv6 则提供了理论上多达 2 128 2^{128} 2128 个地址,有效解决地址不足困境。 IPv6 具…

关卡选择与布局器

unity布局管理器 使用unity布局管理器轻松对关卡选择进行布局。 实现过程 准备普通按钮button设置字体和对应的sprite设置父gameobject(levelbase) 再创建UI.image(selectbackground)布局背景和大小gameobject(grid…

python学opencv|读取图像(十三)BGR图像和HSV图像互相转换深入

【1】引言 前序学习过程中,我们偶然发现:如果原始图像是png格式,将其从BGR转向HSV,再从HSV转回BGR后,图像的效果要好于JPG格式。 文章链接为: python学opencv|读取图像(十二)BGR图…

程序算术题-2

程序算术题-2 输出所有组合逻辑实例代码 输出所有排列逻辑实例代码 输出所有组合 计算一组数字按n位数组合的所有组合。 逻辑 /*** param stringBuilder 用于组合的拼接* param list 组合数序列* param level 目前位数* param exceptedLevel 组合期待位数*/…

VQ-VAE和VAE 的区别是什么?

第一行所展示的就是普通的VAE,它的核心是通过encoder和decoder,将像素空间的图像压缩到一个提取了核心特征的隐变量向量。VQ-VAE的思想是,即使VAE中压缩的这个隐变量中的向量提取了图片中的核心特征信息,但是这些信息仍然可能存在冗余&#x…

使用Qt Creator设计可视化窗体(一)

一、创建项目 打开 Qt Creator ,在菜单栏中选中: “文件” --------> “新建文件或项目” ;或者使用快捷键:Ctrl n;或者直接点击:“new” Qt 中的构建工具有三种可供选择,分别是&#…

Python3 爬虫 Scrapy 与 Redis

Scrapy是一个分布式爬虫的框架,如果把它像普通的爬虫一样单机运行,它的优势将不会被体现出来。因此,要让Scrapy往分布式爬虫方向发展,就需要学习Scrapy与Redis的结合使用。Redis在Scrapy的爬虫中作为一个队列存在。 一、Scrapy_r…

Docker 学习

Docker 学习 Docker 概念 Docker 安装 一般是在服务器里 Docker阿里云镜像加速 配置主要是Linux命令 Docker命令大纲及帮助文档的使用 docker帮助文档 查看docker命令 docker --help查看某个命令,例如ps的详细文档 docker ps --help也可查阅 [docker官方帮助手…

超牛免费 机械臂模型、工业机器人模型下载网站集合

‌机械臂是一种高精度、多输入多输出的复杂系统,能够模仿人手的动作,按照给定程序、轨迹和要求实现自动抓取、搬运等功能‌。它通常由执行机构、驱动装置、控制系统以及传感器等组成,能够完成各种复杂的动作。‌ 机械臂在工业、医学、娱乐、…

【Python技术】同花顺wencai涨停分析基础上增加连板分析

周末,有读者加我, 说 之前的涨停分析 是否可以增加连板分析。 这个可以加上。 先看效果 这里附上完整代码: import streamlit as st import pywencai import pandas as pd from datetime import datetime, timedelta import plotly.graph_o…

小程序子组件调用父组件方法、父组件调用子组件方法

1、子组件调用父组件方法 子组件this.triggerEvent(finish); startShare(e) {let url config.apiUrl "/business/lzShare/edit";let data this.data.currentData;util.httpPut(url, data).then((res) > {this.triggerEvent(finish);console.log(res.result);})…