Placeholders

Use ? — never an f-string for SQL.

Pass values as a tuple to ?. Never build SQL with an f-string from user text.

Goal

Select Nairobi with a placeholder, then show why string concat is unsafe.

import sqlite3
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE t (city TEXT)")
con.execute("INSERT INTO t VALUES (?)", ("Nairobi",))
city = "Nairobi"
print(con.execute("SELECT * FROM t WHERE city = ?", (city,)).fetchall())
con.close()
import sqlite3
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE t (city TEXT)")
con.executemany("INSERT INTO t VALUES (?)", [("Nairobi",), ("Mombasa",)])
print(con.execute("SELECT * FROM t WHERE city IN (?, ?)", ("Nairobi", "Kisumu")).fetchall())
con.close()
import sqlite3
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE t (n INTEGER)")
con.execute("INSERT INTO t VALUES (:n)", {"n": 12})
print(con.execute("SELECT * FROM t").fetchall())
con.close()
city = "Nairobi'; DROP TABLE t; --"
print("would become", f"SELECT * FROM t WHERE city = '{city}'")
Pitfall

The last block only prints the dangerous string. It does not run it.