# Retrieve data from SQLite queries with Python

Since we already know how to connect to SQLite and how to execute queries, all that's left is making use of that cursor that we get back.

When retrieving data we don't need to use the context manager because we're not going to commit or rollback anything. So we'll just use the connection to execute the query:

cursor = connection.execute("SELECT * FROM entries;")

connection.execute returns the cursor that it created implicitly to run the query. It is the same thing as doing this:

cursor = connection.cursor()
cursor.execute("SELECT * FROM entries;")

Just a little bit shorter!

Now that our cursor has executed the query, it is pointing at the very first row of the results returned by the database.

We just need to use those results. We can:

  • Fetch one row;
  • Fetch all rows;
  • Iterate over the cursor as if it were a list (using a for loop).

So with the cursor we can do:

  • cursor.fetchone() - gives you a tuple with the contents of the first row in the results.
  • cursor.fetchall() - gives you a list of tuples with all the contents of the results.
  • Something like this:
for row in cursor:
    print(row)  # tuple of one row

# Creating our data retrieval function

Let's put this code into a function so that we can use it from within app.py. At the moment, we have this function that uses the Python list as a data store:

def get_entries():
    return entries

We're going to change it to this:

def get_entries():
    return connection.execute("SELECT * FROM entries;")

This will return the cursor, and then we can iterate over the cursor inside app.py. This is the code we have in app.py at the moment:

def view_entries(entries):
    for entry in entries:
        print(f"{entry['date']}\n{entry['content']}\n\n")

But the cursor will use indexes instead of keys to access values. We must change the function to this:

def view_entries(entries):
    for entry in entries:
        print(f"{entry[1]}\n{entry[0]}\n\n")

That is because the column 0 is the content, and column 1 is the date. Make sure to swap these numbers around if your CREATE TABLE statement has a different column order.

# Making SQLite give us named results

Especially if you're working with tables that have many columns, being able to access row values by name (e.g. entry['date'] instead of entry[1]) can be super helpful.

SQLite allows us to do this, and we only have to tell it that we want to do that.

Whenever SQLite executes a query and builds a result set, it puts each resultant row of data into the cursor as a tuple. But we can tell it to create an object instead.

If we tell it to create an object, we can make that object allow us to access each column by its name instead of by index.

Fortunately, we don't have to code these objects ourselves. SQLite comes with one that does it all for us. To tell SQLite to use it when fetching results, all we have to do is this[1]:

connection = sqlite3.connect("data.db")
connection.row_factory = sqlite3.Row

What that'll do is for each row of data, SQLite will put the data into a sqlite3.Row object that allows us to access the data by the name of the column. Now we can go back to app.py and revert the change we made just a moment ago:

def view_entries(entries):
    for entry in entries:
        print(f"{entry['date']}\n{entry['content']}\n\n")

You can still access the data by index if you prefer, but using sqlite3.Row you can also access the data by name. It's up to you which one to use, but there's no point to using sqlite3.Row if you always access data by index. Just something to remember!


  1. What is the purpose of row_factory? (StackOverflow) (opens new window) ↩ī¸Ž