Search code examples
pythonpyqtpyqt5qsqldatabase

PyQt subclassing QSqlDatabase to make exceptions


I love all of the goodness you get from QSqlDatabase and QSqlQuery and various models. But I would really like exceptions to be thrown when errors occur. So I thought maybe I could subclass QSqlDatabase to throw an exception when the open() method fails:

from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableView)
from PyQt5.QtSql import (QSqlQuery, QSqlQueryModel, QSqlDatabase)
import sys

class ExceptionalDatabase(QSqlDatabase):

    #def addDatabase(self, *args, **kwargs):
        # what to put here? I need to return an instance of
        # ExceptionalDatabase, right?
        #
        # this doesn't work:
        # return super(ExceptionalDatabase, self).addDatabase(*args, **kwargs)

    def open(self, user=None, pwd=None):
        # this method will never be called, because addDatabase will
        # return a QSqlDatabase instance
        if user is None:
            retval = super(ExceptionalDatabase, self).open()
        else:
            retval = super(ExceptionalDatabase, self).open(user=user, password=pwd)

        if retval == False:
            raise ValueError(self.lastError().text())

app = QApplication(sys.argv)

fid = open('example.db', 'w')
fid.close()

db = ExceptionalDatabase.addDatabase("QSQLITE")
db.setDatabaseName('example.db')
db.open()

db.close()

sys.exit(app.exec_())

The main issue I have is I don't know how to handle the addDatabase method. It returns an instance of QSqlDatabase. But I want it to return an instance of ExceptionalDatabase. How would I get an instance of my class from addDatabase?


Solution

  • First of all addDatabase is a static method so you should not pass it self. On the other hand, a workaround is to establish the __class__ of the QSqlDataBase:

    class ExceptionalDatabase(QSqlDatabase):
        @staticmethod
        def addDatabase(*args, **kwargs):
            db = QSqlDatabase.addDatabase(*args, **kwargs)
            db.__class__ = ExceptionalDatabase
            return db
    
        def open(self, user=None, pwd=None):
            if user is None:
                retval = super(ExceptionalDatabase, self).open()
            else:
                retval = super(ExceptionalDatabase, self).open(user=user, password=pwd)
            if retval == False:
                raise ValueError(self.lastError().text())