目录
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的使用获得数据库异常
最常用的数据库模型主要有两种:关系型数据库和非关系型数据库
主要有:Sqlserver, mysql ,oracle ,sqlite,mariaDB,postgreSQL
关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织:
优点:
1、易于维护:都是使用表结构,格式一致;
2、使用方便:SQL语言通用,可用于复杂查询;
3、复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。
缺点:
1、读写性能比较差,尤其是海量数据的高效率读写;
2、固定的表结构,灵活度稍欠;
3、高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。
主要有:mongoDb,redis,couchDb,cassandra,neo4j,hbase
非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。
优点:
1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
2、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
3、高扩展性;
4、成本低:nosql数据库部署简单,基本都是开源软件。
非关系型数据库的分类和比较:
1、文档型
2、key-value型
3、列式数据库
4、图形数据库
Qt中内置了好几个数据库的驱动程序,也就是说我们可以直接在PyQt中对这些数据库进行操作。这些内置的数据库包括:
db = QtSql.QSqlDatabase.addDatabase('驱动名')
db.setHostName('主机名')
db.setDatabaseName('数据库名')
db.setUserName('用户名')
db.setPassword('密码')
db.open()
坑:发现PyQt5中用QMYSQL驱动会出现不能加载错误,找了许多没有解决,不过可以参考https://blog.csdn.net/qq_38198744/article/details/80261695和https://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_())
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()传入行列索引值即可
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()方法来提交我们对数据库所做的更改。
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_())
参考博客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。
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_())