Python Database Connectivity
Python provides excellent database support through built-in and third-party libraries. Whether you need a simple local database or enterprise-grade connectivity, Python has you covered.
SQLite with sqlite3 (Built-in)
SQLite is a lightweight, file-based database included with Python β no setup required:
import sqlite3
# Connect to database (creates file if not exists)
conn = sqlite3.connect("myapp.db")
# Create a cursor
cursor = conn.cursor()
# Create a table
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
age INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.commit()
conn.close()Always use context managers or explicitly close connections to prevent data loss.
CRUD Operations
Create (INSERT)
import sqlite3
conn = sqlite3.connect("myapp.db")
cursor = conn.cursor()
# Insert a single record (use ? placeholders to prevent SQL injection)
cursor.execute(
"INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
("Alice", "alice@example.com", 30)
)
# Insert multiple records
users = [
("Bob", "bob@example.com", 25),
("Charlie", "charlie@example.com", 35),
("Diana", "diana@example.com", 28),
]
cursor.executemany(
"INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
users
)
conn.commit()
print(f"Inserted {cursor.rowcount} rows")
print(f"Last row ID: {cursor.lastrowid}")
conn.close()Never use f-strings or string formatting in SQL queries β always use parameterized queries (? placeholders) to prevent SQL injection.
Read (SELECT)
import sqlite3
conn = sqlite3.connect("myapp.db")
cursor = conn.cursor()
# Fetch all rows
cursor.execute("SELECT * FROM users")
all_users = cursor.fetchall()
for user in all_users:
print(user) # Tuple: (1, 'Alice', 'alice@example.com', 30, '2024-...')
# Fetch one row
cursor.execute("SELECT * FROM users WHERE id = ?", (1,))
user = cursor.fetchone()
print(user)
# Fetch with conditions
cursor.execute("SELECT name, age FROM users WHERE age > ?", (25,))
results = cursor.fetchall()
for name, age in results:
print(f"{name}: {age} years old")
# Use Row factory for dict-like access
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = ?", (1,))
user = cursor.fetchone()
print(user["name"]) # Alice
print(user["email"]) # alice@example.com
print(dict(user)) # Full dictionary
conn.close()Update
import sqlite3
conn = sqlite3.connect("myapp.db")
cursor = conn.cursor()
# Update a record
cursor.execute(
"UPDATE users SET age = ? WHERE name = ?",
(31, "Alice")
)
conn.commit()
print(f"Updated {cursor.rowcount} rows")
conn.close()Delete
import sqlite3
conn = sqlite3.connect("myapp.db")
cursor = conn.cursor()
# Delete a record
cursor.execute("DELETE FROM users WHERE id = ?", (1,))
conn.commit()
print(f"Deleted {cursor.rowcount} rows")
conn.close()Using Context Managers
import sqlite3
# Automatic commit/rollback
with sqlite3.connect("myapp.db") as conn:
cursor = conn.cursor()
try:
cursor.execute(
"INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
("Eve", "eve@example.com", 22)
)
# Auto-commits on success
except sqlite3.IntegrityError as e:
# Auto-rollbacks on error
print(f"Database error: {e}")Database Helper Class
import sqlite3
from contextlib import contextmanager
class Database:
"""Simple SQLite database wrapper."""
def __init__(self, db_path):
self.db_path = db_path
@contextmanager
def get_connection(self):
"""Get a database connection with auto-commit."""
conn = sqlite3.connect(self.db_path)
conn.row_factory = sqlite3.Row
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()
def execute(self, query, params=None):
"""Execute a query and return results."""
with self.get_connection() as conn:
cursor = conn.cursor()
cursor.execute(query, params or ())
return cursor.fetchall()
def execute_one(self, query, params=None):
"""Execute a query and return one result."""
with self.get_connection() as conn:
cursor = conn.cursor()
cursor.execute(query, params or ())
return cursor.fetchone()
def execute_write(self, query, params=None):
"""Execute a write query and return affected rows."""
with self.get_connection() as conn:
cursor = conn.cursor()
cursor.execute(query, params or ())
return cursor.rowcount
# Usage
db = Database("myapp.db")
# Query
users = db.execute("SELECT * FROM users WHERE age > ?", (25,))
for user in users:
print(f"{user['name']}: {user['age']}")
# Insert
db.execute_write(
"INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
("Frank", "frank@example.com", 40)
)
# Single result
user = db.execute_one("SELECT * FROM users WHERE id = ?", (1,))
if user:
print(dict(user))MySQL and PostgreSQL
For production databases, use dedicated drivers:
MySQL
# Install: pip install mysql-connector-python
import mysql.connector
conn = mysql.connector.connect(
host="localhost",
user="root",
password="password",
database="myapp"
)
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM users WHERE age > %s", (25,))
results = cursor.fetchall()
for row in results:
print(f"{row['name']}: {row['email']}")
conn.close()PostgreSQL
# Install: pip install psycopg2-binary
import psycopg2
from psycopg2.extras import RealDictCursor
conn = psycopg2.connect(
host="localhost",
dbname="myapp",
user="postgres",
password="password"
)
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute("SELECT * FROM users WHERE age > %s", (25,))
results = cursor.fetchall()
for row in results:
print(f"{row['name']}: {row['email']}")
conn.commit()
conn.close()SQLAlchemy ORM
SQLAlchemy provides a powerful Object-Relational Mapping (ORM) layer:
# Install: pip install sqlalchemy
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.orm import declarative_base, sessionmaker
from datetime import datetime
# Setup
engine = create_engine("sqlite:///myapp.db", echo=False)
Base = declarative_base()
Session = sessionmaker(bind=engine)
# Define model
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False)
email = Column(String(200), unique=True, nullable=False)
age = Column(Integer)
created_at = Column(DateTime, default=datetime.utcnow)
def __repr__(self):
return f"<User(name='{self.name}', email='{self.email}')>"
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"email": self.email,
"age": self.age,
}
# Create tables
Base.metadata.create_all(engine)ORM CRUD Operations
# Create
session = Session()
user = User(name="Alice", email="alice@example.com", age=30)
session.add(user)
session.commit()
print(f"Created: {user}")
# Bulk create
users = [
User(name="Bob", email="bob@example.com", age=25),
User(name="Charlie", email="charlie@example.com", age=35),
]
session.add_all(users)
session.commit()
# Read
all_users = session.query(User).all()
for u in all_users:
print(u.to_dict())
# Filter
young_users = session.query(User).filter(User.age < 30).all()
alice = session.query(User).filter_by(name="Alice").first()
# Update
alice.age = 31
session.commit()
# Delete
session.delete(alice)
session.commit()
session.close()Practical Example: Task Manager
"""
Simple task manager using SQLite.
"""
import sqlite3
from datetime import datetime
class TaskManager:
def __init__(self, db_path="tasks.db"):
self.db_path = db_path
self._init_db()
def _init_db(self):
with sqlite3.connect(self.db_path) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
priority TEXT DEFAULT 'medium',
completed BOOLEAN DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP
)
""")
def add_task(self, title, description="", priority="medium"):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.execute(
"INSERT INTO tasks (title, description, priority) VALUES (?, ?, ?)",
(title, description, priority)
)
return cursor.lastrowid
def complete_task(self, task_id):
with sqlite3.connect(self.db_path) as conn:
conn.execute(
"UPDATE tasks SET completed = 1, completed_at = ? WHERE id = ?",
(datetime.now().isoformat(), task_id)
)
def get_tasks(self, show_completed=False):
with sqlite3.connect(self.db_path) as conn:
conn.row_factory = sqlite3.Row
if show_completed:
query = "SELECT * FROM tasks ORDER BY priority, created_at"
else:
query = "SELECT * FROM tasks WHERE completed = 0 ORDER BY priority, created_at"
return conn.execute(query).fetchall()
def delete_task(self, task_id):
with sqlite3.connect(self.db_path) as conn:
conn.execute("DELETE FROM tasks WHERE id = ?", (task_id,))
def show_tasks(self, show_completed=False):
tasks = self.get_tasks(show_completed)
if not tasks:
print("No tasks found!")
return
priority_icons = {"high": "π΄", "medium": "π‘", "low": "π’"}
print(f"\n{'ID':<4} {'Status':<8} {'Priority':<10} {'Title'}")
print("-" * 50)
for task in tasks:
status = "β
" if task["completed"] else "β¬"
icon = priority_icons.get(task["priority"], "βͺ")
print(f"{task['id']:<4} {status:<8} {icon} {task['priority']:<7} {task['title']}")
# Usage
tm = TaskManager()
tm.add_task("Learn Python databases", "Study sqlite3 and SQLAlchemy", "high")
tm.add_task("Build API client", "Create reusable HTTP client", "medium")
tm.add_task("Write unit tests", priority="low")
tm.show_tasks()
tm.complete_task(1)
tm.show_tasks(show_completed=True)Summary
- SQLite (
sqlite3) is built into Python β great for development and small apps - Always use parameterized queries (
?placeholders) to prevent SQL injection - Use context managers for automatic connection management
row_factory = sqlite3.Rowenables dict-like access to results- MySQL: use
mysql-connector-python; PostgreSQL: usepsycopg2 - SQLAlchemy ORM provides a Pythonic way to interact with any database
- Build wrapper classes for clean, reusable database code
- Always commit after write operations and close connections
Congratulations! You've completed all the core Python topics. Continue practicing by building projects and exploring Python's vast ecosystem of libraries!