PyQt5学习(13):数据库的处理QtSql,包括介绍一些主流数据库介绍

目录

1,数据库的种类

1.1,关系型数据库

1.2,非关系型数据库

2,PyQ5使用Qtsql连接数据库

2,1,连接数据库

2.2,使用模型和视图

2.2.1 ,QsqlQuerymodel

2.2.2,QSqlTableModel

2.2.3,综合案例

3,QsqlError的使用获得数据库异常



1,数据库的种类

最常用的数据库模型主要有两种:关系型数据库和非关系型数据库

1.1,关系型数据库

主要有:Sqlserver, mysql ,oracle  ,sqlite,mariaDB,postgreSQL

关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织:

优点:
1、易于维护:都是使用表结构,格式一致;
2、使用方便:SQL语言通用,可用于复杂查询;
3、复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。
缺点:
1、读写性能比较差,尤其是海量数据的高效率读写;
2、固定的表结构,灵活度稍欠;
3、高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。


1.2,非关系型数据库

主要有:mongoDb,redis,couchDb,cassandra,neo4j,hbase

非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。
优点:
1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
2、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
3、高扩展性;
4、成本低:nosql数据库部署简单,基本都是开源软件。

非关系型数据库的分类和比较:
1、文档型
2、key-value型
3、列式数据库
4、图形数据库

 

2,PyQ5使用Qtsql连接数据库

2,1,连接数据库

Qt中内置了好几个数据库的驱动程序,也就是说我们可以直接在PyQt中对这些数据库进行操作。这些内置的数据库包括:

  • IBM DB2,驱动名为QDB2;
  • Borland InterBase,驱动名为QIBASE;
  • MySQL,驱动名为QMYSQL;
  • Oracle,驱动名为QOCI;
  • Microsoft SQL Server和其他符合ODBC的数据库,驱动名为QODBC;
  • PostgreSQL,驱动名为QPSQL;
  • SQLite2,驱动名为QSQLITE2;
  • SQLite3,驱动名为QSQLITE;
db = QtSql.QSqlDatabase.addDatabase('驱动名')
db.setHostName('主机名')
db.setDatabaseName('数据库名')
db.setUserName('用户名')
db.setPassword('密码')
db.open()

坑:发现PyQt5中用QMYSQL驱动会出现不能加载错误,找了许多没有解决,不过可以参考https://blog.csdn.net/qq_38198744/article/details/80261695https://blog.csdn.net/La_vie_est_belle/article/details/83038159,改用SQlite驱动

代码如下:参考:https://blog.csdn.net/La_vie_est_belle/article/details/82894758

SQL语句操作:http://www.w3school.com.cn/sql/sql_update.asp

import sys
from PyQt5.QtWidgets import QMainWindow,QWidget,QApplication,QMessageBox,QTableView,QHBoxLayout
from PyQt5.QtSql import QSqlDatabase,QSqlQuery,QSqlTableModel

class sqlDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
    def initUI(self):
        self.setWindowTitle('demo')
        self.resize(600,600)
        self.layout=QHBoxLayout()
        self.setLayout(self.layout)

        #------数据库操作------
        #1连接数据库
        self.connect_db()
        #操作数据库
        self.opreate_db()
        #关闭数据库
        self.close_db()
       


    def connect_db(self):
        self.db=QSqlDatabase.addDatabase('QSQLITE')
        self.db.setDatabaseName('./test.db')
        # d=self.db.open()
        # print(d)       #显示True 表示连接成功
        if not self.db.open():
            QMessageBox.critical(self,'database error',self.db.lastError.text(),QMessageBox.Yes|QMessageBox.No,QMessageBox.Yes)

    
    def opreate_db(self):
        query=QSqlQuery()
        # #【建立表】
        # query.exec_("create table test(ID int primary key,name varchar(20),url varchar(100))")
        # #【插入数据】
        # query.exec_("insert into test values(1000,'tom','http://www.xx.com')")
        # query.exec_("insert into test values(1001,'marray','http://www.xx.com')")
        # query.exec_("insert into test values(1002,'jack','http://www.xx.com')")
        # #【查询数据】
        query.exec_("select * from test ")
        #备注:在执行exec_()查询时指针会放在记录集中第一个记录之上所以需要调用next()
        while query.next():
            id=query.value(0)
            name=query.value(1)
            url=query.value(2)
            print('id:'+str(id)+'----name:'+name+'---url:'+url)
        # #【删除数据】
        value=query.exec_("delete from test where ID=1001")
        # print(value) #为true表示删除成功
        if value:
            QMessageBox.information(self,'delet data','删除成功',QMessageBox.Ok|QMessageBox.No,QMessageBox.Ok)
        else:
             QMessageBox.information(self,'delet data','删除失败',QMessageBox.Ok|QMessageBox.No,QMessageBox.Ok)
        
        # #【修改数据】
        query.exec_("update test set name='bbb' where id=1002")
        
  

    #关闭数据库
    def close_db(self):
        self.db.close()


if __name__=='__main__':
    app=QApplication(sys.argv)
    demo=sqlDemo()
    demo.show()
    sys.exit(app.exec_())

 

2.2,使用模型和视图

2.2.1 ,QsqlQuerymodel

QSqlQueryModel模型只提供只读操作,如果同时获取写入操作的话,就需要继承

pyqt5提供了相比Qsqlquery之下更高级的接口:Qsqlquerymodel,QSqlTableModel和QsqlRelationalTableModel。

这些模型类让我们不必生硬的使用原始的sql语句,而且提供了许多便捷的方法,对应的视图我们通常会选择表格视图QTableView(因为数据库中是用表来存储数据的)主要来讲下QSqlQueryModel和QSqlTableModel这两种模型,(QSqlRelationalTableModel用于有外键的表,大部分用法和QSqlTableModel类似,我们这里就简单讲一下如何建立表之间的联系并显示数据)。

import sys
from PyQt5.QtWidgets import QMainWindow,QWidget,QApplication,QTableView,QHBoxLayout
from PyQt5.QtSql import QSqlDatabase,QSqlQuery,QSqlTableModel,QSqlQueryModel
from PyQt5.QtCore import Qt
class sqlDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
    def initUI(self):
        self.setWindowTitle('demo')
        self.resize(600,600)
        self.layout=QHBoxLayout()
        self.setLayout(self.layout)

        #------数据库操作------
        #1连接数据库
        self.connect_db()
        #操作数据库
        # self.opreate_db()
        #在Pyqt界面显示数据库内容 (相当于查询)
        self.show_db()



    def connect_db(self):
        self.db=QSqlDatabase.addDatabase('QSQLITE')
        self.db.setDatabaseName('./test.db')
        d=self.db.open()
        # print(d)       #显示True 表示连接成功
    
    def opreate_db(self):
        query=QSqlQuery()
        #建立表
        query.exec_("create table test(ID int primary key,name varchar(20),url varchar(100))")
        #插入数据
        query.exec_("insert into test values(1000,'tom','http://www.xx.com')")
        query.exec_("insert into test values(1001,'marray','http://www.xx.com')")
        query.exec_("insert into test values(1002,'jack','http://www.xx.com')")
   
    def show_db(self):
        tableview=QTableView()
        self.layout.addWidget(tableview) #用来在界面显示查询的数据库

        #【Qsqlquerymodel】 相对于原生的Qsqlquery更方便
        querymodel=QSqlQueryModel()
        querymodel.setQuery("select * from test")
        querymodel.setHeaderData(0,Qt.Horizontal,'id')
        querymodel.setHeaderData(1,Qt.Horizontal,'姓名')
        querymodel.setHeaderData(2,Qt.Horizontal,'地址')

        tableview.setModel(querymodel)

        #在后台显示 第一种方法
        for i in range(querymodel.rowCount()):
            id=querymodel.record(i).value('id')
            name=querymodel.record(i).value(1)
            print(str(id)+name)

        for i in range(querymodel.rowCount()):
            id=querymodel.data(querymodel.index(i,0))
            print('显示出所有的id'+str(id))



    #关闭数据库
    def close_db(self):
        self.db.close()


if __name__=='__main__':
    app=QApplication(sys.argv)
    demo=sqlDemo()
    demo.show()
    sys.exit(app.exec_())

上面代码的解释

1. 实例化一个QSqlQueryModel模型,并调用setQuery()方法来执行一个SQL查询语句。setHeaderData()方法用来设置表格标题,若不使用该方法的话,程序则会默认使用数据表中的字段名作为标题;

2. 调用setModel()方法来设置视图所使用的模型(注意这里程序直接继承QTableView,所以直接使用self);

3. 在使用QSqlQuery类执行查询语句后,我们是通过next()方法循环遍历结果集,并使用value()方法来获取数据。其实我们还可以使用record()方法,传入相应的行索引,我们就可以获取到相对应的行记录。

首先我们调用rowCount()方法获取行总数,接下来进行循环,将索引值传入record()中并调用value()方法传入字段名(也可以传入索引)获取到id和name。

4. 除了第3点中涉及的方法,我们还可以调用data()来获取数据,需要传入的是ModelIndex值。为获取该值,我们调用index()传入行列索引值即可

2.2.2,QSqlTableModel

QSqlTableModel可以支持读写,修改操作

import sys
from PyQt5.QtWidgets import QMainWindow,QWidget,QPushButton,QApplication,QTableView,QHBoxLayout
from PyQt5.QtSql import QSqlDatabase,QSqlQuery,QSqlTableModel,QSqlQueryModel
from PyQt5.QtCore import Qt
class sqlDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
    def initUI(self):
        self.setWindowTitle('demo')
        self.resize(600,600)
        self.layout=QHBoxLayout()
        self.setLayout(self.layout)

        #------数据库操作------
        #1连接数据库
        self.connect_db()
        #操作数据库
        # self.opreate_db()
        #在Pyqt界面显示数据库内容 (相当于查询)
        self.show_db()



    def connect_db(self):
        self.db=QSqlDatabase.addDatabase('QSQLITE')
        self.db.setDatabaseName('./test.db')
        d=self.db.open()
        # print(d)       #显示True 表示连接成功
    
    def opreate_db(self):
        query=QSqlQuery()
        #建立表
        query.exec_("create table test(ID int primary key,name varchar(20),url varchar(100))")
        #插入数据
        query.exec_("insert into test values(1000,'tom','http://www.xx.com')")
        query.exec_("insert into test values(1001,'marray','http://www.xx.com')")
        query.exec_("insert into test values(1002,'jack','http://www.xx.com')")
   
    def show_db(self):
        tableview=QTableView()
        self.layout.addWidget(tableview) #用来在界面显示查询的数据库

        #【Qsqltablemodel】 
        tablemodel=QSqlTableModel()
        tablemodel.setTable('test') #确定要读取的数据库表是哪个
        #设置在界面更改时,是否同步更新到数据库
        tablemodel.setEditStrategy(QSqlTableModel.OnFieldChange)
        tablemodel.setHeaderData(0,Qt.Vertical,'id')
        tablemodel.setHeaderData(1,Qt.Vertical,'姓名')
        tablemodel.setHeaderData(2,Qt.Vertical,'url')
        tablemodel.select()

        # 插入一行
        i=tablemodel.rowCount()
        tablemodel.insertRow(i)
        tablemodel.setData(tablemodel.index(i,0),1004)
        tablemodel.setData(tablemodel.index(i,1),'dj')
        tablemodel.setData(tablemodel.index(i,2),'www.baidu.com')
        tablemodel.submit()       #不要忘记提交

        # 过滤操作(根据条件)
        tablemodel.setFilter("name='dj'")
        tablemodel.select()

        #删除操作
        i=tablemodel.rowCount()
        tablemodel.removeRow(i)
        tablemodel.submit()

        tableview.setModel(tablemodel)
    


    #关闭数据库
    def close_db(self):
        self.db.close()


if __name__=='__main__':
    app=QApplication(sys.argv)
    demo=sqlDemo()
    demo.show()
    sys.exit(app.exec_())

上述代码解释:

调用setTable()来选择要进行操作的数据表

setEditStrategy()用来设置模型的编辑策略,一共三种

QSqlTableModel.OnFieldChange所有变更立即更新到数据库中
QSqlTableModel.OnRowChange当用户对某行数据操作后,点击其他行时在更新数据库
QSqlTableModel.OnManualSubmit只有在调用submitAll()或者reverAll()后才会更新数据库
  

1. 要进行插入操作的话,只需要调用insertRow()方法传入索引值来确定要插入的位置。这里我们传入0表示在第一行插入。setData()方法可以用来插入或更新值,需要两个参数,第一个是位置,第二个是插入的数据。最后调用submit()方法来提交我们对数据库所做的更改。

2.2.3,综合案例

import sys,re
from PyQt5.QtWidgets import QMainWindow,QTableView,QMessageBox,QWidget,QInputDialog,QPushButton,QHBoxLayout,QVBoxLayout,QApplication,QGroupBox
from PyQt5.QtSql import QSqlDatabase,QSqlTableModel,QSqlQuery,QSqlError
from PyQt5.QtCore import Qt

class Judege_English():
    def judge(self,context):
        context=str(context)
        result=re.findall('[a-zA-Z]',context)
        if len(context)!=len(result):
            return 0
        else:
            return 1

class DbOp(QSqlDatabase):
    def __init__(self):
        super().__init__()
        self.db=''
        self.model=''

    def database_creator(self,text):
        #创建连接数据库
        self.db=self.addDatabase('QSQLITE') #1,加载驱动
        self.db.setDatabaseName('{}.sqlite'.format(text))#2,设置数据库的名称,或访问已经存在的数据库
        return self.db.open() #打开数据库

    def table_creator(self,text):
            querry=QSqlQuery()
            querry.exec_("create table {} (id int primary key,name varchar(20),email varchar(30))".format(text))
     
    def check_table(self,table_name):
        list_table=self.db.tables() 
        if table_name in list_table:
            return 1
        else:
            return 0

    def data_show(self,table_name):
        #1,建立模型类Qsqtablemodel
        self.model=QSqlTableModel()
        #2,表与模型类建立联系
        self.model.setTable(table_name)
        #3,表的更改时否同步到数据库
        self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
        #4,设置表的标题行
        self.model.setHeaderData(0,Qt.Horizontal,'id')
        self.model.setHeaderData(1,Qt.Horizontal,'姓名')
        self.model.setHeaderData(2,Qt.Horizontal,'邮箱')
        #5,模型类查询
        self.model.select()
        #6模型类加载到Qtableview显示
        return self.model

    def data_add(self):
        row=self.model.rowCount()
        self.model.insertRow(row)
        self.model.submit()
    

    def data_del(self,row):
        self.model.removeRow(row)
        self.model.submit()

    def data_close(self):
        self.db.close()
        


        


class sqlDemo(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.dbop=DbOp()
        self.table_name=''
    def initUI(self):
        self.setWindowTitle('界面对数据库操作')
        self.resize(600,400)
        widget=QWidget()
        self.setCentralWidget(widget)
        layout=QHBoxLayout()
        widget.setLayout(layout)

#左边
        box=QGroupBox('数据库按钮')
        boxlayout=QVBoxLayout()
        box.setLayout(boxlayout)
        #添加数据库按钮
        self.b1=QPushButton('创建连接数据库')
        boxlayout.addWidget(self.b1)
        self.b1.clicked.connect(self.create_db)
        self.b11=QPushButton('创建表')
        boxlayout.addWidget(self.b11)
        self.b11.setEnabled(False)
        self.b11.clicked.connect(self.create_table)
        self.b11.clicked.connect(self.create_table)
        self.b2=QPushButton('浏览数据')
        boxlayout.addWidget(self.b2)
        self.b2.setEnabled(False)
        self.b2.clicked.connect(self.show_db)
        self.b3=QPushButton('添加一行')
        boxlayout.addWidget(self.b3)
        self.b3.setEnabled(False)
        self.b3.clicked.connect(self.add_db)
        self.b4=QPushButton('删除一行')
        boxlayout.addWidget(self.b4)
        self.b4.setEnabled(False)
        self.b4.clicked.connect(self.del_db)
        self.b5=QPushButton('退出')
        boxlayout.addWidget(self.b5)
        self.b5.setEnabled(False)
        self.b5.clicked.connect(self.exit_db)

        layout.addWidget(box)

#右边
        self.tableview=QTableView()
        layout.addWidget(self.tableview)
        layout.setStretchFactor(box,1)
        layout.setStretchFactor(self.tableview,3)

        

    def create_db(self):
        text,ok=QInputDialog.getText(self,'数据库名称','输入数据库名称(输入英文)')
        j=Judege_English()
        if ok and (text.replace(' ','')!='')and j.judge(text):
            fin=self.dbop.database_creator(text)
            if fin:
                self.b11.setEnabled(True)
                self.b2.setEnabled(True)
                
       

        else:
            QMessageBox.critical(self,'信息','输入的名字有误,请从新创建',QMessageBox.Ok|QMessageBox.No,QMessageBox.Ok)
            
    def create_table(self):
        text,ok=QInputDialog.getText(self,'数据表的名称添加','输入表名(输入英文)')
        j=Judege_English()
        if ok and (text.replace(' ','')!='')and j.judge(text):
            self.dbop.table_creator(text)        
        else:
            QMessageBox.critical(self,'信息','输入的名字有误,请从新创建',QMessageBox.Ok|QMessageBox.No,QMessageBox.Ok)

        
    def show_db(self):
        table_name,ok=QInputDialog.getText(self,'展示数据表','输入表名(输入英文)')
        #判断是否存在输入的表
        isbeing=self.dbop.check_table(table_name)
        #存在
        if isbeing:
            model=self.dbop.data_show(table_name)
            self.tableview.setModel(model)
            self.table_name=table_name
            self.b3.setEnabled(True)
            self.b4.setEnabled(True)
            self.b5.setEnabled(True)
        #不存在
        else:
            QMessageBox.critical(self,'信息','表不存在',QMessageBox.Ok|QMessageBox.No,QMessageBox.Ok)
            self.b3.setEnabled(False)
            self.b4.setEnabled(False)
            self.b5.setEnabled(False)        

    def add_db(self):
        self.dbop.data_add()
    
    def del_db(self):
        row=self.tableview.currentIndex().row()
        self.dbop.data_del(row)
    
    def exit_db(self):
        self.dbop.data_close()
       
        self.b11.setEnabled(False)
        self.b2.setEnabled(False) 
        self.b3.setEnabled(False)
        self.b4.setEnabled(False)
        self.b5.setEnabled(False) 
        

if __name__=='__main__':
    app=QApplication(sys.argv)
    demo=sqlDemo()
    demo.show()
    sys.exit(app.exec_())

3,QsqlError的使用获得数据库异常

参考博客https://blog.csdn.net/iloveqt5/article/details/16812729/

参考Qt的异常

QSqlError 类提供了SQL数据库错误信息。

一个QSqlError 对象可以提供特定数据库的错误信息,包括driverText() 和 databaseText() 消息(或是将两者结合的 text() 消息),还有nativeErrorCode() 和type().。

构造一个QSqlError对象包含驱动错误文本driverText, 特定数据库错误文本databaseText,错误类型type和错误码code。

QSqlError::QSqlError(const QSqlError &other)
创建一个other的副本。

QSqlError::~QSqlError()
销毁对象并释放所有已分配的资源。

QString QSqlError::databaseText() const
返回数据库报告的错误文本。这可能包含特定于数据库的描述;它可能是空的。

QString QSqlError::driverText() const
返回驱动报告的错误文本。这可能包含特定于数据库的描述;它可能是空的。

bool QSqlError::isValid() const
如果设置了错误返回true,否则返回false.

例子:
  QSqlQueryModel model;
  model.setQuery("select * from myTable");
  if (model.lastError().isValid())
      qDebug() << model.lastError();

QString QSqlError::nativeErrorCode() const
返回特定于数据库的错误码,如果不能确定则返回空字符串。

QString QSqlError::text() const
这是一个方便的函数,它返回databaseText()和driverText()连接成的一个字符串。

ErrorType QSqlError::type() const
返回错误类型,如果不能确定错误类型则返回-1。

bool QSqlError::operator!=(const QSqlError &other) const
对比other错误对象和这个错误对象,如果不相等返回true。

QSqlError &QSqlError::operator=(const QSqlError &other)
将other错误对象的值赋给这个错误对象。

bool QSqlError::operator==(const QSqlError &other) const
对比other错误对象和这个错误对象,如果相等返回true。
 

4,案例实现查询数据分页

import sys
import odbc
from xxx import Ui_MainWindow
from PyQt5.QtSql import QSqlDatabase,QSqlQuery,QSqlQueryModel,QSqlTableModel
from PyQt5.QtWidgets import QMainWindow,QApplication,QTableView
from PyQt5.QtCore import Qt


class Demo(QMainWindow,Ui_MainWindow):
    def __init__(self):
        super().__init__()
      
        #当前页
        self.currentpage=0
        #总页数
        self.totalpage=0
        #多少条记录
        self.totlecount=0
        #每页显示多少条
        self.everycount=5

        self.initGui()
    def initGui(self):
        self. setupUi(self)

        #连接数据库
        driver=QSqlDatabase.addDatabase('QSQLITE')
        driver.setDatabaseName('demo.db')
        driver.open()

        #tableview
        self.model=QSqlQueryModel()
        self.tableView.setModel(self.model)


        #查询函数
        self.recordQuery(0)
        
        #得到数据条数
        tablemodel=QSqlTableModel()
        tablemodel.setTable('people')  #确定读取的表时哪个
        tablemodel.select()
        self.totlecount=tablemodel.rowCount()
        self.totalpage= self.totlecount / self.everycount
        if self.totlecount % self.everycount !=0:
            self.totalpage=(self.totlecount % self.everycount)
        

        self.model.setHeaderData(0,Qt.Horizontal,'ID')
        self.model.setHeaderData(1,Qt.Horizontal, '站点名称')

        #button
        self.pushButton.clicked.connect(self.prvepage)
        self.pushButton_2.clicked.connect(self.nextpage)
        self.pushButton_3.clicked.connect(self.gopage)

        self.lineEdit_2.setText(str(self.totalpage))
    
    def recordQuery(self,limpage):
        sql="select * from people limit {},{}".format(limpage,self.everycount)
        self.model.setQuery(sql)
        


    # 前一页按钮
    def prvepage(self):
        self.pushButton_2.setEnabled(True)
        if self.currentpage<=0:
            self.pushButton.setEnabled(False)
        self.currentpage-=1
        self.recordQuery(self.currentpage*self.everycount)


    #下一页
    def nextpage(self):
        self.pushButton.setEnabled(True)
        if self.currentpage>=self.totalpage-1:
            self.pushButton_2.setEnabled(False)
        self.currentpage+=1
        
        self.recordQuery(self.currentpage*self.everycount)
    
    #跳转
    def gopage(self):
        size=self.lineEdit.text()


        size=int(size)

        self.currentpage=(size-1)*self.everycount
        self.recordQuery(self.currentpage)
        


if __name__=='__main__':

    app=QApplication(sys.argv)
    demo=Demo()
    demo.show()
  
 
    sys.exit(app.exec_())

 

  • 4
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值