PyQt6/PySide6 的 SQL 数据库操作(QtSql)

一、核心组件架构

1.1 QtSql模块构成

  • QSqlDatabase:数据库连接管理(支持连接池)
  • QSqlQuery:SQL语句执行与结果遍历
  • QSqlTableModel:可编辑的表格数据模型
  • QSqlQueryModel:只读查询结果模型
  • QSqlRelationalTableModel:支持外键关系模型

1.2 数据库驱动支持

python 复制代码
print(QSqlDatabase.drivers())  # 输出可用驱动列表
# 典型输出:['QSQLITE', 'QMYSQL', 'QPSQL', 'QODBC']

二、数据库连接实战

2.1 SQLite连接示例

python 复制代码
def create_connection():
    db = QSqlDatabase.addDatabase('QSQLITE')
    db.setDatabaseName('contacts.db')
  
    if not db.open():
        QMessageBox.critical(None, "数据库错误",
            f"连接失败:{db.lastError().text()}")
        return False
  
    # 验证表存在性
    required_tables = {'contacts'}
    existing_tables = db.tables(QSql.Tables)
    if not required_tables.issubset(existing_tables):
        init_database(db)
  
    return True

def init_database(conn):
    query = QSqlQuery()
    query.exec("""
        CREATE TABLE contacts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            phone TEXT UNIQUE,
            email TEXT,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )
    """)
    # 创建索引
    query.exec("CREATE INDEX idx_contacts_name ON contacts(name)")

2.2 MySQL连接配置

python 复制代码
db = QSqlDatabase.addDatabase('QMYSQL')
db.setHostName('localhost')
db.setPort(3306)
db.setDatabaseName('mydb')
db.setUserName('root')
db.setPassword('secret')
db.setConnectOptions("MYSQL_OPT_RECONNECT=1;")

三、CRUD操作进阶

3.1 参数化查询(防SQL注入)

python 复制代码
# 插入数据
query = QSqlQuery()
query.prepare("""
    INSERT INTO contacts 
    (name, phone, email)
    VALUES (?, ?, ?)
""")
query.addBindValue("张三")
query.addBindValue("13800138000")
query.addBindValue("[email protected]")
query.exec()

# 批量插入
names = [("李四", "13912345678"), ("王五", "13687654321")]
query.prepare("INSERT INTO contacts (name, phone) VALUES (?, ?)")
for name, phone in names:
    query.addBindValue(name)
    query.addBindValue(phone)
    query.exec()

3.2 复杂查询与结果处理

python 复制代码
query = QSqlQuery()
query.exec("""
    SELECT id, name, phone, 
           strftime('%Y-%m-%d', created_at) AS create_date
    FROM contacts
    WHERE name LIKE ?
    ORDER BY created_at DESC
    LIMIT 10
""")
query.addBindValue("%张%")

while query.next():
    record = query.record()
    print(f"ID: {query.value(0)}, Name: {query.value('name')}")
    print(f"Phone: {record.value(2)}, Date: {query.value(3)}")

四、模型-视图编程

4.1 QSqlTableModel实时同步

python 复制代码
class ContactManager(QWidget):
    def __init__(self):
        self.model = QSqlTableModel()
        self.model.setTable("contacts")
        self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
        self.model.select()

        self.view = QTableView()
        self.view.setModel(self.model)
        self.view.setColumnHidden(0, True)  # 隐藏ID列
        self.view.doubleClicked.connect(self.edit_contact)

    def add_contact(self):
        row = self.model.rowCount()
        self.model.insertRow(row)
        self.model.setData(self.model.index(row, 1), "新联系人")
        self.model.submitAll()

    def delete_contact(self):
        index = self.view.currentIndex()
        if index.isValid():
            self.model.removeRow(index.row())
            self.model.submitAll()

4.2 自定义查询模型

python 复制代码
class FilterProxyModel(QSortFilterProxyModel):
    def __init__(self, pattern, parent=None):
        super().__init__(parent)
        self.pattern = QRegularExpression(pattern, 
            QRegularExpression.CaseInsensitiveOption)

    def filterAcceptsRow(self, row, parent):
        model = self.sourceModel()
        index = model.index(row, 1)  # 过滤name列
        return self.pattern.match(model.data(index)).hasMatch()

# 使用示例
custom_model = QSqlQueryModel()
custom_model.setQuery("SELECT * FROM contacts")
proxy = FilterProxyModel("张")
proxy.setSourceModel(custom_model)
view.setModel(proxy)

五、事务与性能优化

5.1 事务处理模板

python 复制代码
db = QSqlDatabase.database()
db.transaction()

try:
    # 执行多个操作
    insert_query.exec()
    update_query.exec()
    db.commit()
except:
    db.rollback()
    raise

5.2 连接池配置

python 复制代码
from PySide6.QtCore import QSettings

def setup_connection_pool():
    settings = QSettings("config.ini", QSettings.IniFormat)
  
    QSqlDatabase.setConnectOptions(
        f"QSQLITE_MAX_CONNECTIONS=20;"
        f"QSQLITE_BUSY_TIMEOUT=5000;"
    )
  
    for i in range(5):  # 初始化5个连接
        conn = QSqlDatabase.addDatabase('QSQLITE', f"conn_{i}")
        conn.setDatabaseName(settings.value("Database/Path"))

六、实战案例:通讯录管理系统

功能清单:

  1. 联系人增删改查
  2. 拼音快速检索
  3. 导出CSV功能
  4. 数据库备份/恢复
  5. 批量导入Excel

关键代码实现:

python 复制代码
class ContactBook(QMainWindow):
    def export_csv(self):
        with open('contacts.csv', 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            writer.writerow(['姓名', '电话', '邮箱'])
          
            query = QSqlQuery("SELECT name, phone, email FROM contacts")
            while query.next():
                writer.writerow([
                    query.value(0),
                    query.value(1),
                    query.value(2)
                ])

七、常见问题解决方案

7.1 中文乱码处理

python 复制代码
# MySQL连接字符串添加charset参数
db.setConnectOptions("MYSQL_OPT_SET_CHARSET_NAME=UTF8MB4;")

# SQLite设置编码
query.exec("PRAGMA encoding = 'UTF-8'")

7.2 数据库迁移方案

使用Alembic进行版本控制:

python 复制代码
# 版本迁移脚本示例
from alembic import op
import sqlalchemy as sa

def upgrade():
    op.create_table('contacts',
        sa.Column('id', sa.Integer, primary_key=True),
        sa.Column('name', sa.String(50), nullable=False)
    )

def downgrade():
    op.drop_table('contacts')

附录:PyQt6与PySide6差异对照表

功能点 PyQt6 PySide6
模块导入 from PyQt6 import QtSql from PySide6 import QtSql
信号语法 pyqtSignal Signal
数据库错误处理 QSqlError QSqlError (相同实现)

通过本指南,开发者可以快速掌握PyQt6/PySide6数据库编程的核心要点,并构建出高性能、易维护的数据库应用程序。

相关推荐
hxung26 分钟前
如何保证mysql和redis的数据一致性
java·数据库·redis·mysql
涛思数据(TDengine)28 分钟前
时序数据库 TDengine Cloud 私有连接实战指南:4步实现数据安全传输与成本优化
数据库·时序数据库·tdengine
翻滚吧键盘33 分钟前
debian12 mysql完全卸载
数据库·mysql
安得权38 分钟前
Ubunut18.04 离线安装MySQL 5.7.35
数据库·mysql·adb
镜舟科技1 小时前
什么是模型上下文协议(MCP)?
数据库·api·mcp
Databend1 小时前
理解 Calvin 的架构设计与工作原理
数据库
白鸽(二般)1 小时前
mysql
数据库·mysql
AuLuo-1 小时前
Mysql专题篇章
数据库
Linging_242 小时前
Debezium嵌入式连接postgresql封装服务
数据库·postgresql
TDengine (老段)2 小时前
TDengine 窗口预聚集
大数据·数据库·物联网·时序数据库·tdengine·iotdb