SQLAlchemy
flush vs commit
database operations
Python ORM
session management

SQLAlchemy What's the difference between flush and commit?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

In SQLAlchemy, flush() and commit() are related, but they do different jobs. flush() sends pending SQL changes from the session to the database inside the current transaction, while commit() finalizes that transaction and makes the changes durable.

The easiest way to remember the difference is this: flush writes now, commit finishes now. A flush can still be rolled back. A commit ends the transaction.

What flush() Does

The ORM session keeps track of pending inserts, updates, and deletes in memory. When you call flush(), SQLAlchemy emits the necessary SQL so the database state inside the current transaction matches the in-memory objects.

That means flush() can:

  • execute INSERT, UPDATE, and DELETE
  • populate database-generated primary keys
  • trigger database constraints before commit
  • make later ORM queries in the same session consistent with pending work

Here is a minimal example:

python
1from sqlalchemy import create_engine, Integer, String
2from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
3
4
5engine = create_engine("sqlite:///flush_demo.db", echo=False)
6
7
8class Base(DeclarativeBase):
9    pass
10
11
12class User(Base):
13    __tablename__ = "users"
14    id: Mapped[int] = mapped_column(Integer, primary_key=True)
15    name: Mapped[str] = mapped_column(String(50))
16
17
18Base.metadata.create_all(engine)
19
20with Session(engine) as session:
21    user = User(name="Ada")
22    session.add(user)
23
24    print("before flush:", user.id)
25    session.flush()
26    print("after flush:", user.id)
27
28    session.rollback()

Before the flush, user.id is usually None. After the flush, SQLAlchemy has already executed the INSERT, so the generated primary key is available.

What commit() Does

commit() ends the current transaction. In SQLAlchemy, that includes an automatic flush first if there are pending changes.

So this sequence:

python
session.add(user)
session.commit()

behaves roughly like this:

python
session.add(user)
session.flush()
transaction.commit()

The important difference is durability and visibility. After commit, the transaction is complete. Other database sessions can now observe the committed changes according to the database isolation rules.

Visibility: Same Session vs Other Sessions

A flushed row is usually visible to the current session because it is part of that session's ongoing transaction. It is not necessarily visible to another session until commit.

This example demonstrates the difference:

python
1from sqlalchemy import create_engine, Integer, String, select
2from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
3
4
5engine = create_engine("sqlite:///flush_vs_commit.db", echo=False)
6
7
8class Base(DeclarativeBase):
9    pass
10
11
12class Item(Base):
13    __tablename__ = "items"
14    id: Mapped[int] = mapped_column(Integer, primary_key=True)
15    name: Mapped[str] = mapped_column(String(50))
16
17
18Base.metadata.create_all(engine)
19
20with Session(engine) as s1, Session(engine) as s2:
21    s1.add(Item(name="draft"))
22    s1.flush()
23
24    print("session 1:", s1.scalars(select(Item.name)).all())
25    print("session 2 before commit:", s2.scalars(select(Item.name)).all())
26
27    s1.commit()
28    print("session 2 after commit:", s2.scalars(select(Item.name)).all())

The first session sees the row after flush because it is part of its own transaction. The second session does not reliably see it until the first session commits.

Why Manual Flush Is Useful

Even though commit() performs a flush automatically, explicit flush() calls are still useful.

Common reasons:

  • you need a generated primary key before commit
  • you want database constraint failures to happen early
  • you are building related objects and need foreign keys now
  • you want to send SQL but keep the transaction open for more work

For example:

python
1with Session(engine) as session:
2    parent = User(name="parent")
3    session.add(parent)
4    session.flush()
5
6    print(parent.id)
7    # create dependent rows here using parent.id
8
9    session.commit()

That pattern is very common in service code.

SQLAlchemy also has autoflush, which means the session may flush automatically before certain queries so query results stay consistent with pending changes.

That does not mean every operation commits. Autoflush still happens inside the current transaction.

You can disable it temporarily if needed:

python
with session.no_autoflush:
    # build objects without triggering an early flush
    ...

But disabling autoflush should be deliberate. It is usually there to prevent subtle read-after-write inconsistencies within the session.

Common Pitfalls

The most common mistake is thinking flush() permanently saves data. It does not. A later rollback can still undo flushed work.

Another mistake is thinking commit() only commits and never flushes. In practice, commit() performs a flush first when needed.

People also get confused when primary keys appear before commit. That is normal because a flush may already have inserted the row.

Finally, if a flush fails because of a constraint violation, the session is no longer in a clean transaction state. You usually need to call rollback() before continuing to use that session.

Summary

  • 'flush() sends pending SQL to the database inside the current transaction.'
  • 'commit() finalizes the transaction and makes the changes durable.'
  • A flush can still be rolled back.
  • 'commit() normally performs a flush automatically first.'
  • Manual flush is useful when you need generated keys or early constraint checks.
  • Flushed changes are usually visible in the same session before they are visible to other sessions.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.