《快速掌握PyQt5》第二十六章 資料庫
第二十六章 資料庫
如果小夥伴還不瞭解資料庫可以先去快速學習下,也可以選擇先跳過本章,後續章節閱讀並不會受到影響。
PyQt5提供了一些資料庫驅動以方便我們來連線不同的資料庫:
驅動型別 |
支援的資料庫 |
QDB2 |
IBM DB2 |
QIBASE |
Borland InterBase |
QMYSQL |
MySQL |
QOCI |
Oracle呼叫介面 |
QODBC |
ODBC(包括微軟SQL伺服器) |
QPSQL |
PostgreSQL |
QSQLITE |
|
QTDS |
Sybase自適應伺服器 |
本章筆者將會使用QSQLITE驅動和QMYSQL驅動來進行演示並講解如何在PyQt5中進行資料庫應用(連線和使用資料庫操作其實都是類似的)。請讀者確保已經安裝並配置好MySQL資料庫(SQLite資料庫無需額外下載和配置,我們可直接在PyQt5中使用)。
26.1 資料庫連線和關閉
在用到資料庫的程式中,我們通常把資料庫連線操作放在程式應用開始時(因為資料庫無法連線的話,程式的功能就會收到影響了,所以要先確保資料庫連線成功)。首先來看一下連線SQLite資料庫:
import sys from PyQt5.QtSql import QSqlDatabase from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.db = None self.db_connect() def db_connect(self): self.db = QSqlDatabase.addDatabase('QSQLITE') # 1 self.db.setDatabaseName('./test.db') # 2 if not self.db.open(): # 3 QMessageBox.critical(self, 'Database Connection', self.db.lastError().text()) def closeEvent(self, QCloseEvent): # 4 self.db.close() if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_())
1. 通過呼叫QSqlDatabase類的addDatabase()方法來建立一個數據庫連線,因為要連線SQLite資料庫,所以這裡傳入的是QSQLite引數;
2. 呼叫setDatabaseName()設定要使用的資料庫名稱,只需要寫入一個路徑,檔名以.db結尾即可(若該資料庫已經存在,則使用該資料庫;若不存在則會新建一個);
3. 呼叫open()方法開啟資料庫,若開啟成功則返回True,失敗則返回False。在這裡我們用訊息框來提示使用者資料庫開啟失敗,lastErrot().text()方法可以獲取資料庫開啟失敗的原因;
4. 在視窗關閉事件中通過self.db.close()方法來關閉資料庫。
執行成功後我們會在當前目錄下多出一個test.db的檔案。
其他型別的資料庫連線要多幾行程式碼,這裡以MySQL為例:
import sys
from PyQt5.QtSql import QSqlDatabase
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.db = None
self.db_connect()
def db_connect(self):
self.db = QSqlDatabase.addDatabase('QMYSQL') # 1
self.db.setHostName('localhost') # 2
self.db.setDatabaseName('test_db') # 3
self.db.setUserName('root') # 4
self.db.setPassword('password') # 5
if not self.db.open():
QMessageBox.critical(self, 'Database Connection', self.db.lastError().text())
def closeEvent(self, QCloseEvent):
self.db.close()
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
1. 要連線MySQL資料庫那就要使用QMYSQL驅動;
2. 呼叫setHostName()方法設定主機名,因為是本地的,所以直接寫localhost;
3. 呼叫setDatabaseName()設定要使用的資料庫名稱,請注意筆者之前已經在MySQL上新建了一個名為test_db的資料庫,使用某資料庫名前,請確保相應的資料庫存在;
4-5. 呼叫setUserName()和setPassword()來分別輸入資料庫的使用者名稱和密碼。
26.2 SQL語句使用
在PyQt5中,我們可以通過QSqlQuery類來執行SQL語句(以下均以操作MySQL資料庫為例):
import sys
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.db = None
self.db_connect()
self.sql_exec()
def db_connect(self):
self.db = QSqlDatabase.addDatabase('QMYSQL')
self.db.setHostName('localhost')
self.db.setDatabaseName('test_db')
self.db.setUserName('root')
self.db.setPassword('password')
if not self.db.open():
QMessageBox.critical(self, 'Database Connection', self.db.lastError().text())
def closeEvent(self, QCloseEvent):
self.db.close()
def sql_exec(self):
query = QSqlQuery() # 1
query.exec_("CREATE TABLE students " # 2
"(id INT(11) PRIMARY KEY, class VARCHAR(4) NOT NULL, "
"name VARCHAR(25) NOT NULL, score FLOAT)")
query.exec_("INSERT INTO students (id, class, name, score) " # 3
"VALUES (2018010401, '0104', 'Louis', 59.5)")
query.exec_("INSERT INTO students (id, class, name, score) "
"VALUES (2018011603, '0116', 'Chris', 99.5)")
query.exec_("SELECT name, class, score FROM students") # 4
while query.next():
stu_name = query.value(0)
stu_class = query.value(1)
stu_score = query.value(2)
print(stu_name, stu_class, stu_score)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
1. 例項化一個QSqlQuery物件,之後只用對該物件呼叫exec_()方法就可以執行SQL語句了;
2. 這裡我們新建了一個students表,一共有四個欄位id, class, name, score;
3. 這裡插入兩條資料。這裡使用的是直接插入資料的方式,當然我們還可以使用佔位符進行插入,這時就需要用到prepare()方法。一共有兩種插入風格,首先是Oracle風格:
query.prepare("INSERT INTO students (id, class, name, score) "
"VALUES (:id, :class, :name, :score)")
query.bindValue(':id', 2018010401)
query.bindValue(':class', '0104')
query.bindValue(':name', 'Louis')
query.bindValue(':score', 59.5)
query.exec_()
還有一種是ODBC風格:
query.prepare("INSERT INTO students (id, class, name, score) "
"VALUES (?, ?, ?, ?)")
query.addBindValue(2018011603)
query.addBindValue('0116')
query.addBindValue('Chris')
query.addBindValue(99.5)
query.exec_()
雖然麻煩點,但是可以避免將int型的數值轉化為字串。
4. 這裡進行查詢操作,執行完畢後呼叫next()方法就可以將記錄指標定位到返回結果中的第一條。呼叫value()方法傳入相應的索引值就可以返回指定的欄位資料,最後控制檯會輸出我們的查詢結果:
讀者朋友可以自行下載相應的資料庫視覺化工具來查看錶中的資料。
26.3 使用模型和檢視
PyQt5提供了相比QSqlQuery之下更高階的介面:QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel。這些模型類讓我們不必生硬地在程式碼中使用原始的SQL語句,而且提供了許多便捷的方法。對應的檢視我們通常會選擇表格檢視QTableView(因為資料庫中是用表來儲存資料的嘛)。我們主要來講下QSqlQueryModel和QSqlTableModel這兩種模型,(QSqlRelationalTableModel用於有外來鍵的表,大部分用法和QSqlTableModel類似,我們這裡就簡單講一下如何建立表之間的聯絡並顯示資料)。
QSqlQueryModel類為SQL結果集提供了只讀的資料模型。它基於低階的QSqlQuery類,用於執行SQL語句和遍歷結果集。接下來我們就直接使用在26.2中使用的資料表進行操作即可:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtSql import QSqlDatabase, QSqlQueryModel
from PyQt5.QtWidgets import QApplication, QMessageBox, QTableView
class Demo(QTableView):
def __init__(self):
super(Demo, self).__init__()
self.db = None
self.db_connect()
self.sql_exec()
def db_connect(self):
self.db = QSqlDatabase.addDatabase('QMYSQL')
self.db.setHostName('localhost')
self.db.setDatabaseName('test_db')
self.db.setUserName('root')
self.db.setPassword('password')
if not self.db.open():
QMessageBox.critical(self, 'Database Connection', self.db.lastError().text())
def closeEvent(self, QCloseEvent):
self.db.close()
def sql_exec(self):
model = QSqlQueryModel() # 1
model.setQuery("SELECT id, name, class, score FROM students")
model.setHeaderData(0, Qt.Horizontal, 'ID')
model.setHeaderData(1, Qt.Horizontal, 'Name')
model.setHeaderData(2, Qt.Horizontal, 'Class')
model.setHeaderData(3, Qt.Horizontal, ' Score')
self.setModel(model) # 2
for i in range(model.rowCount()): # 3
id = model.record(i).value('id')
name = model.record(i).value(1)
print(id, name)
print('---------------------')
for i in range(model.rowCount()): # 4
id = model.data(model.index(i, 0))
name = model.data(model.index(i, 1))
print(id, name)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
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()傳入行列索引值即可。
執行截圖如下:
QSqlQueryModel模型只提供只讀操作,如果要同時獲取寫入操作的話,那就需要繼承QSqlQueryModel並重新實現setData()和fla()方法。另一種選擇是使用QSqlTableModel,該模型可讀可寫:
def sql_exec(self):
model = QSqlTableModel() # 1
model.setTable('students')
model.setEditStrategy(QSqlTableModel.OnFieldChange)
model.setHeaderData(0, Qt.Horizontal, 'ID')
model.setHeaderData(1, Qt.Horizontal, 'Class')
model.setHeaderData(2, Qt.Horizontal, 'Name')
model.setHeaderData(3, Qt.Horizontal, 'Score')
model.select()
self.setModel(model)
其他地方程式碼不變,我們只需要改下sql_exe()函式即可。
1. 例項化一個QSqlTableModel模型,呼叫setTable()來選擇要進行操作的資料表。setEditStrategy()用來設定模型的編輯策略,一共有以下三種:
QSqlTableModel::OnFieldChange |
0 |
所有變更立即更新到資料庫中 |
QSqlTableModel::OnRowChange |
1 |
當用戶對某行資料操作後,點選其他行時再更新資料庫 |
QSqlTableModel::OnManualSubmit |
2 |
只有在呼叫submitAll()或者reverAll()後才會更新資料庫 |
最後呼叫select()方法選擇該表中的所有資料,相應的檢視也會顯示出全部的資料;
此時執行截圖如下:
我們把第三行Chris的分數改成100,由於編輯策略是OnFieldChange,所以此時資料庫中的值也會立即改變:
我們關掉程式,然後再次執行就會發現資料庫中的值確實改變了,分數已經變成了100。
以下是插入操作:
def sql_exec(self):
model = QSqlTableModel()
model.setTable('students')
model.setEditStrategy(QSqlTableModel.OnFieldChange)
model.setHeaderData(0, Qt.Horizontal, 'ID')
model.setHeaderData(1, Qt.Horizontal, 'Class')
model.setHeaderData(2, Qt.Horizontal, 'Name')
model.setHeaderData(3, Qt.Horizontal, 'Score')
model.select()
model.insertRow(0) # 1
model.setData(model.index(0, 0), 201801010111)
model.setData(model.index(0, 1), '0101')
model.setData(model.index(0, 2), 'Who Cares')
model.setData(model.index(0, 3), 0.5)
model.submit()
self.setModel(model)
1. 要進行插入操作的話,只需要呼叫insertRow()方法傳入索引值來確定要插入的位置。這裡我們傳入0表示在第一行插入。setData()方法可以用來插入或更新值,需要兩個引數,第一個是位置,第二個是插入的資料。最後呼叫submit()方法來提交我們對資料庫所做的更改。
此時執行截圖如下:
過濾操作也非常簡單:
def sql_exec(self):
model = QSqlTableModel()
model.setTable('students')
model.setEditStrategy(QSqlTableModel.OnFieldChange)
model.setHeaderData(0, Qt.Horizontal, 'ID')
model.setHeaderData(1, Qt.Horizontal, 'Class')
model.setHeaderData(2, Qt.Horizontal, 'Name')
model.setHeaderData(3, Qt.Horizontal, 'Score')
model.insertRow(0)
model.setData(model.index(0, 0), 2018010111)
model.setData(model.index(0, 1), '0101')
model.setData(model.index(0, 2), 'Who Cares')
model.setData(model.index(0, 3), 0.5)
model.submit()
model.setFilter('score < 60') # 1
model.select()
self.setModel(model)
1. 呼叫setFilter()方法可以進行過濾操作。在這裡我們選擇了分數不及格的同學進行顯示。這等價於下面的SQL語句:
SELECT * FROM students WHERE score < 60
此時執行截圖如下:
再來看一下如何刪除行:
def sql_exec(self):
model = QSqlTableModel()
model.setTable('students')
model.setEditStrategy(QSqlTableModel.OnFieldChange)
model.setHeaderData(0, Qt.Horizontal, 'ID')
model.setHeaderData(1, Qt.Horizontal, 'Class')
model.setHeaderData(2, Qt.Horizontal, 'Name')
model.setHeaderData(3, Qt.Horizontal, 'Score')
model.insertRow(0)
model.setData(model.index(0, 0), 2018010111)
model.setData(model.index(0, 1), '0101')
model.setData(model.index(0, 2), 'Who Cares')
model.setData(model.index(0, 3), 0.5)
model.submit()
model.setFilter('score < 60')
model.select()
model.removeRow(0) # 1
model.submit()
self.setModel(model)
1. 呼叫removeRow()方法傳入索引值即可。
此時執行截圖如下:
如果想獲取單獨資料的話,用上面講過的record()+value()方法或者data()方法即可。
最後是QSqlRelationalTableModel,該模型使用者建立表之間的聯絡(即外來鍵),大部分用法和QSqlTableModel類似,所以筆者這裡就主要講解下如何建立聯絡並顯示。同樣我們會用到上面的students表,不過還需要再新建另一個teachers表:
然後插入兩條資料:
def sql_exec(self):
model = QSqlRelationalTableModel()
model.setTable('students')
# 1
model.setRelation(1, QSqlRelation('teachers', 'class', 'name'))
# 2
model.setHeaderData(0, Qt.Horizontal, 'ID')
model.setHeaderData(1, Qt.Horizontal, 'Teacher')
model.setHeaderData(2, Qt.Horizontal, 'Student')
model.setHeaderData(3, Qt.Horizontal, 'Score')
model.select()
self.setModel(model)
1. 要建立表之間的聯絡,就要呼叫setRelation()方法。它一共傳入兩個引數,第一個是作為外來鍵的索引,第二個是QSqlRelation物件。可以知道筆者這裡是想把class欄位作為外來鍵的,而在students表中class外來鍵的索引為1。QSqlRelation物件例項化需要三個引數:外來鍵關係對應的表、外來鍵欄位名以及要進行顯示的欄位。外來鍵關係對應的表很明顯就是teachers了,teachers表中主鍵名class就是聯絡時用的外來鍵欄位名稱,最後個teachers中的name欄位就是用於顯示的。
簡單來說,這行程式碼就是把兩個表建立了聯絡,並且把students中class欄位下的值換成teachers表中name欄位下的值用於顯示;
2. 設定表格標題,因為顯示的不是班級而是老師的名字,所以把Class改成了Teacher。
執行截圖如下:
26.4 小結
1. 使用資料庫前當然是要先建立連線,別忘了在程式關閉時要同時關閉資料庫哦~
2. 如果是單純的想執行SQL語句,那我們可以使用QSqlQuery類;
3. 資料模型類為我們提供了很多便捷的方法,非常適合在大型複雜的專案中使用;
4. QSqlQueryModel為只讀模型,如果想要讀寫的話,建議使用QSqlTableModel,這個也是我們平常使用最多的模型了。
----------------------------------------------------------------------
喜歡的小夥伴可以加入這個Python QQ交流群一起學習:820934083