SQLAlchemy
database querying
Python programming
database tutorial
ORM integration

How to query database by id using SqlAlchemy?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Querying a row by its primary key is one of the most common ORM operations, and SQLAlchemy gives it a dedicated API for a reason. If you know the record's id, the modern answer is usually Session.get(), not a generic filtered query.

The Modern Way: Session.get()

In current SQLAlchemy ORM usage, Session.get() is the primary-key lookup method. It returns the mapped object if found, or None if the row does not exist.

python
1from sqlalchemy import String, create_engine
2from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
3
4
5class Base(DeclarativeBase):
6    pass
7
8
9class User(Base):
10    __tablename__ = "users"
11
12    id: Mapped[int] = mapped_column(primary_key=True)
13    name: Mapped[str] = mapped_column(String(50))
14
15
16engine = create_engine("sqlite:///app.db", echo=False)
17Base.metadata.create_all(engine)
18
19with Session(engine) as session:
20    user = session.get(User, 1)
21
22    if user is None:
23        print("No user with id 1")
24    else:
25        print(user.name)

This is the clearest option when you are retrieving by primary key and only need one row.

Why Session.get() Is Preferred

SQLAlchemy's session maintains an identity map for ORM objects keyed by primary key. Because of that, Session.get() can return an already-loaded object directly when it is present in the session, instead of always constructing a new SELECT path the way a generic query does.

That makes the method both semantically correct and potentially more efficient. It also communicates intent clearly to anyone reading the code: this is a primary-key fetch, not an arbitrary filter.

Older Query Style Versus Current Style

You may still see older examples like this:

python
user = session.query(User).get(1)

That pattern belongs to the legacy query API. The equivalent modern style is:

python
user = session.get(User, 1)

If you are writing new code, use Session.get().

Querying by a Different Column

If the value you have is not the primary key, then Session.get() is not the right tool. In that case, use select() and a filter condition.

python
1from sqlalchemy import select
2
3with Session(engine) as session:
4    stmt = select(User).where(User.name == "Alice")
5    user = session.scalar(stmt)
6
7    if user:
8        print(user.id, user.name)

This distinction matters. Session.get() is strictly for primary key identity, while select() handles general queries.

Composite Primary Keys

If the mapped table has a composite primary key, pass a tuple or dictionary of primary-key values.

python
1from sqlalchemy.orm import Mapped, mapped_column
2
3
4class Membership(Base):
5    __tablename__ = "memberships"
6
7    user_id: Mapped[int] = mapped_column(primary_key=True)
8    team_id: Mapped[int] = mapped_column(primary_key=True)
9    role: Mapped[str] = mapped_column(String(20))
10
11
12with Session(engine) as session:
13    membership = session.get(Membership, (10, 3))
14    print(membership.role if membership else "not found")

The ordering in the tuple follows the mapped primary-key column order.

Handling Missing Rows Cleanly

Because Session.get() returns None when nothing matches, the usual handling pattern is straightforward:

python
1with Session(engine) as session:
2    user = session.get(User, 999)
3    if user is None:
4        raise LookupError("User not found")

This is cleaner than forcing a list result and checking whether the list is empty.

A Reusable Helper Function

If you query by id in several places, wrapping the pattern is reasonable:

python
1def get_user_by_id(session: Session, user_id: int) -> User | None:
2    return session.get(User, user_id)
3
4
5with Session(engine) as session:
6    user = get_user_by_id(session, 5)
7    print(user.name if user else "missing")

The helper remains simple because Session.get() already matches the use case precisely.

Common Pitfalls

One common mistake is using .all() for a primary-key lookup. That returns a list, adds unnecessary work, and makes the result shape harder to reason about.

Another mistake is using Session.get() for non-primary-key columns. If you only know an email address or username, switch to select() with a filter.

A third pitfall is copying old examples that use query.get(). They may still appear in older tutorials, but the current ORM API centers on Session.get().

Session scope is another source of confusion. Session.get() can reuse an object already present in the session's identity map, but that benefit only exists within the same session lifecycle.

Summary

  • Use session.get(Model, primary_key) for primary-key lookups in modern SQLAlchemy
  • 'Session.get() returns one mapped object or None'
  • Prefer select() with filters when the lookup column is not the primary key
  • Legacy query.get() examples should be updated to Session.get()
  • Composite primary keys can be passed as tuples or dictionaries

Course illustration
Course illustration

All Rights Reserved.