SQLAlchemy Cheatsheet
Section titled “SQLAlchemy Cheatsheet”Installation & Setup
Section titled “Installation & Setup”Install
Section titled “Install”pip install sqlalchemypip install alembic # For migrationsBasic Imports
Section titled “Basic Imports”from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, DateTime, Boolean, Text, Float, Numericfrom sqlalchemy.orm import declarative_base, sessionmaker, relationship, Sessionfrom sqlalchemy import select, update, delete, insert, func, and_, or_, not_from datetime import datetimeEngine & Session Setup
Section titled “Engine & Session Setup”# SQLiteengine = create_engine("sqlite:///example.db", echo=True)
# PostgreSQLengine = create_engine("postgresql://user:password@localhost/dbname")
# MySQLengine = create_engine("mysql+pymysql://user:password@localhost/dbname")
# Session factorySessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
# Create sessionsession = SessionLocal()Declarative Base & Models
Section titled “Declarative Base & Models”Base = declarative_base()
class User(Base): __tablename__ = "users"
id = Column(Integer, primary_key=True, index=True) name = Column(String(100), nullable=False) email = Column(String(255), unique=True, nullable=False, index=True) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)Create Tables
Section titled “Create Tables”Base.metadata.create_all(engine)Basic CRUD Operations
Section titled “Basic CRUD Operations”Insert (Create)
Section titled “Insert (Create)”# Single insertuser = User(name="Alice", email="alice@example.com")session.add(user)session.commit()session.refresh(user) # Get updated data from DB
# Bulk insertusers = [ User(name="Bob", email="bob@example.com"), User(name="Charlie", email="charlie@example.com")]session.add_all(users)session.commit()
# Insert with returning (2.0 style)stmt = insert(User).values(name="Dave", email="dave@example.com").returning(User)result = session.execute(stmt)new_user = result.scalar_one()session.commit()Read (Query)
Section titled “Read (Query)”# Get allusers = session.query(User).all()
# Get by IDuser = session.query(User).get(1)user = session.query(User).filter_by(id=1).first()
# Get one (raises if not found or multiple)user = session.query(User).filter_by(email="alice@example.com").one()
# Get firstuser = session.query(User).first()
# Using select() (2.0 style)stmt = select(User).where(User.id == 1)user = session.execute(stmt).scalar_one()
# Scalar resultsstmt = select(User)users = session.execute(stmt).scalars().all()Update
Section titled “Update”# Update single objectuser = session.query(User).filter_by(id=1).first()user.email = "newemail@example.com"session.commit()
# Bulk updatesession.query(User).filter(User.is_active == False).update({"is_active": True})session.commit()
# Update with returning (2.0 style)stmt = ( update(User) .where(User.id == 1) .values(email="updated@example.com") .returning(User))result = session.execute(stmt)session.commit()Delete
Section titled “Delete”# Delete single objectuser = session.query(User).filter_by(id=1).first()session.delete(user)session.commit()
# Bulk deletesession.query(User).filter(User.is_active == False).delete()session.commit()
# Delete with 2.0 stylestmt = delete(User).where(User.id == 1)session.execute(stmt)session.commit()Querying
Section titled “Querying”Basic Filtering
Section titled “Basic Filtering”# filter_by (keyword arguments)users = session.query(User).filter_by(name="Alice").all()
# filter (expressions)users = session.query(User).filter(User.name == "Alice").all()
# Multiple conditionsusers = session.query(User).filter( User.name == "Alice", User.is_active == True).all()Ordering
Section titled “Ordering”# Ascendingusers = session.query(User).order_by(User.name.asc()).all()
# Descendingusers = session.query(User).order_by(User.created_at.desc()).all()
# Multiple ordersusers = session.query(User).order_by( User.name.asc(), User.created_at.desc()).all()
# Nulls first/lastusers = session.query(User).order_by(User.name.asc().nulls_last()).all()Limiting & Offsetting
Section titled “Limiting & Offsetting”# Limitusers = session.query(User).limit(10).all()
# Offsetusers = session.query(User).offset(20).all()
# Paginationpage = 2per_page = 10users = session.query(User).limit(per_page).offset((page - 1) * per_page).all()
# Slice notationusers = session.query(User)[10:20] # Items 10-19Distinct
Section titled “Distinct”names = session.query(User.name).distinct().all()Relationships
Section titled “Relationships”One-to-Many
Section titled “One-to-Many”class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) name = Column(String) addresses = relationship("Address", back_populates="user", cascade="all, delete-orphan")
class Address(Base): __tablename__ = "addresses" id = Column(Integer, primary_key=True) email = Column(String) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE")) user = relationship("User", back_populates="addresses")Many-to-Many
Section titled “Many-to-Many”from sqlalchemy import Table
user_group = Table( 'user_group', Base.metadata, Column('user_id', Integer, ForeignKey('users.id')), Column('group_id', Integer, ForeignKey('groups.id')))
class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) groups = relationship("Group", secondary=user_group, back_populates="users")
class Group(Base): __tablename__ = "groups" id = Column(Integer, primary_key=True) users = relationship("User", secondary=user_group, back_populates="groups")One-to-One
Section titled “One-to-One”class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) profile = relationship("Profile", back_populates="user", uselist=False)
class Profile(Base): __tablename__ = "profiles" id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey("users.id"), unique=True) user = relationship("User", back_populates="profile")Advanced Queries
Section titled “Advanced Queries”Select with Complex Filtering, Joins, and Ordering
Section titled “Select with Complex Filtering, Joins, and Ordering”from sqlalchemy import selectfrom sqlalchemy.orm import selectinload, joinedload
# Complex query with eager loadingstmt = ( select(User) .where( User.is_active == True, User.created_at >= datetime(2024, 1, 1) ) .options( selectinload(User.addresses), # Separate query joinedload(User.profile) # JOIN ) .order_by( User.created_at.desc().nulls_last(), User.name.asc() ) .limit(100))users = session.execute(stmt).unique().scalars().all()
# Chained eager loadingstmt = ( select(Document) .options( selectinload(Document.source).selectinload(Source.items) ))Subqueries
Section titled “Subqueries”from sqlalchemy import select
# Scalar subquerysubq = select(func.avg(User.age)).scalar_subquery()query = session.query(User).filter(User.age > subq)
# Subquery as tablesubq = select(Address.user_id, func.count().label("address_count")).group_by(Address.user_id).subquery()query = session.query(User, subq.c.address_count).join(subq, User.id == subq.c.user_id)Common Table Expressions (CTE)
Section titled “Common Table Expressions (CTE)”# Recursive CTEcte = select(User.id, User.name, User.manager_id).cte(recursive=True)
cte = cte.union_all( select(User.id, User.name, User.manager_id) .join(cte, User.manager_id == cte.c.id))
stmt = select(cte)results = session.execute(stmt).all()Window Functions
Section titled “Window Functions”from sqlalchemy import over
# Row numberrow_num = func.row_number().over(order_by=User.created_at).label('row_num')query = session.query(User, row_num)
# Partition byrank = func.rank().over( partition_by=User.department_id, order_by=User.salary.desc()).label('salary_rank')query = session.query(User, rank)Filtering & Conditions
Section titled “Filtering & Conditions”Comparison Operators
Section titled “Comparison Operators”# Equalusers = session.query(User).filter(User.name == "Alice").all()
# Not equalusers = session.query(User).filter(User.name != "Alice").all()
# Greater than, Less thanusers = session.query(User).filter(User.age > 18).all()users = session.query(User).filter(User.age <= 65).all()
# INusers = session.query(User).filter(User.id.in_([1, 2, 3])).all()
# NOT INusers = session.query(User).filter(~User.id.in_([1, 2, 3])).all()
# IS NULLusers = session.query(User).filter(User.email.is_(None)).all()
# IS NOT NULLusers = session.query(User).filter(User.email.isnot(None)).all()
# LIKEusers = session.query(User).filter(User.name.like("%alice%")).all()
# ILIKE (case-insensitive)users = session.query(User).filter(User.name.ilike("%alice%")).all()
# BETWEENusers = session.query(User).filter(User.age.between(18, 65)).all()Logical Operators
Section titled “Logical Operators”from sqlalchemy import and_, or_, not_
# ANDusers = session.query(User).filter( and_(User.is_active == True, User.age > 18)).all()
# ORusers = session.query(User).filter( or_(User.name == "Alice", User.name == "Bob")).all()
# NOTusers = session.query(User).filter( not_(User.is_active == False)).all()
# Combinedusers = session.query(User).filter( and_( User.is_active == True, or_(User.age > 18, User.verified == True) )).all()Aggregations & Functions
Section titled “Aggregations & Functions”# Count allcount = session.query(User).count()count = session.query(func.count(User.id)).scalar()
# Count with filtercount = session.query(User).filter(User.is_active == True).count()
# Count distinctcount = session.query(func.count(User.name.distinct())).scalar()Sum, Avg, Min, Max
Section titled “Sum, Avg, Min, Max”# Sumtotal = session.query(func.sum(Order.total)).scalar()
# Averageavg_age = session.query(func.avg(User.age)).scalar()
# Min/Maxmin_age = session.query(func.min(User.age)).scalar()max_age = session.query(func.max(User.age)).scalar()Group By
Section titled “Group By”# Group by with aggregatesresults = session.query( User.department, func.count(User.id).label("employee_count"), func.avg(User.salary).label("avg_salary")).group_by(User.department).all()
# Having clauseresults = session.query( User.department, func.count(User.id).label("count")).group_by(User.department).having(func.count(User.id) > 5).all()String Functions
Section titled “String Functions”# Concatenationfull_name = func.concat(User.first_name, ' ', User.last_name).label('full_name')
# Lower/Upperlower_name = func.lower(User.name)upper_name = func.upper(User.name)
# Lengthname_length = func.length(User.name)Date Functions
Section titled “Date Functions”# Current timestampfrom sqlalchemy import func
now = func.now()current_date = func.current_date()
# Date partsyear = func.extract('year', User.created_at)month = func.extract('month', User.created_at)
# Date arithmetic (PostgreSQL)future_date = User.created_at + func.interval('1 day')Joins & Eager Loading
Section titled “Joins & Eager Loading”Explicit Joins
Section titled “Explicit Joins”# Inner joinresults = session.query(User).join(Address).all()
# Left outer joinresults = session.query(User).outerjoin(Address).all()
# Join with conditionresults = session.query(User).join( Address, and_(User.id == Address.user_id, Address.is_primary == True)).all()
# Multiple joinsresults = session.query(User).join(Address).join(City).all()Eager Loading Strategies
Section titled “Eager Loading Strategies”selectinload (Separate SELECT)
Section titled “selectinload (Separate SELECT)”from sqlalchemy.orm import selectinload
# Loads related objects with separate SELECT IN queryusers = session.query(User).options(selectinload(User.addresses)).all()
# Chainedusers = session.query(User).options( selectinload(User.orders).selectinload(Order.items)).all()joinedload (JOIN)
Section titled “joinedload (JOIN)”from sqlalchemy.orm import joinedload
# Loads related objects using LEFT OUTER JOINusers = session.query(User).options(joinedload(User.profile)).all()
# Multiple relationshipsusers = session.query(User).options( joinedload(User.profile), selectinload(User.addresses)).all()subqueryload
Section titled “subqueryload”from sqlalchemy.orm import subqueryload
# Loads related objects with subqueryusers = session.query(User).options(subqueryload(User.addresses)).all()raiseload (Prevent N+1)
Section titled “raiseload (Prevent N+1)”from sqlalchemy.orm import raiseload
# Raises error if lazy loading attemptedusers = session.query(User).options(raiseload(User.addresses)).all()Transactions
Section titled “Transactions”Basic Transaction
Section titled “Basic Transaction”try: user = User(name="Alice") session.add(user) session.commit()except Exception as e: session.rollback() raisefinally: session.close()Context Manager
Section titled “Context Manager”from contextlib import contextmanager
@contextmanagerdef get_db(): db = SessionLocal() try: yield db db.commit() except: db.rollback() raise finally: db.close()
# Usagewith get_db() as db: user = User(name="Alice") db.add(user)Savepoints
Section titled “Savepoints”session.begin_nested() # Create savepointtry: # ... operations ... session.commit() # Commit savepointexcept: session.rollback() # Rollback to savepointManual Transaction Control
Section titled “Manual Transaction Control”# Begin transaction explicitlysession.begin()
try: # ... operations ... session.commit()except: session.rollback()Session Management
Section titled “Session Management”Session Lifecycle
Section titled “Session Lifecycle”# Create sessionsession = SessionLocal()
# Use sessionuser = session.query(User).first()
# Close sessionsession.close()
# Check if object is in sessionfrom sqlalchemy.orm import object_sessionsession = object_session(user)Session States
Section titled “Session States”from sqlalchemy.inspect import inspect
# Check object stateinsp = inspect(user)print(insp.persistent) # In session and has database identityprint(insp.pending) # In session, no database identityprint(insp.transient) # Not in sessionprint(insp.detached) # Has identity but not in sessionExpiring & Refreshing
Section titled “Expiring & Refreshing”# Expire object (reload on next access)session.expire(user)
# Expire all objectssession.expire_all()
# Refresh from databasesession.refresh(user)
# Merge detached objectdetached_user = User(id=1, name="Updated")merged_user = session.merge(detached_user)Flush vs Commit
Section titled “Flush vs Commit”# Flush: Send SQL to database, no commitsession.add(user)session.flush() # SQL executed, transaction still open
# Commit: Flush + commit transactionsession.commit()Additional Tips
Section titled “Additional Tips”Raw SQL
Section titled “Raw SQL”# Execute raw SQLresult = session.execute("SELECT * FROM users WHERE name = :name", {"name": "Alice"})for row in result: print(row)
# Use text()from sqlalchemy import textresult = session.execute(text("SELECT * FROM users WHERE name = :name"), {"name": "Alice"})Bulk Operations
Section titled “Bulk Operations”# Bulk insert (bypasses ORM)session.bulk_insert_mappings(User, [ {"name": "Alice", "email": "alice@example.com"}, {"name": "Bob", "email": "bob@example.com"}])
# Bulk updatesession.bulk_update_mappings(User, [ {"id": 1, "name": "Alice Updated"}, {"id": 2, "name": "Bob Updated"}])Inspect Database
Section titled “Inspect Database”from sqlalchemy import inspect
inspector = inspect(engine)
# Get table namestables = inspector.get_table_names()
# Get columnscolumns = inspector.get_columns('users')
# Get foreign keysfks = inspector.get_foreign_keys('addresses')