2

I've run into an issue with PySide6 that I cannot subclass QSqlDatabase.

My biggest complaint about the QtSql system is that it does not throw exceptions when things go wrong. You have to manually check if things are really open or if there are any errors. So I want to subclass parts of it to automatically throw errors for me.

I asked this same question 5 years ago about PyQt5. That solution (from the venerable @eyllanesc) still works in PyQt6, but not PySide6. Here's a minimal example:

from PySide6.QtWidgets import (QApplication, QMainWindow, QTableView)
from PySide6.QtSql import (QSqlQuery, QSqlQueryModel, QSqlDatabase)
#from PyQt6.QtWidgets import (QApplication, QMainWindow, QTableView)
#from PyQt6.QtSql import (QSqlQuery, QSqlQueryModel, QSqlDatabase)
import sys

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())
            
app = QApplication(sys.argv)

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

db = ExceptionalDatabase.addDatabase("QSQLITE")
db.setDatabaseName('example.db') # throws error that __init__ was not called
db.open()
if db.isOpen():
    print('database opened')
else:
    print('database did not open')

db.close()

sys.exit(app.exec())

It throws an error when I make the next call after addDatabase:

RuntimeError: 'init' method of object's base class (ExceptionalDatabase) not called.

If I try to explicitly call the __init__ function (db.__init__()), then it complains that I cannot call __init__ twice.

8
  • I'm not 100% sure, but knowing how differently behave PyQt and PySide under the hood (sip vs. Shiboken), "overwriting" the __class__ is probably inappropriate, and, from what I can understand, it may also be impossible to do that for a privately instantiated class. Yet, there may be a solution (which seems to be still valid for PySide6) at least based on this old post: Unable to change __class__ of PySide.QtGui objects. Commented Sep 19 at 20:17
  • Note that the above is strictly for virtual methods, and, in theory, you should be able to monkey patch an instance with your own functions (which internally call the actual function of the instance) as long as they're always called directly from Python. I've done this several times in PyQt to "patch" older Qt versions (in order to provide missing or bugged behavior), but I don't see reasons for which this shouldn't work in PySide - again, as long as we're talking about functions specifically called from Python. Commented Sep 19 at 20:23
  • 1
    What is the specific use-case for creating a subclass, as such? A manager class that delegates to QSqlDatabase would be much easier to handle. There will usually be many higher-level functions that need direct access to a database connection, and the natural place for such functions is a database-manager class. Why worry about the type of the database connection itself? What concrete problem is this intended to solve? As the Qt docs state, QSqlDatabase is a value class, so keeping references to instances is unnecessary (and often undesirable). Commented Sep 20 at 13:43
  • The use case is that when open fails, I want to throw an exception. Commented Sep 21 at 19:02
  • @bfris If you're doing this just for checking open(), then trying to do all that is inefficient: you're only complicating things. As ekhumoro said, you can simply use a manager class, and eventually use similar function names (with similar argument signatures) used as delegates for the actual calls done to the QSqlDatabase when necessary, while keeping in mind the warninsg suggested in the related docs (eg: don't keep a persistent reference to QSqlDatabase unless you know what you're doing). Commented Sep 21 at 19:22

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.