1. 程式人生 > >《快速掌握PyQt5》第二十六章 資料庫

《快速掌握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