Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
285: Using sqlite3 for Local Databases
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
recordswith the following columns:id(Integer Primary Key),artist(Text),album_name(Text), andrelease_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_artistfunction for that specific artist.
There are no comments for now.