Python's sqlite3 module

SQLite · Essentials · 14 min

sqlite3 ships with Python, so it's the quickest way to give any script or app a real database.

Python's standard library includes the sqlite3 module, so there's nothing to install.

  • con = sqlite3.connect("app.db") opens a connection (":memory:" for a throwaway database).
  • con.execute(sql, params) runs a statement and returns a cursor; fetchone(), fetchall() or iterating the cursor gets rows as tuples.
  • Always pass values as parameters: ? placeholders with a tuple, or :name placeholders with a dict. Never build SQL with f-strings — that invites SQL injection and breaks on quotes.
  • executemany(sql, rows) inserts many rows efficiently.
  • con.row_factory = sqlite3.Row returns rows you can index by column name.

Transactions: by default (the legacy mode) the module opens a transaction implicitly before INSERT, UPDATE, DELETE and REPLACE, and nothing is saved until you call con.commit(). Closing without committing discards the changes. Using the connection as a context manager, with con:, commits if the block succeeds and rolls back if it raises — but it does not close the connection. Python 3.12 added an explicit autocommit parameter that makes this behaviour configurable (next lessons).

cursor.lastrowid gives the id of the last inserted row and cursor.rowcount the number of rows changed. Use contextlib.closing(con) or call con.close() when you're done.

Example

import sqlite3

con = sqlite3.connect(":memory:")
con.row_factory = sqlite3.Row
con.execute("CREATE TABLE user (id INTEGER PRIMARY KEY, name TEXT, city TEXT)")

with con:                                        # commit on success
    cur = con.execute("INSERT INTO user (name, city) VALUES (?, ?)", ("Ani", "Yerevan"))
    print(cur.lastrowid)                         # 1
    con.executemany("INSERT INTO user (name, city) VALUES (?, ?)",
                    [("Davit", "Gyumri"), ("O'Brien", "Dublin")])   # quotes are safe

for row in con.execute("SELECT id, name FROM user WHERE city = :c", {"c": "Gyumri"}):
    print(row["id"], row["name"])                # 2 Davit

name = "x' OR '1'='1"                            # injection attempt is just data
print(con.execute("SELECT count(*) FROM user WHERE name = ?", (name,)).fetchone()[0])  # 0
con.close()

Remember

  • Use ? or :name placeholders, never string formatting
  • Changes need con.commit() (or a with con: block)
  • with con: commits or rolls back but doesn't close the connection

Watch out for

  • Forgetting to commit, so inserts silently disappear when the program exits.

Check yourself

  1. A script inserts rows with con.execute() and exits without commit() or a with block. What is saved?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/sqlite3.html