30. 数据库操作
一、SQL与数据库
数据库 (database)是统一管理的、有组织的、可共享的大量数据的集合。数据库将数据存储在一个或多个表格中,管理这个数据库的软件称为 数据库管理系统(database management system, DBMS)。
数据库不是针对具体的应用程序,而是立足于数据本身的管理,它将所有数据保存在数据库中,进行科学的组织,并借助于数据库管理系统,通过 SQL 与各种应用程序或应用系统连接,使之能方便地使用数据库中的数据。
关系型数据库管理系统一般都支持结构化查询语言 (structured query language, SQL),SQL 是数据库的基础,通过它可以实现对关系型数据库进行查询、新增、更新、删除、求和和排序等操作。关系型数据库由数据表 (table)构成,数据表的一行数据称为字段,可以在数据库中添加和删除数据表,在数据表中可以查询、添加和删除字段。
SQL 语句主要分为以下几类:
- 数据定义语言(data definition language, DDL)。DDL 是 SQL 中定义数据结构和数据库对象的语言,主要关键字有CREATE、ALTER 和 DROP。
- 数据操纵语言(data manipulation language, DML)。DML 是 SQL 中操纵数据库中数据的语言,可对数据库中的数据进行插入、删除、更新和选择,主要关键字有 INSERT、UPDATE、DELETE 和 SELECT。
- 事务控制语言(transaction control language, TCL)。TCL 用于管理 DML 对数据所作的修改,主要关键字有提交 COMMIT 和撤销 ROLLBACK。
- 对数据库的操作需要用 COMMIT 进行确认,事务一经提交就不能撤销,如果要在提交前撤销对数据库的操作,可以用 ROLLBACK 来 “回滚” 到相应事务开始时的状态。
- 数据控制语言(data control language, DCL)。DCL 是对数据访问权限进行控制、定义安全级别及创建用户的语言,主要关键字有 GRANT 和 REVOKE。
二、SQLite数据库
SQLite 数据库是一个常用的开源、跨平台、轻量化本机版数据库,可以作为嵌入式数据库使用。Python 自带的 sqlite3 可以实现对 SQLite 数据库的查询,使用前需要用 import sqlite3 语句导入 sqlite3。
sqlite3 提供了两个基本的类:数据库连接 Connection 和游标 Cursor。用 sqlite3 的 connect(database:str) 方法 打开一个已经存在的 SQLite 数据库或新建一个数据库,并返回 Connection 对象,其中 databaseName 是 本机上已经存储的数据库文件,或者新建立的数据库文件名称,还可取 ":memory:",表示 在内存中创建临时数据库。用 Connection 对象的 cursor() 方法 获取 Cursor 对象,用 Cursor 对象提供的方法可以执行 SQL 指令。
数据库连接 Connection 类的常用方法如下:
cursor() -> Cursor # 创建并返回Cursor对象
commit() -> None # 提交当前的事务
interrupt() -> None # 停止还未执行的任务
rollback() -> None # 放弃对数据库的操作,返回到调用commit()时的状态
close() -> None # 关闭数据库
backup(target:Connection) -> None # 将数据备份到另一个数据库中
游标 Cursor 用于执行 SQL 语句,它的常用方法如下:
execute(sql:sqk, parameters:Sequence=()) -> None # 执行一条SQL语句,parameters是SQL中的占位符的值
executemany(sql:str, parameters:Sequence) -> None # 重复执行SQL语句,parameters是SQL中的占位符的值
executescript(sql_script:str) -> None # 执行多条SQL语句
fetchone() -> Tuple # 获取数据表中的下一行数据构成的元组或None
fetchmany(size:int=cursor.arraysize) -> List # 获取多行数据,构成的列表,参数size是要获取的行数
fetchall() -> List # 获取所有剩余的行构成的列表
close() -> None # 关闭游标
新建一个 template.py 文件。
import sqlite3
connection = sqlite3.connect("test.db") # 1.获取连接
cursor = connection.cursor() # 2.获取游标
cursor.execute("create table if not exists user(name varchar(20), age int)") # 3.执行SQL语句,创建表
# 4.向表中插入数据,新增用户信息
cursor.execute("insert into user(name,age) values('Sakura',10)")
cursor.execute("insert into user(name,age) values('Mikoto',14)")
cursor.execute("insert into user(name,age) values('Shana',15)")
cursor.execute("select * from user") # 5.执行查询结果
# 6.获取查询结果
result = cursor.fetchone() # 使用fetchone()获取一条记录
print(result,end="\n\n")
result = cursor.fetchmany(2) # 使用fetchmany()获取多条记录
print(result,end="\n\n")
result = cursor.fetchall() # 使用fetchall()获取全部查询结果
print(result,end="\n\n")
# 7.修改用户信息
# 使用问号作为占位符代替具体的数值,然后使用一个元组来替代问号
# 使用占位符的方式可以避免SQL注入的风险
cursor.execute("update user set age = ? where name = ?",(12,"Sakura"))
cursor.execute("select * from user where name = ?",("Sakura",))
result = cursor.fetchall()
print(result,end="\n\n")
# 8.删除用户数据
cursor.execute("delete from user where name = ?",("Shana",))
cursor.execute("select * from user")
result = cursor.fetchall()
print(result,end="\n\n")
cursor.close() # 9.关闭游标
connection.close() # 10.关闭数据库
三、PySide6操作数据库
PySide6 提供了对常用数据库的驱动,可以从数据库中进行查询、读取、写入、修改、删除数据等操作,同时提供了可视化的控件和数据库数据模型,可以将数据从数据库读取到数据模型中,然后用 Model/View 结构在图形界面中对数据进行操作。
PySide6 可以驱动常用的关系型数据库,在对数据库进行操作之前需要用 QSqlDatabase 类建立对数据库的连接,然后再用 QSqlQuery 类执行 SQL 命令,实现对数据库的操作。
我们可以在终端中使用 pip 安装 PySide6 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载。
pip install pyside6 -i https://mirrors.aliyun.com/pypi/simple
国内常用的 pip 下载源列表:
- 阿里云 https://mirrors.aliyun.com/pypi/simple
- 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple
- 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple
- 中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple
3.1、数据库连接
在对数据库进行操作之前,需要先建立对数据库的连接。数据库连接用 QSqlDatabase 类。
用 QSqlDatabase 创建实例的方法如下所示:
QSqlDatabase()
QSqlDatabase(type:str)
其中 type 是 数据库的驱动类型,可取的值下所示。如果要建立自定义的驱动类型,可以创建 QSqlDriver 的子类。
"QDB2" # IBM DB2数据库
"QIBASE" # Borlad InterBase数据库
"QMYSQL" # MySQL数据库
"QODBC" # 支持ODBC接口的数据库
"QPSQL" # PostgreSQL数据库
"QSQLITE" # SQLite数据库
"QOCI" # Oracle数据库
QSqlDatabase 类的常用方法如下:
# 实例方法
connectionName() -> str # 获取连接的名称
driverName() -> str # 获取驱动类型的名称
setDatabaseName(name:str) -> None # 设置连接的数据库名称
databaseName() -> str # 获取连接的数据库名称
setHostName(host:str) -> None # 设置连接的主机名称
hostName() -> str # 获取连接的主机名称
setPort(port:int) -> None # 设置连接的端口号
port() -> int # 获取连接的端口号
setUserName(username:str) -> None # 设置用户名
userName() -> str # 获取用户名
setPassword(password:str) -> None # 设置密码
password() -> str # 获取密码
setConnectOptions(options="") -> None # 设置连接选项
connectOptions() -> str # 获取连接参数
open() -> bool # 打开数据库
open(user:str, password:str) -> bool # 打开数据库
isOpen() -> bool # 获取数据库是否打开
isOpenError() -> bool # 获取打开数据库是是否出错
isValid() -> bool # 获取连接是否有效
close() -> None # 关闭连接
transaction() -> bool # 开启事务
commit() -> bool # 提交事务
rollback() -> bool # 回滚事务
lastError() -> QSqlError # 获取最后的错误信息
record(tablename:str) -> QSqlRecord # 获取含有字段名称的记录
setNumericalPrecisionPolicy(precisionPolicy:QSql.NumericalPrecisionPolicy) -> None # 设置对数据库进行查询时默认的数值精度
tables(kind:QSql.TableType=QSql.Tables) -> List[str] # 根据表格类型参数,获取数据库中的表格名称
# 静态方法
drivers() -> List[str] # 获取系统支持的驱动类型
isDriverAvailable(name:str) -> bool # 获取是否支持某种类型的驱动
# 添加数据库连接
addDatabase(driver:str, connectionName:str=QLatin1StringView(QSqlDatabase.defaultConnection)) -> QSqlDatabase
# 根据连接名称获取数据库连接
database(connectionName:str=QLatin1StringView(QSqlDatabase.defaultConnection), open:bool=true) -> QSqlDatabase
removeDatabase(connectionName:str) -> None # 删除数据库连接
connectionNames() -> List[str] # 获取已经添加的连接名称
# 如果connectionNames()返回值中有指定的连接,则返回True
contains(connectionName:str=QLatin1StringView(QSqlDatabase.defaultConnection)) -> bool
用静态方法 addDatabase(type:str,connectionName:str='qt_sql_default_connection') 添加某种驱动类型的连接,其中参数 type 是 驱动类型。对同一个数据库可以添加多个连接,数据库连接的识别是通过连接名称 connectionName 来区分的,而不是关联的数据库。如果不输入连接名称,则该连接作为默认连接,将使用默认的连接名称 'qt_sql_default_connection'。
在用 open() 方法 打开数据库 前,需要分别用 setDatabaseName(name:str) 方法、setHostName(host:str) 方法、setPassword(password:str) 方法、setPort(port:int) 方法、setUserName(name:str) 方法、setConnectOptions(options:str='') 方法 设置连接的数据库文件名、主机名、密码、端口号、用户名和连接参数,如果用 open() 方法打开数据库后再设置这些参数将不起作用。
在用 setDatabaseName(name:str) 方法 打开 SQLite 数据库 时,如果数据库不存在则创建新数据库,参数 name 也可取 ':memory:',在内存中临时创建数据库,程序运行结束后删除数据库。对于 ODBC 数据库,参数 name 是 *.dsn 文件或连接字符串;对于 Oracle 数据库,name 参数是 TNS 服务名称。
用 setConnectOptions(options:str='') 方法 设置数据库的参数,不同的驱动类型需要设置的参数也不同,各参数值之间用分号 ; 隔开,例如 SQLite 数据库,可选参数如下:
QSQLITE_BUSY_TIMEOUT
QSQLITE_OPEN_READONLY
QSQLITE_OPEN_URI
QSQLITE_ENABLE_SHARED_CACHE
QSQLITE_ENABLE_REGEXP
QSQLITE_NO_USE_EXTENDED_RESULT_CODES
用 tables(kind:QSql.TableType=QSql.Tables) 方法 获取数据库中存在的数据表名称列表,其中参数 kind 是 QSql.TableType 类型的枚举值,可以取值如下:
QSql.TableType.Tables # 对用户可见的所有表
QSql.TableType.SystemTables # 数据库使用的内部表
QSql.TableType.Views # 对用户可见的所有视图
QSql.TableType.AllTables # 以上三种表和视图
用 setNumericalPrecisionPolicy(precisionPolicy:QSql.NumericalPrecisionPolicy) 方法 设置对数据库进行查询时默认的数值精度,参数 precisionPolicy 是 QSql.NumericalPrecisionPolicy 类型的枚举值,可以取值如下:
QSql.NumericalPrecisionPolicy.LowPrecisionInt32 # 32位整数,忽略小数部分
QSql.NumericalPrecisionPolicy.LowPrecisionInt64 # 64位整数,忽略小数部分
QSql.NumericalPrecisionPolicy.LowPrecisionDouble # 双精度值,默认值
QSql.NumericalPrecisionPolicy.HighPrecision # 保持数据的原有精度
用 lastError() 方法 获取最后的出错信息 QSqlError 对象,用 QSqlError 的 type() 方法可以 获取出错类型,返回值是 QSqlError.ErrorType 的枚举值或 -1(不能确定错误类型),QSqlError.ErrorType 的枚举值如下:
QSqlError.ErrorType.NoError # 0,没有错误
QSqlError.ErrorType.ConnectionError # 1,数据库连接错误
QSqlError.ErrorType.StatementError # 2,SQL命令语法错误
QSqlError.ErrorType.TransactionError # 3,事务错误
QSqlError.ErrorType.UnknownError # 4,未知错误
3.2、数据库查询
数据库查询 QSqlQuery 用于执行标准的 SQ L命令,例如 CREATE TABLE、SELECT、INSERT、UPDATE、DELETE 等,还可执行特定的非标准的 SQL 命令。
用 QSqlQuery 类创建实例对象的方法如下所示:
QSqlQuery(db:QSqlDatabase)
QSqlQuery(other:QSqlQuery)
QSqlQuery(query:str="", db:QSqlDatabase=Default(QSqldatabase))
QSqlQuery 类常用方法如下:
prepare(query:str) -> bool # 准备SQL语句,成功返回True
addBindValue(val:Any, type:QSql.ParamType=QSql.In) -> None # 如果prepare(query)中有占位符,则按顺序依次设置占位符的值
bindValue(placeholder:str, val:Any, type:QSql.ParamType=QSql.In) -> None # 如果prepare(query)中有占位符,则根据占位名称设置占位符的值
bindValue(pos:int, val:Any, type:QSql.ParamType=QSql.In) -> None # 如果prepare(query)中有占位符,则根据占位位置设置占位符的值
boundValue(placeholder:str) -> Any # 根据占位符名称获取绑定值
boundValue(pos:int) -> Any # 根据占位位置获取绑定值
boundValues() -> List[Any] # 获取绑定值列表
exec() -> bool # 执行prepare(query)准备的SQL语句
exec(query:str) -> bool # 执行SQL语句,成功返回True
execBatch(mode:QSqlQuery.BatchExecutionMode=QSqlQuery.BatchExecutionMode.ValuesAsRows) -> bool # 批处理用prepare()方法准备的命令
finish() -> None # 完成查询,不再获取数据
clear() -> None # 清空结果,释放所有资源,查询处于不活跃状态
executedQuery() -> str # 返回最后正确执行的SQL语句
lastQuery() -> str # 返回当前查询使用使用的SQL语句
# 返回查询的当前内部位置,第一个记录位置是0,如果位置无效,则返回值是QSql.BeforeFirstRow(值是-1)或QSql.AfterLastRow(值是-2)
at() -> int
isSelect() -> bool # 当前SQL语句是SELECT语句时返回True
isValid() -> bool # 当前查询定位在有效记录时返回True
isActive() -> bool # 获取查询是否处于活跃状态
isNull(field:int) -> bool # 将查询处于非活跃状态,查询定位在无效记录或空字段上时返回True
isNull(name:str) -> bool # 将查询处于非活跃状态,查询定位在无效记录或空字段上时返回True
first() -> bool # 将当前查询位置定位在第一个记录
last() -> bool # 将当前查询位置定位到最后一个记录
previous() -> bool # 将当前查询位置定位到前一个记录
next() -> bool # 将当前查询位置定位到下一个记录
seek(i:int, relative:bool=false) -> bool # 将当前查询位置定位到指定记录
setForwardOnly(forward:bool) -> None # 当forward取True时,只能用next()和seek()方法来定位结果,此时,seek()参数为正值
isForwardOnly() -> bool # 获取定位模式
lastError() -> QSqlError # 返回最近出错信息
lastInsertId() -> Any # 返回最近插入行的对象ID号
nextResult() -> bool # 放弃当前查询结果并定位到下一个结果
record() -> QSqlRecord # 返回查询指向的当前记录
size() -> int # 返回查询结果的记录数,无法确定、非SELECT命令或数据库不支持该功能是返回-1、
value(i:int) -> Any # 根据字段索引,获取当前记录的字段值
value(name:int) -> Any # 根据字段名称,获取当前记录的字段值
numRowsAffected() -> int # 获取受影响的行的个数,无法确定或查询处于非活跃状态时返回-1
swap(other:QSqlQuery) -> None # 与其它查询交换数据
用 exec(query:str) 方法 直接执行 SQL 命令。也可以用 prepare(query:str)方法 准备要执行的 SQL 命令;用 exec()方法或 execBatch(mode=QSqlQuery.ValuesAsRows) 方法 执行已经准备好的 SQL 命令,其中参数 mode 是 QSqlQuery.ValuesAsRows 类型的枚举值,可以取值如下:
QSqlQuery.BatchExecutionMode.ValuesAsRows # 更新多行,列表中的每个值作为一个值来更新下一行
QSqlQuery.BatchExecutionMode.ValuesAsColumns # 更新一行,列表作为一个值来使用
在用 prepare(query:str) 方法准备 SQL 命令时,SQL 命令中可以有占位符,占位符可以用问号 "?"(ODBC 格式),也可以用冒号 ":surname"(Oracle 格式)。
占位符的真实值可以用 addBindValue(val:Any,type:QSql.ParamType=QSql.In) 方法按照 顺序 依次设置,也可用 bindValue(placeholder:str,val:Any,type:QSql.ParamType=QSql.In) 方法根据 占位符的名称 设置,还可以用 bindValue(pos:int,val:Any,type:QSql.ParamType=QSql.In) 方法根据 占位符的位置 设置,其中参数 type 是 QSql.ParamType 类型的枚举值,可以取值如下:
QSql.ParamTypeFlag.In # 绑定参数输入到数据库中
QSql.ParamTypeFlag.Out # 绑定参数从数据库中接收数据
QSql.ParamTypeFlag.InOut # 既可以将数据输入到数据库中,也可以从数据库中接收数据
QSql.ParamTypeFlag.Binary # 数据是二进制,需要将“|”与以上三种参数联合使用
当返回的结果有多个记录时,需要首先定位到所需要的记录上,当 isActive() 方法和 isSelect() 方法的返回值是 True 时,可以用 first()、last()、previous()和 next() 方法分别定位到 第一个记录、最后一个记录、前一个记录 和 下一个记录 上,成功则返回 True;用 seek(index:int,relative:bool=False) 方法可以 定位到指定的记录 上。如果只是想从开始到结束浏览数据,可以设置 setForwardOnly(True),这样可以节省大量的内存。
用 value(index:int) 方法或 value(name:str) 方法 获取当前记录的字段值。也可用 record() 方法 获取当前的记录对象 QSqlRecord,QSqlRecord 是指 数据表(table)或视图(view)中的一行,然后用记录对象的 value(index:int) 方法或 value(name:str) 方法 获取字段的值,用记录对象的 count() 方法 获取字段的数量,用 indexOf(name:str) 方法 获取字段的索引。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtSql import QSqlDatabase, QSqlQuery
if __name__ == "__main__":
persons = (("Sakura", 10), ("Mikoto", 14), ("Shana", 15))
db_name = "./test.db"
app = QApplication(sys.argv) # 1.创建QApplication对象
db = QSqlDatabase.addDatabase("QSQLITE", "SQLite") # 2.添加数据库连接
db.setDatabaseName(db_name) # 3.设置连接数据库的名称
query = QSqlQuery(db) # 4.创建数据库查询对象
if db.open(): # 5.打开数据库
query.exec("create table if not exists user(name varchar(20), age int)") # 6.执行SQL语句,创建表
if db.transaction(): # 7.开启事务
for person in persons:
query.prepare("insert into user(name, age) values(?, ?)") # 8.准备SQL语句
query.addBindValue(person[0]) # 9.按顺序设置占位符的值
query.bindValue(1, person[1]) # 10.按索引位置设置占位符的值
query.exec() # 11.执行SQL语句
db.commit() # 12.提交事务
if query.exec("select * from user"): # 13.查询数据
while query.next(): # 14.遍历结果集
print(query.value("name"), query.value("age")) # 15.获取字段的值
db.close() # 16.关闭数据库
在使用 PySide6 操作数据库时,必须先创建
QApplication实例,否则会报qt.sql.qsqldatabase: QSqlDatabase requires a QCoreApplication。
四、数据库模型
PySide6 提供了对数据库进行可视化操作的 Model/View 结构,通过数据库模型读入在数据库中查询到的数据,并通过视图控件(如 QTableView)显示数据库模型中的数据,通过代理控件在视图控件中对数据进行新增、更新、删除等操作,再通过数据模型把操作后的数据保存到数据库中。PySide6 提供的数据库模型有 QSqlQueryModel、QSqlTableModel 和 QSqlRelationalTableModel。

4.1、数据库查询模型
数据库查询模型 QSqlQueryModel 只能从数据库中读取数据,而不能修改数据,可以用视图控件。
用 QSqlQueryModel 创建数据库查询模型对象的方法如下所示。
QSqlQueryModel(paarent:QObject=None)
QSqlQueryModel 类常用方法如下:
setQuery(query:QSqlQuery) -> None # 设置数据库查询
setQuery(query:str, db:QSqkDatabase=QSqlDatabase()) -> None # 设置数据库查询
query() -> QSqlQuery # 获取数据库查询
record() -> QSqlRecord # 获取包含字段信息的空记录
record(row:int) -> QSqlRecord # 获取指定行的记录
clear() -> None # 清空查询模型中的数据
# 设置显示数据的视图控件表头某角色的值
setHeaderData(section:int, orientation:Qt.Orientation, value:Any, role:int=Qt.EditRole) -> bool
# 获取显示数据的视图控件表头某种角色的值
headerData(section:int, orientation:Qt.Orientation, role:int=Qt.DisplayRole) -> Any
rowCount(parent:QModelIndex=QModelIndex()) -> int # 获取数据表中记录(行的数量)
columnCount(parent:QModelIndex=QModelIndex()) -> int # 获取数据表中列的数量
用 setQuery(query:QSqlQuery) 方法或 setQuery(query:str,db:QSqlDatabase=Default(QSqlDatabase)) 方法 设置数据库查询 QSqlQuery;用 setHeaderData(section:int,orientation:Qt.Orientation,value:Any,role:int=Qt.EditRole) 方法 设置显示数据的视图控件表头某角色的值,在 orientation 取 Qt.Orientation.Horizontal,并且 section 取值合适时返回 True,其他情况返回 False,其中 value 是 某种角色的值,section 是 列索引。
新建一个 ui.py 文件,用来存放 UI 相关的代码。
from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QTableView
from PySide6.QtWidgets import QVBoxLayout
class MyUi:
def setupUi(self, window:QWidget):
window.resize(800, 600) # 1.设置窗口对象大小
layout = QVBoxLayout(window) # 2.创建一个垂直布局
self.tableView = QTableView(window) # 3.创建一个表格控件,并添加到布局中
layout.addWidget(self.tableView)
self.tableView.setSortingEnabled(True) # 4.设置单击头部时是否可以排序
self.tableView.setAlternatingRowColors(True) # 5.设置表格颜色交错显示
新建一个 widget.py 文件,用来存放业务逻辑相关的代码。
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
from PySide6.QtCore import Qt
from ui import MyUi
class MyWidget(QWidget):
def __init__(self):
super().__init__() # 1.调用父类Qwidget类的__init__()方法
self.__ui = MyUi()
self.__ui.setupUi(self) # 2.初始化页面
self.use_database() # 3.使用数据库
self.__ui.tableView.setModel(self.sqlQueryModel) # 4.设置表格控件的模型
def use_database(self):
persons = (("Sakura", 10), ("Mikoto", 14), ("Shana", 15))
db_name = "./test.db"
db = QSqlDatabase.addDatabase("QSQLITE") # 1.添加数据库连接
db.setDatabaseName(db_name) # 2.设置连接数据库的名称
query = QSqlQuery(db) # 3.创建数据库查询对象
self.sqlQueryModel = QSqlQueryModel() # 4.创建数据库查询模型
if db.open(): # 5.打开数据库
# 6.执行SQL语句,创建表
self.sqlQueryModel.setQuery("create table if not exists user(name varchar(20), age int)")
if db.transaction(): # 7.开启事务
for person in persons:
query.prepare("insert into user(name, age) values(?, ?)") # 8.准备SQL语句
query.addBindValue(person[0]) # 9.按顺序设置占位符的值
query.bindValue(1, person[1]) # 10.按索引位置设置占位符的值
query.exec() # 11.执行SQL语句
db.commit() # 12.提交事务
self.sqlQueryModel.setQuery("select * from user") # 13.设置查询
header = self.sqlQueryModel.record() # 14.获取字段信息当作头部信息
for i in range(header.count()):
# 15.设置显示数据的视图控件表头某角色的值
self.sqlQueryModel.setHeaderData(i, Qt.Orientation.Horizontal, header.fieldName(i), Qt.ItemDataRole.DisplayRole)
db.close() # 16.关闭数据库
if __name__ == "__main__":
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
window = MyWidget() # 2.创建一个窗口
window.show() # 3.展示窗口
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
4.2、数据库表格模型
【1】、QSqlTableModel 数据库表格模型
数据库表格模型 QSqlTableModel 借助视图控件可以对查询到的数据进行修改、插入、删除和排序等操作,同时将修改后的数据更新到数据库中。
用 QSqlTableModel 创建数据库表格模型的方法如下所示:
QSqlTableModel(parent:QObject=None, db:QSqlDatabase=Default(QSqlDataBase))
数据库表格模型 QSqlTableModel 的常用方法按如下所示:
# 实例方法
insertRows(row:int, count:int, parent:QModelIndex=QModelIndex()) -> bool # 插入多个空行,在OnFieldChange和OnRowChange模式下每次只能插入一行
removeRow(row:int, parent:QModelIndex=QModelIndex()) -> bool # 删除指定行
removeRows(row:int, count:int, parent:QModelIndex=QModelIndex()) -> bool # 删除多行
insertColumns(column:int, count:int, parent:QModelIndex=QModelIndex()) -> bool # 插入多个空列
removeColumn(column:int, parent:QModelIndex=QModelIndex()) -> bool # 删除指定列
removeColumns(column:int, count:int, parent:QModelIndex=QModelIndex()) -> bool # 删除多列
revert() -> None # 恢复数据模型到初始状态
submit() -> bool # 提交数据模型中的所有更改
rowCount(parent:QModelIndex=QModelIndex()) -> int # 获取行的数量
columnCount(parent:QModelIndex=QModelIndex()) -> int # 获取列的数量
setData(index:QModelIndex, value:Any, role:Qt.ItemDataRole=Qt.EditRole) -> bool # 设置指定索引的数据项的角色值
data(index:QModelIndex, role:Qt.ItemDataRole=Qt.EditRole) -> Any # 获取指定索引的数据项的角色值
sort(column:int, order:Qt.SortOrder=Qt.AscendingOrder) -> None # 直接对结果进行排序
index(row:int, column:int, parent:QModelIndex=QModelIndex()) -> QModelIndex # 获取子索引
parent(child:QModelIndex)-> QModelIndex # 获取子索引的父索引
sibling(row:int, column:int, idx:QModelIndex) -> QModelIndex # 获取指定索引的兄弟索引
clearItemData(index:QModelIndex) -> bool # 根据索引清除数据项中的数据
setEditStrategy(strategy:QSqlTableModel.EditStrategy) -> None # 设置修改提交模式
database() -> QSqlDatabase # 获取关联的数据库连接
deleteRowFromTable(row:int) -> bool # 直接删除数据表中指定的行(记录)
fieldIndex(fieldName:str) -> int # 获取字段的索引,-1表示没有对应的字段
insertRecord(row:int, record:QSqlRecord) -> bool # 在指定行位置插入行
insertRowIntoTable(values:QSqlDatabase) -> bool # 直接在数据表中插入行
isDirty() -> bool # 获取模型中是否有脏数据
isDirty(index:QModelIndex) -> bool # 根据索引获取数据数据是否时脏数据
primaryValues(row:int) -> QSqlRecord # 返回指定行的含有表格字段的记录
revertRow(row:int) -> None # 复原指定行的更改
setRecord(row:int, record:QSqlRecord) -> bool # 用指定的记录填充指定的行
setTable(tableName:str) -> None # 设置数据表中的字段名称
setFilter(filter:str) -> None # 设置SELECT查询语句中WHERE子句部分
filter() -> str # 获取SELECT查询语句中WHERE子句部分
setSort(column:int, order:Qt.SortOrder) -> None # 设置SELECT查询语句中ORDER BY子句部分
orderByClause() -> str # 获取SELECT查询语句中ORDER BY子句部分
select() -> bool # 执行SELECT查询语句
selectRow(row:int) -> bool # 用数据库库中行更新模型中的数据
selectStatement() -> bool # 获取"SELECT ... WHERE ... ORDER BY..."
# 槽函数
revertAll() -> None # 复原所有未提交的更改
submitAll() -> bool # 提交所有更改
数据库表格模型 QSqlTableModel 的常用信号如下所示:
# 在调用deleteRowFromTable(row:int)方法删除指定行之前发色和信号
beforeDelete(row:int)
# 在调用insertRowIntoTable(values:QSqlRecord)方法插入记录之前发射信号,可以在插入之前修改记录
beforeInsert(record:QSqlRecord)
# 在调用updateRowInTable(row:int, values:QSqlRecord)方法更新指定行之前发射信号
beforeUpdate(row:int, record:QSqlRecord)
# 在调用insertRows(row:int, column:int)方法对新插入的行进行初始化时发射信号
primeInsert(row:int, record:QSqlRecord)
在视图控件中对数据库表格模型中的数据进行修改提交时,有 3 种模式可供选择:立即模式、行模式 和 手动模式。我们可以用 setEditStrategy(strategy:QSqlTableModel.EditStrategy) 方法 设置修改提交模式,参数 strategy 是 QSqlTableModel.EditStrategy 类型的枚举值,可以取的值如下:
# 0,对模型的修改会立即更新到当前的模式
QSqlTableModel.EditStrategy.OnFieldChange
# 1,修改完一行,在选择其它行后把修改应用到数据库中
QSqlTableModel.EditStrategy.OnRowChange
# 2,修改后不会立即更新到数据库中,而是保存在缓存中
# 调用submitALL()方法才会把修改应用到数据库中
# 调用revertAll()方法可撤销修改并恢复原状
QSqlTableModel.EditStrategy.OnManualSubmit
对数据库的查询可以先用 setTable(tableName:str) 方法、setFilter(filter:str) 方法和 setSort(column:int, order:Qt.SortOrder) 方法分别 设置需要查询的数据表、SQL 的 WHERE 从句 和 SORT BY 从句,最后调用 select() 方法,也可以直接用 setQuery(query:QSqlQuery) 方法进行查询。
用 setSort(column:int,order:Qt.SortOrder) 方法可以 将数据模型中的数据按照某列的值进行排序,参数 order 是 Qt.SortOrder 类型的枚举值,可以取值如下:
Qt.SortOrder.AscendingOrder # 升序
Qt.SortOrder.DescendingOrder # 降序
【2】、QSqlField 字段
记录 QSqlRecord 表示 数据表中的一行数据,一行数据中每个字段有不同的值,可用 QSqlTableModel 的 record(row:int) 方法获取 QSqlRecord 对象,以获取数据表中的一行数据。
用 QSqlRecord 创建记录实例对象的方法如下所示:
QSqlRecord()
QSqlRecord(other:QSqlRecord)
QSqlRecord 常用的方法如下所示:
append(field:QSqlField) -> None # 在末尾添加字段
insert(pos:int, field:QSqlField) -> None # 在指定的位置插入字段
remove(pos:int) -> None # 删除指定位置的字段
replace(pos:int, field:QSqlField) -> None # 替换指定位置的字段
setValue(name:str, val:Any) -> None # 根据字段名称设置字段的值
setValue(i:int, val:Any) -> None # 根据字段索引设置字段的值
value(name:str) -> Any # 根据字段名称获取字段的值
value(i:int) -> Any # 根据字段索引获取字段的值
setNull(name:str) -> None # 根据字段名称设置字段为NULL
setNull(i:int) -> None # 根据字段索引设置字段为NULL
isNull(name:str) -> bool # 根据字段名称判断字段是否为NULL
isNull(i:int) -> bool # 根据字段索引判断字段是否为NULL
clear() -> None # 删除所有的字段
isEmpty() -> bool # 获取是否含有字段
clearValues() -> None # 删除所有的字段值
contains(name:str) -> bool # 获取是否包含指定的字段
count() -> int # 获取字段的数量
field(name:str) -> QSqlField # 根据字段名称获取字段
field(i:int) -> QSqlField # 根据字段索引获取字段
fieldName(i:int) -> str # 获取字段的名称
indexOf(name:str) -> int # 获取字段的索引
keyValues(keyFields:QSqlRecord) -> QSqlRecord # 获取与给定的记录具有相同字段名称的记录
setGenerated(name:str, generated:bool) -> None # 根据索引设置字段值是否已经生成
setGenerated(i:int, generated:bool) -> None # 根据索引设置字段值是否已经生成
isGenerated(name:str) -> bool # 根据索引获取字段是否已经生成
isGenerated(i:int) -> bool # 根据名称获取字段是否已经生成
用 append(field:QSqlField) 方法可以 在末尾添加字段。用 insert(pos:int,field:QSqlField) 方法 插入字段;用 remove(pos:int) 方法 移除字段。用 setValue(i:int,val:Any) 方法或 setValue(name:str,val:Any) 方法 根据字段索引或字段名称设置字段的值。用 setGenerated(i:int,generated:bool) 方法或 setGenerated(name:str,generated:bool) 方法 根据索引或名称设置字段值是否已经生成,只有已经生成的字段值才能用 QSqlTableModel 的 updateRowInTable(row:int, values:QSqlRecord) 方法 更新到数据库 中,默认值是 True。
字段 QSqlField 是 数据表中的列,一个记录由多个字段构成。字段的属性有字段名、字段类型和字段值等。用 QSqlRecord 的 field(i:int) 方法或 field(name:str) 方法可获得 QSqlField。
用 QSqlField 创建字段的方法如下所示:
QSqlField(fieldName:str="", type:QMetaType:Default(QMetaType), tableName:str="")
QSqlField(other:QSqlField)
其中 type 用于 定义字段的类型,可取 PySide6 中常见的类。
QSqlField 类的常用方法如下:
setName(name:str) -> None # 设置字段的名称
name() -> str # 获取字段的名称
setValue(value:Any) -> None # 获取字段的值,只读时不能设置值
value() -> Any # 获取字段的值,只读时不能设置值
setDefaultValue(value:Any) -> None # 设置字段的默认值
defaultValue() -> Any # 获取字段的默认值
setMetaType(type:QMetaType) -> None # 设置字段的类型
metaType() -> QMetaType # 获取存储在数据库中的类型
setReadOnly(readOnly:bool) -> None # 设置是否只读,只读时不能修改字段的值
isReadOnly() -> bool # 获取是否只读
setRequired(required:bool) -> None # 设置字段的值是必须要输入还是可见的
setRequiredStatus(status:QSqlFieldRequiredStatus) -> None # 设置可选状态
setGenerated(gen:bool) -> None # 设置字段的生成状态
isGenerated() -> bool # 获取字段的生成状态
setLength(fieldLength:int) -> None # 设置字段的长度
length() -> int # 获取字段的长度
setPrecision(precision:int) -> None # 设置浮点数的精度
precision() -> int # 获取浮点数的精度
setTableName(tableName:str) -> None # 设置数据表名称
tableName() -> str # 获取数据表名称
setAutoValue(autoVal:bool) -> None # 将字段的值标记是由数据库自动生成的
isAutoValue() -> bool # 获取字段的值是否是由数据库自动生成的
isValid() -> bool # 获取字段的类型是否有效
clear() -> None # 清除字段的值并设置成None
isNull() -> bool # 获取字段的值是否为None
用 setName(name:str) 方法 设置字段名称。用 setValue(value:Any) 方法 设置字段的值。用 setDefaultValue(value:Any) 方法 设置字段的默认值。用 setReadOnly(readOnly:bool) 方法 设置是否是只读,在只读状态不能更改字段的值。用 setRequired(required:bool) 方法或 setRequiredStatus(status:QSqlField.RequiredStatus) 方法 设置字段的值是必须要输入的还是可选的,其中参数 status 是 QSqlField.RequiredStatus 类型的枚举值,可以取值如下:
QSqlField.RequiredStatus.Required
QSqlField.RequiredStatus.Optional
QSqlField.RequiredStatus.Unknown
新建一个 dialog.py 文件 ,用来存放自定义的对话框控件。
from PySide6.QtWidgets import QWidget, QDialog
from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QFormLayout
from PySide6.QtWidgets import QPushButton, QLineEdit, QSpinBox
class MyDialog(QDialog):
def __init__(self, parent:QWidget | None = None):
super().__init__(parent) # 1.调用父类Qwidget类的__init__()方法
self.setupUi() # 3.初始化对话框页面
def setupUi(self):
veritical_layout = QVBoxLayout(self) # 2.创建一个垂直布局
form_layout = QFormLayout() # 3.创建一个表单布局,并添加到垂直布局中
veritical_layout.addLayout(form_layout)
self.name_lineEdit = QLineEdit() # 4.创建一个文本输入框,并添加到表单布局中
form_layout.addRow("姓名:", self.name_lineEdit)
self.age_spinBox = QSpinBox() # 6.创建一个数字输入框,并添加到表单布局中
self.age_spinBox.setRange(0, 300)
form_layout.addRow("年龄:", self.age_spinBox)
horizontal = QHBoxLayout() # 7.创建一个水平布局,并添加到垂直布局中
veritical_layout.addLayout(horizontal)
self.ok_button = QPushButton("确定") # 8.创建一个按钮,并添加到水平布局中
horizontal.addWidget(self.ok_button)
self.cancel_button = QPushButton("取消")
horizontal.addWidget(self.cancel_button)
self.clear_button = QPushButton("清空")
horizontal.addWidget(self.clear_button)
修改 ui.py 文件的内容。
from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QTableView, QPushButton, QLineEdit
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout
class MyUi:
def setupUi(self, window:QWidget):
window.resize(800, 600) # 1.设置窗口对象大小
vertical_layout = QVBoxLayout(window) # 2.创建一个垂直布局
self.tableView = QTableView(window) # 3.创建一个表格控件,并添加到布局中
vertical_layout.addWidget(self.tableView)
self.tableView.setSortingEnabled(True) # 4.设置单击头部时是否可以排序
self.tableView.setAlternatingRowColors(True) # 5.设置表格颜色交错显示
horizontal_layout = QHBoxLayout() # 6.创建水平布局,并添加到垂直布局
vertical_layout.addLayout(horizontal_layout)
self.quert_button = QPushButton("查询") # 7.创建查询按钮,并添加到水平布局中
horizontal_layout.addWidget(self.quert_button)
self.query_lineEidt = QLineEdit()
self.query_lineEidt.setPlaceholderText("请输入查询条件")
horizontal_layout.addWidget(self.query_lineEidt)
self.add_button = QPushButton("添加")
horizontal_layout.addWidget(self.add_button)
self.delete_button = QPushButton("删除")
horizontal_layout.addWidget(self.delete_button)
修改 widget.py 文件的内容。
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtSql import QSqlDatabase, QSqlRecord, QSqlTableModel
from ui import MyUi
from dialog import MyDialog
class MyWidget(QWidget):
def __init__(self):
super().__init__() # 1.调用父类Qwidget类的__init__()方法
self.__ui = MyUi()
self.__ui.setupUi(self) # 2.初始化页面
self.dialog = MyDialog(self) # 3.初始化对话框
self.use_database() # 4.使用数据库
self.__ui.tableView.setModel(self.sqlTableModel) # 5.设置表格控件的模型
self.__ui.quert_button.clicked.connect(self.query_button_clicked)
self.__ui.add_button.clicked.connect(self.add_button_clicked)
self.__ui.delete_button.clicked.connect(self.delete_button_clicked)
self.dialog.ok_button.clicked.connect(self.dialog_ok_button_clicked)
self.dialog.cancel_button.clicked.connect(self.dialog_cancel_button_clicked)
self.dialog.clear_button.clicked.connect(self.dialog_clear_button_clicked)
def use_database(self):
persons = [{"name": "Sakura", "age": 10}, {"name": "Mikoto", "age": 14}, {"name": "Shana", "age": 10}]
db_name = "./test.db"
db = QSqlDatabase.addDatabase("QSQLITE") # 1.添加数据库连接
db.setDatabaseName(db_name) # 2.设置连接数据库的名称
self.sqlTableModel = QSqlTableModel(self, db) # 3.创建数据库表格模型
if db.open(): # 4.打开数据库
# 5.执行SQL语句,创建表
self.sqlTableModel.setQuery("CREATE TABLE IF NOT EXISTS person(id INTEGER PRIMARY KEY AUTOINCREMENT, name varchar(20), age int)")
self.sqlTableModel.setTable("person") # 6.设置数据表中字段的名称
if db.transaction(): # 7.开启事务
for i, person in enumerate(persons):
record = QSqlRecord(self.sqlTableModel.record()) # 8.创建QSqlRecord对象,表示数据表中的一行数据
for key, value in person.items():
record.setValue(key, value) # 9.根据字段名称设置字段的值
self.sqlTableModel.insertRecord(i, record) # 10.在指定位置插入行
db.commit() # 11.提交事务
self.sqlTableModel.select() # 12.查询数据
def query_button_clicked(self):
query_condition = self.__ui.query_lineEidt.text() # 1.获取查询条件
if query_condition:
self.sqlTableModel.setFilter(query_condition) # 2.设置查询条件
else:
self.sqlTableModel.setFilter("")
self.sqlTableModel.select() # 3.执行查询
def add_button_clicked(self):
self.dialog.open() # 1.打开对话框
def delete_button_clicked(self):
select_row_index_list = self.__ui.tableView.selectedIndexes() # 1.获取选中的多行的索引
select_rows = [index.row() for index in select_row_index_list] # 2.获取行索引
select_rows.sort(key=int, reverse=True) # 3.对行索引进行降序排序
for row in select_rows:
self.sqlTableModel.removeRow(row) # 4.删除行
self.sqlTableModel.submit() # 5.提交修改
self.sqlTableModel.select() # 6.重新查询,刷新表格
def dialog_ok_button_clicked(self):
# 1.获取输入控件的数据
name = self.dialog.name_lineEdit.text()
age = self.dialog.age_spinBox.value()
if name:
self.dialog_clear_button_clicked() # 2.清空对话框的输入数据
count = self.sqlTableModel.rowCount() # 3.获取数据表的行数
self.sqlTableModel.insertRow(count) # 4.在数据库表格模型中插入一行数据
self.sqlTableModel.setData(self.sqlTableModel.index(count, 0), name) # 5.设置数据库表格模型中指定行的数据
self.sqlTableModel.setData(self.sqlTableModel.index(count, 1), age)
self.sqlTableModel.submit() # 6.提交数据库表格模型中的数据
self.dialog.accept() # 7.隐藏对话框
def dialog_cancel_button_clicked(self):
self.dialog_clear_button_clicked() # 1.清空对话框的输入数据
self.dialog.reject() # 2.隐藏对话框
def dialog_clear_button_clicked(self):
self.dialog.name_lineEdit.clear()
self.dialog.age_spinBox.setValue(0)
if __name__ == "__main__":
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
window = MyWidget() # 2.创建一个窗口
window.show() # 3.展示窗口
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
这里,创建表的时候要增加一个主键字段,不要然后面删除行的时候,会把数据相同的行全部删除。
在 SQLite 中,创建带自增主键的表时字段的格式为
主键字段名 INTEGER PRIMARY KEY AUTOINICREMENT(不区分大小写),否则会创建表失败或者主键为空,插入数据时没有自增。
4.3、关系表格模型
数据库关系表格模型 QSqlRelationalTableModel 继承自 QSqlTableModel,除具有 QSqlTableModel 的方法外,它还提供了外键功能。关系表格模型 QSqlRelationalTableModel 实现了 SQL 的 SELECT 命令中的 INNER JOIN 和 LEFT JOIN 功能。
用 QSqlRelationalTableModel 类创建关系表格模型的方法如下所示。
QSqlRelationalTableModel(parent:QObject=None, db:QSqlDatabase=Default(QSqlDatabase))
用 QSqlRelationalTableModel 的 setRelation(column:int,relation:QSqlRelation) 方法定义 QSqlRelationalTableModel 当前数据表格(如 table1)的外键和映射关系,其中参数 column 是 table1 的字段编号,用于确定 table1 中当作外键的字段 field1,relation 参数是 QSqlRelation 的实例对象,用于确定另外一个数据表格(如 table2)和对应的字段 field2。
QSqlRelation 实例对象的创建方法是 QSqlRelation(tableName:str,indexCol:str,displayCol:str),其中 tableName 用于 确定第 2 个数据表格 table2,indexCol 用于 指定 table2 的字段 field2,displayCol 是 table2 中用于显示在 table1 的 field1 位置处的字段 field3,用 field3 的值显示在 field1 位置处,field1 的值不显示。
另外用 QSqlRelationalTableModel 的 setJoinMode(joinMode:QSqlRelationalTableModel.JoinMode) 方法可 设置两个数据表格的数据映射模式,即使 table1 和 table2 没有匹配的数据,也列出 table1 中的数据。参数 joinMode 是 QSqlRelationalTableModel.JoinMode 类型的枚举值,可以取值如下:
QSqlRelationalTableModel.InnerJoin # 内连接,值为0,只列出table1和table2中匹配的数据
QSqlRelationalTableModel.LeftJoin # 左连接,值为1

浙公网安备 33010602011771号