advanced
Step 18 of 20
Database Access
Python Programming
Database Access
Most real-world applications need to persist data beyond the lifetime of the program. Databases provide structured, reliable, and efficient storage for application data. Python includes built-in support for SQLite through the sqlite3 module and supports virtually every database system through third-party libraries. Understanding how to connect to databases, execute queries, and use an ORM (Object-Relational Mapper) like SQLAlchemy is essential for backend development, data analysis, and building any application that needs persistent storage.
SQLite with the sqlite3 Module
import sqlite3
# Connect (creates file if it doesn't exist)
conn = sqlite3.connect("myapp.db")
cursor = conn.cursor()
# Create 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
)
""")
# Insert data — always use parameterized queries to prevent SQL injection!
cursor.execute(
"INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
("Alice", "alice@example.com", 30)
)
# Insert multiple rows
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()
# Query data
cursor.execute("SELECT * FROM users WHERE age > ?", (26,))
rows = cursor.fetchall()
for row in rows:
print(f"ID: {row[0]}, Name: {row[1]}, Email: {row[2]}")
# Use Row factory for dict-like access
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
for row in cursor.fetchall():
print(f"{row['name']}: {row['email']}")
conn.close()
Context Manager Pattern
import sqlite3
def get_connection(db_path="myapp.db"):
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
return conn
# Safe database operations with context manager
def create_user(name, email, age):
with get_connection() as conn:
try:
conn.execute(
"INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
(name, email, age)
)
conn.commit()
print(f"User {name} created successfully")
except sqlite3.IntegrityError:
print(f"User with email {email} already exists")
def get_users(min_age=0):
with get_connection() as conn:
cursor = conn.execute(
"SELECT * FROM users WHERE age >= ? ORDER BY name",
(min_age,)
)
return [dict(row) for row in cursor.fetchall()]
def update_user(user_id, **kwargs):
fields = ", ".join(f"{k} = ?" for k in kwargs)
values = list(kwargs.values()) + [user_id]
with get_connection() as conn:
conn.execute(f"UPDATE users SET {fields} WHERE id = ?", values)
conn.commit()
def delete_user(user_id):
with get_connection() as conn:
conn.execute("DELETE FROM users WHERE id = ?", (user_id,))
conn.commit()
SQLAlchemy ORM
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, sessionmaker
# Setup
engine = create_engine("sqlite:///app.db", echo=False)
Base = declarative_base()
Session = sessionmaker(bind=engine)
# Define model
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
email = Column(String(200), unique=True, nullable=False)
age = Column(Integer)
def __repr__(self):
return f"User(id={self.id}, name='{self.name}')"
# Create tables
Base.metadata.create_all(engine)
# CRUD operations
session = Session()
# Create
new_user = User(name="Alice", email="alice@example.com", age=30)
session.add(new_user)
session.commit()
# Read
all_users = session.query(User).all()
user = session.query(User).filter_by(name="Alice").first()
young_users = session.query(User).filter(User.age < 30).all()
# Update
user.age = 31
session.commit()
# Delete
session.delete(user)
session.commit()
session.close()
Pro tip: Never build SQL queries by concatenating strings with user input — this creates SQL injection vulnerabilities. Always use parameterized queries (placeholders like?in sqlite3 or:paramin SQLAlchemy) or an ORM to safely handle user data in database queries.
Key Takeaways
- Python's built-in
sqlite3module provides a lightweight database that requires no server setup. - Always use parameterized queries (
?placeholders) to prevent SQL injection attacks. - Use context managers (
withstatement) to ensure database connections are properly closed. - SQLAlchemy ORM maps Python classes to database tables, allowing you to work with objects instead of raw SQL.
- Use
conn.row_factory = sqlite3.Rowfor dictionary-like access to query results.