2

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?

bfris
  • 5,272
  • 1
  • 20
  • 37

1 Answers1

1

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())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • I cannot get addDatabase to work correctly. There is something it doesn't like about the signature. It *says* it's expecting `QSqlDatabase.addDatabase(str, str connectionName=QLatin1String(QSqlDatabase.defaultConnection))` or `QSqlDatabase.addDatabase(QSqlDriver, str connectionName=QLatin1String(QSqlDatabase.defaultConnection))`. The only thing I could get to work was by passing bogus first argument: `db = ExceptionalDatabase.addDatabase('', 'QSQLITE')` – bfris Jun 03 '20 at 22:19
  • @bfris What tool gives you this warning? Are you using any linter or IDE? – eyllanesc Jun 03 '20 at 22:23
  • I get this from command line. TypeError: arguments did not match any overloaded call: QSqlDatabase.addDatabase(str, str connectionName=QLatin1String(QSqlDatabase.defaultConnection)): not enough arguments QSqlDatabase.addDatabase(QSqlDriver, str connectionName=QLatin1String(QSqlDatabase.defaultConnection)): not enough arguments – bfris Jun 03 '20 at 22:34
  • How strange, which version of PyQt5 and python do you use? With PyQt5 5.14.2 and python 3.8.3 I don't see that problem on Linux. – eyllanesc Jun 03 '20 at 22:35
  • Oh. This is embarrassing. You left `self` off of the signature and I included self. If I get rid of it, it works a treat with Python as old as 3.4.4 and Qt 5.5.1. – bfris Jun 03 '20 at 22:57
  • @bfris oops, it usually happens :-). – eyllanesc Jun 03 '20 at 23:00