Skip to Content
Course content

285: Using sqlite3 for Local Databases

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I worked with a developer who was building a personal finance tracker. He started by saving everything into a massive JSON file. It worked great for the first month, but as his data grew to a few thousand transactions, he hit a wall. Every time he wanted to update a single transaction category, he had to load the entire file into memory, modify the list, and rewrite the whole file back to disk. It was slow, risky—one crash during a save and the whole history was gone—and honestly, a nightmare to query.

That's the exact moment you stop using flat files and move to SQLite. The beauty of the sqlite3 module in Python is that it gives you the full power of a relational database without the overhead of setting up a server like PostgreSQL or MySQL. It's just a file on your hard drive, but you get to use SQL to filter, sort, and update your data efficiently.

Connecting to a File-Based Database

When you call sqlite3.connect('my_data.db'), Python looks for that file. If it doesn't exist, it creates it on the fly. I always recommend giving your database file a clear name so you don't accidentally delete it thinking it's a temporary cache file. Once you have a connection object, you're essentially holding a pipeline to that file.

import sqlite3

# This creates the file if it doesn't exist
connection = sqlite3.connect('library.db')

# We need a cursor to actually execute commands
cursor = connection.cursor()

# Create a table for our home library
cursor.execute('''
    CREATE TABLE IF NOT EXISTS books (
        id INTEGER PRIMARY KEY, 
        title TEXT, 
        author TEXT, 
        year INTEGER
    )
''')

connection.commit()

You'll notice I used connection.commit(). This is a common spot where people trip up. SQLite uses transactions; if you insert or update data but don't commit, your changes stay in a temporary state and vanish the moment the program closes. Think of it as "saving" your progress.

Interacting with Data and Avoiding Injection

The cursor object is where the actual work happens. You use it to run your SQL commands, and it keeps track of where you are in the resulting dataset. If you're pulling 10,000 rows, you don't necessarily want them all in a Python list at once—you can use fetchone() to iterate through them one by one.

Now, here is a piece of advice I give every junior dev: never use f-strings or .format() to put variables into your SQL queries. It opens you up to SQL injection attacks, which is a fancy way of saying someone could delete your entire database by typing a clever string into an input box. Always use placeholders (the ? syntax).

# The wrong way (Dangerous!)
# cursor.execute(f"INSERT INTO books (title) VALUES ('{user_input}')")

# The right way (Safe)
new_book = ("The Martian", "Andy Weir", 2011)
cursor.execute("INSERT INTO books (title, author, year) VALUES (?, ?, ?)", new_book)
connection.commit()

# Querying for a specific author
author_to_find = "Andy Weir"
cursor.execute("SELECT title FROM books WHERE author = ?", (author_to_find,))

# fetchall() returns a list of tuples
results = cursor.fetchall()
for row in results:
    print(f"Found book: {row[0]}")

connection.close()

Managing the Database Lifecycle

While you can manually call .close(), I've found that using a with statement (context manager) is much cleaner. It ensures the connection closes even if your code throws an exception halfway through. However, be careful: the default sqlite3.connect doesn't automatically commit transactions when exiting the block, so you still need to call commit() explicitly or wrap it in a custom manager.

SQLite is an incredible tool for local caching, small-scale applications, or prototyping. Once your app grows to the point where multiple users need to write to the database simultaneously from different machines, that's when you migrate to a client-server database. Until then, sqlite3 is your best friend.




📋 Practical Task

Exercise: Building a Digital Vinyl Collection Tracker

Your goal is to create a local database system to track a vinyl record collection. You need to build a script that handles the setup, data entry, and a specific search function.

Requirements:

  • Create a database file named vinyl_collection.db.
  • Create a table named records with the following columns: id (Integer Primary Key), artist (Text), album_name (Text), and release_year (Integer).
  • Write a function add_record(artist, album, year) that inserts a new record into the table using parameterized queries.
  • Write a function get_records_by_artist(artist_name) that returns a list of all album names released by a specific artist.
  • Test your code by adding three different albums (at least two by the same artist) and then printing the results of the get_records_by_artist function for that specific artist.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.