PyQt efficiently works with relational databases. No SQL queries needed. | Towards Data Science

Python is an easy-to-learn and powerful programming language.
You can get sophisticated outputs, and compared to other languages, you’ll need to write significantly fewer lines of code.
However, when it comes to GUI application development, you can experience difficulties. Especially when you need to work with huge datasets.
This article is part of PyQt & Relational Databases Series:
Stefan Melo
If you’d like to read more content like this, become a member:
melo-stefan.medium.com
Or you can subscribe to my newsletter below. Thank you.
The first issue with GUI in python is the proper framework to choose.
For database applications, I strongly recommend PyQt. It saves a vast amount of boilerplate code, and its database support is extensive.
For this purpose, I’ve tried to avoid third-party module imports as much as possible. The most important advantage of this approach is code independence when it comes to package updates.
Thus maintenance is being reduced significantly. The only package, outside of python’s internal modules, needed here is PyQt.
Note: I assume you are familiar with
PyQt installation
Basic usage of the framework
SQLite databases
Basics of relational databases
It’s essential to understand that a relational database consists of at least two tables, which is the simplest case. To avoid our example look boring, we’ll have three tables.
Relational database groups data into tables based on specific criteria. Some tables can relate to each other; they are linked together.
These links are based on shared data. Instead of challenging definitions, let’s see an example of related tables.
To maintain simplicity, we’ll use the SQLite database. There are multiple rationales behind this decision.
SQLite is a fast, small, self-contained, highly reliable, and full-featured SQL database engine.
The database is stored in a single disc file. It’s accessible to everyone for any purpose, and thesqlite3 module is python’s in-built module.
We create the main table ‘orders’ with some data:
Now, we have an elementary order booking system to play with. The table is hideous since it contains non-human-readable values. Yes, indeed!
AlthoughOrderID, Customer_id, and Product_id values make sense — they’re ids, there are the same weird values in column Price as in column Product_id.
It’s not an accident. The Price value in table orders is not an actual price. It’s a reference to Product_id in theproducts table. It’s the developer’s responsibility to assign the Product_id value to Price whenever the Product_id changes.
We also have customers and products tables, as shown below. These tables hold the information (values) needed for the orders table.
Let’s start coding now. First, we import the inevitable modules.
import sysfrom PyQt6.QtWidgets import QApplication, QMainWindow, QMessageBox, QTableViewfrom PyQt6.QtSql import QSqlDatabase, QSqlRelation, QSqlRelationalTableModel
Here I’d like to note that I use PyQt6. However, the code will work with PyQt5 without any issues.
Next, we’ll include the typical code snippet needed for the PyQt application to run. Nothing special.
if __name__ == "__main__":    app = QApplication(sys.argv)    form = MainWindow()    form.show()    app.exec()
Here we created app, an instance of PyQt QApplication class, form which is an instance of MainWindow class; we’ll create it later.
We make sure the form will show, and we execute the app.
Next, we’ll create a function createConnection() which returns a boolean value.
The function will handle the database connection and returns True when everything is correct, and the database connection is stable.
# Type hint for return valuedef createConnection() -> bool:    # SQLite type database connection instance        con = QSqlDatabase.addDatabase('QSQLITE')     # Connect to the database file    con.setDatabaseName('testDB.db')    # Show message box when there is a connection issue    if not con.open():        QMessageBox.critical(            None,            'QTableView Example - Error!',            'Database Error: %s' % con.lastError().databaseText(),        )        return False    return True
We don’t want the application to start without a proper database connection.
The best moment to validate the link is before GUI fully starts. So we have to modify the application initialization code above.
if __name__ == "__main__":    app = QApplication(sys.argv)    if not createConnection():        sys.exit(1)    form = MainWindow()    form.show()    app.exec()
It’s time to code our desired GUI for convenient data presentation.
We’ve already prepared our main form (window) of the application. You can see above form = MainWindow(), the MainWindow class instance named form. Nevertheless, we still don’t have the MainWindow class!
We’ll do something about it right now. This is the most appealing part.
I can reveal now that we’ll be using the QTableView widget. It’s capable of the magic we need.
# Inherit from QMainWindowclass MainWindow(QMainWindow):    def __init__(self, parent = None):        super().__init__(parent)        # Set the window title        self.setWindowTitle('QTable Example')        # Create the model        model = QSqlRelationalTableModel(self)        # Set the table to display        model.setTable('orders')        # Set relations for related columns to be displayed        model.setRelation(1, QSqlRelation('products', 'ProductID', 'Price'))        model.setRelation(2, QSqlRelation('customers', 'CustomerID', 'Customer'))        model.setRelation(3, QSqlRelation('products', 'ProductID', 'Product'))        model.select()        # Setup the view        # Create the view = a table widget        view = QTableView(self)        # Set the data model for table widget        view.setModel(model)        # Adjust column widths to their content        view.resizeColumnsToContents()        # Add the widget to main window        self.setCentralWidget(view)
The code above shows the whole application GUI setup. Comments explain the details of every step.
However, I’d like to clarify and analyze the model’s setRelation() method and use of the QSqlRelation object.
Let’s see what we’ve done here except lines linked to GUI itself. We’ll focus on our data model.
PyQt includes several model objects we can use effectively. In a view, we work with a relational table; the SqlRelationalTableModel class fits entirely. The model we’ll use does all the hard work we need.
The setRelation() method requires two arguments: column and relation. The method specifies that the column (a foreign key) has its behavior defined by relation object.
Let’s take the first case as an example:
model.setTable(‘orders’) model.setRelation(1, QSqlRelation('products', 'ProductID', 'Price'))
The behavior is that column 1 in table orders maps with field ProductID of table customers, and the view should present the customers’ Customer field to the user.
Using the QSqlRelationalTableModel, we gracefully avoided SQL language knowledge and other boilerplate code for constructing complex SQL queries.
Next, we’ll make our table widget more engaging.