Iterate through the contents of rows selected from a QTableView QSqlQueryModel

Learn how to retrieve and work with user-selected rows in a QTableView backed by a QSqlQueryModel
Heads up! You've already completed this tutorial.

When you're working with a QTableView backed by a QSqlQueryModel or QSqlTableModel, you'll often need to find out which rows the user has selected and then do something with the data in those rows. Maybe you want to delete records, export them, or pass them to another part of your application.

In this tutorial, we'll walk through how to retrieve selected rows from a QTableView, iterate through them, and access specific column values — both by position and by field name.

Setting up a simple database table view

Before we get into row selection, let's set up a minimal working example with a SQLite database and a QTableView. This gives us something to select rows from.

python
import sys

from PyQt6.QtCore import Qt
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
from PyQt6.QtWidgets import (
    QApplication,
    QMainWindow,
    QPushButton,
    QTableView,
    QVBoxLayout,
    QWidget,
)


def create_sample_db():
    """Create an in-memory SQLite database with some sample data."""
    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName(":memory:")
    db.open()

    query = QSqlQuery()
    query.exec(
        "CREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT, city TEXT, age INTEGER)"
    )
    query.exec("INSERT INTO people VALUES (1, 'Alice', 'Portland', 32)")
    query.exec("INSERT INTO people VALUES (2, 'Bob', 'Denver', 45)")
    query.exec("INSERT INTO people VALUES (3, 'Carol', 'Austin', 28)")
    query.exec("INSERT INTO people VALUES (4, 'Dave', 'Seattle', 37)")
    query.exec("INSERT INTO people VALUES (5, 'Eve', 'Boston', 41)")

    return db


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QTableView Selection Example")

        self.model = QSqlQueryModel()
        self.model.setQuery("SELECT * FROM people")

        self.table = QTableView()
        self.table.setModel(self.model)
        self.table.setSelectionBehavior(QTableView.SelectRows)

        self.button = QPushButton("Print Selected Rows")
        self.button.clicked.connect(self.print_selected)

        layout = QVBoxLayout()
        layout.addWidget(self.table)
        layout.addWidget(self.button)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

    def print_selected(self):
        print("Button clicked — no selection logic yet!")


app = QApplication(sys.argv)
create_sample_db()
window = MainWindow()
window.show()
sys.exit(app.exec())

Run this and you'll see a table with five rows of people data and a button underneath. We've set setSelectionBehavior(QTableView.SelectRows) so clicking on any cell selects the entire row. You can hold Ctrl (or Cmd on macOS) to select multiple rows.

Now let's make that button actually do something.

Getting the selected rows

Every QTableView has a selection model that tracks which items the user has selected. You can access it through self.table.selectionModel(). The method we need is selectedRows(), which returns a list of QModelIndex objects — one for each selected row.

Replace the print_selected method with this:

python
def print_selected(self):
    indexes = self.table.selectionModel().selectedRows()
    print(f"{len(indexes)} row(s) selected")
    for index in indexes:
        print(f"  Row {index.row()}")

Select a couple of rows in the table and click the button. You'll see output like:

python
2 row(s) selected
  Row 1
  Row 3

Each QModelIndex in the list tells you the row number (via index.row()) and can be used to look up data in the model.

Packaging Python Applications with PyInstaller by Martin Fitzpatrick — This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.

Get the book

Accessing column data from selected rows

A QModelIndex from selectedRows() points to a specific row and column. By default, selectedRows() returns indexes for column 0. To get data from other columns, you have a few options.

Using model.data() with model.index()

You can ask the model for a new index at any column in the same row, then read the data from it:

python
def print_selected(self):
    indexes = self.table.selectionModel().selectedRows()
    model = self.table.model()

    for index in indexes:
        row = index.row()
        name = model.data(model.index(row, 1))
        city = model.data(model.index(row, 2))
        age = model.data(model.index(row, 3))
        print(f"Row {row}: {name}, {city}, age {age}")

Here we take a reference to the model once with model = self.table.model(), which keeps the code cleaner than calling self.table.model() repeatedly.

The model.index(row, column) call creates a QModelIndex pointing to a specific cell. Then model.data() retrieves the value at that cell. By default it uses Qt.DisplayRole, which gives you the displayed text value — exactly what you'd expect.

Passing a column to selectedRows()

The selectedRows() method accepts an optional column parameter. If you only need data from one specific column, you can pass the column number directly:

python
def print_selected(self):
    indexes = self.table.selectionModel().selectedRows(column=2)

    model = self.table.model()
    for index in indexes:
        city = model.data(index, Qt.DisplayRole)
        print(f"Row {index.row()}: city = {city}")

This is convenient because the returned indexes already point to the column you want — no need to create new indexes manually.

Using index.siblingAtColumn()

If you have an index and want to get data from a different column in the same row, siblingAtColumn() is a concise way to do it:

python
def print_selected(self):
    indexes = self.table.selectionModel().selectedRows()

    for index in indexes:
        name = index.siblingAtColumn(1).data()
        city = index.siblingAtColumn(2).data()
        age = index.siblingAtColumn(3).data()
        print(f"Row {index.row()}: {name}, {city}, age {age}")

Calling .data() directly on a QModelIndex returns the display value for that cell. This approach is compact and doesn't require you to reference the model at all.

Accessing data by field name

When you're working with SQL models, referring to columns by number can be fragile. If you later change your query to add or remove a column, all those numeric positions break silently.

If you're using QSqlTableModel instead of QSqlQueryModel, you can use record() and fieldIndex() to reference columns by their database field name.

Here's a modified version of the example using QSqlTableModel:

python
import sys

from PyQt6.QtCore import Qt
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel
from PyQt6.QtWidgets import (
    QApplication,
    QMainWindow,
    QPushButton,
    QTableView,
    QVBoxLayout,
    QWidget,
)


def create_sample_db():
    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName(":memory:")
    db.open()

    query = QSqlQuery()
    query.exec(
        "CREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT, city TEXT, age INTEGER)"
    )
    query.exec("INSERT INTO people VALUES (1, 'Alice', 'Portland', 32)")
    query.exec("INSERT INTO people VALUES (2, 'Bob', 'Denver', 45)")
    query.exec("INSERT INTO people VALUES (3, 'Carol', 'Austin', 28)")
    query.exec("INSERT INTO people VALUES (4, 'Dave', 'Seattle', 37)")
    query.exec("INSERT INTO people VALUES (5, 'Eve', 'Boston', 41)")

    return db


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QTableView Selection by Field Name")

        self.model = QSqlTableModel()
        self.model.setTable("people")
        self.model.select()

        self.table = QTableView()
        self.table.setModel(self.model)
        self.table.setSelectionBehavior(QTableView.SelectRows)

        self.button = QPushButton("Print Selected Rows")
        self.button.clicked.connect(self.print_selected)

        layout = QVBoxLayout()
        layout.addWidget(self.table)
        layout.addWidget(self.button)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

    def print_selected(self):
        indexes = self.table.selectionModel().selectedRows()

        for index in indexes:
            record = self.model.record(index.row())
            name = record.value("name")
            city = record.value("city")
            age = record.value("age")
            print(f"Row {index.row()}: {name}, {city}, age {age}")


app = QApplication(sys.argv)
create_sample_db()
window = MainWindow()
window.show()
sys.exit(app.exec())

The record(row) method returns a QSqlRecord for that row, and value("field_name") gives you the data for a specific column by its database name. This is much more resilient to schema changes.

You can also combine fieldIndex() with selectedRows() to get indexes for a named column directly:

python
def print_selected(self):
    city_column = self.model.fieldIndex("city")
    indexes = self.table.selectionModel().selectedRows(column=city_column)

    model = self.table.model()
    for index in indexes:
        print(f"Row {index.row()}: city = {model.data(index)}")

This way you never hard-code column numbers.

Complete working example

Here's the full example with all the approaches collected into one application. Each button demonstrates a different way to access the selected row data:

python
import sys

from PyQt6.QtCore import Qt
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel
from PyQt6.QtWidgets import (
    QApplication,
    QMainWindow,
    QPushButton,
    QTableView,
    QVBoxLayout,
    QWidget,
)


def create_sample_db():
    """Create an in-memory SQLite database with some sample data."""
    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName(":memory:")
    db.open()

    query = QSqlQuery()
    query.exec(
        "CREATE TABLE people ("
        "id INTEGER PRIMARY KEY, "
        "name TEXT, "
        "city TEXT, "
        "age INTEGER)"
    )
    query.exec("INSERT INTO people VALUES (1, 'Alice', 'Portland', 32)")
    query.exec("INSERT INTO people VALUES (2, 'Bob', 'Denver', 45)")
    query.exec("INSERT INTO people VALUES (3, 'Carol', 'Austin', 28)")
    query.exec("INSERT INTO people VALUES (4, 'Dave', 'Seattle', 37)")
    query.exec("INSERT INTO people VALUES (5, 'Eve', 'Boston', 41)")

    return db


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QTableView — Iterating Selected Rows")
        self.resize(500, 400)

        self.model = QSqlTableModel()
        self.model.setTable("people")
        self.model.select()

        self.table = QTableView()
        self.table.setModel(self.model)
        self.table.setSelectionBehavior(QTableView.SelectRows)

        btn_by_index = QPushButton("Print via model.data()")
        btn_by_index.clicked.connect(self.print_via_model_data)

        btn_by_sibling = QPushButton("Print via siblingAtColumn()")
        btn_by_sibling.clicked.connect(self.print_via_sibling)

        btn_by_name = QPushButton("Print via record().value()")
        btn_by_name.clicked.connect(self.print_via_record)

        layout = QVBoxLayout()
        layout.addWidget(self.table)
        layout.addWidget(btn_by_index)
        layout.addWidget(btn_by_sibling)
        layout.addWidget(btn_by_name)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

    def print_via_model_data(self):
        """Access column data using model.data() and model.index()."""
        indexes = self.table.selectionModel().selectedRows()
        model = self.table.model()

        print("--- Using model.data() ---")
        for index in indexes:
            row = index.row()
            name = model.data(model.index(row, 1))
            city = model.data(model.index(row, 2))
            age = model.data(model.index(row, 3))
            print(f"  Row {row}: {name}, {city}, age {age}")

    def print_via_sibling(self):
        """Access column data using siblingAtColumn()."""
        indexes = self.table.selectionModel().selectedRows()

        print("--- Using siblingAtColumn() ---")
        for index in indexes:
            name = index.siblingAtColumn(1).data()
            city = index.siblingAtColumn(2).data()
            age = index.siblingAtColumn(3).data()
            print(f"  Row {index.row()}: {name}, {city}, age {age}")

    def print_via_record(self):
        """Access column data by field name using record()."""
        indexes = self.table.selectionModel().selectedRows()

        print("--- Using record().value() ---")
        for index in indexes:
            record = self.model.record(index.row())
            name = record.value("name")
            city = record.value("city")
            age = record.value("age")
            print(f"  Row {index.row()}: {name}, {city}, age {age}")


app = QApplication(sys.argv)
create_sample_db()
window = MainWindow()
window.show()
sys.exit(app.exec())

Run this, select one or more rows (hold Ctrl/Cmd for multi-select), and click each button to see the different approaches produce the same output in the console.

Summary

Here's a quick recap of the approaches we covered:

Approach When to use it
model.data(model.index(row, col)) General purpose, works with any model
selectedRows(column=N) When you only need one specific column
index.siblingAtColumn(col).data() Concise, no model reference needed
model.record(row).value("name") SQL models — reference columns by name, resilient to schema changes
model.fieldIndex("name") Convert a field name to a column number for use with other approaches

For SQL-backed models, referencing columns by field name with record().value() or fieldIndex() is generally the safest approach, since it won't break if you modify your database schema or query later. For quick one-off access or non-SQL models, the index-based approaches work well and are perfectly readable.

If you're looking to learn more about working with table views and models, see our tutorial on the Model/View architecture in PyQt6, or take a deeper dive into displaying tabular data with QTableView using NumPy and Pandas. You can also sort and filter table data using proxy models, or learn about editing data in a QTableView.

Over 15,000 developers have bought Create GUI Applications with Python & Qt!
Create GUI Applications with Python & Qt6
Get the book

Downloadable ebook (PDF, ePub) & Complete Source code

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak
Martin Fitzpatrick

Iterate through the contents of rows selected from a QTableView QSqlQueryModel was written by Martin Fitzpatrick.

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.