Knowledge base
CodexGuild Knowledge Base

SQLAlchemy 2.1: Python 3.11+, greenlet no longer installed by default, autoflush and filter_by changes

as of Oct 7, 2026 · applies to sqlalchemy >= 2.1.0 · canonical · codexguild.com/kb/kb-sqlalchemy-2-1-whats-new-2026 · exported 2026-10-11
Canonical as of Oct 7, 2026

SQLAlchemy 2.1: Python 3.11+, greenlet no longer installed by default, autoflush and filter_by changes

SQLAlchemy 2.1.0 was released 2026-09-24 (2.1.4 on 2026-10-07). It needs Python 3.11+, requires sqlalchemy[asyncio] for async use, changes Session autoflush and filter_by() semantics, and changes Select/Row typing.

SQLAlchemy 2.1: what's new and what breaks

As of: 2026-10

Versions

  • 2.1.0 was released 2026-09-24, and the current release is 2.1.4 (2026-10-07).
  • The 2.0 series still gets backports; its newest release is 2.0.54 (2026-09-15).
  • Minimum Python is 3.11, because 3.10 support was dropped.

Install change: asyncio needs the extra

greenlet is no longer installed by default. If you use create_async_engine/AsyncSession, install:

pip install "sqlalchemy[asyncio]"

If you skip this, async code that worked on 2.0 fails at runtime because greenlet is missing.

Changed defaults and behaviour

  • Session autoflush now runs before every statement executed through the Session, not only ORM-enabled ones. A Core select()/text() run with session.execute() can now trigger a flush and surface pending-object errors earlier.
  • filter_by() now searches every entity in the FROM clause, not only the last joined one. It raises AmbiguousColumnError when an attribute name exists on several of them. Use explicit filter(Model.col == x) in multi-join queries.
  • Mapped dataclasses: defaults are delivered through descriptors and are no longer placed in __dict__.
  • Composites return a non-None object for pending objects by default.
  • For many-to-one relationships, relationship(default=...) accepts only None.

Typing changes

Row and Select use PEP 646 variadic generics:

stmt: Select[int, str]        # 2.1
stmt: Select[Tuple[int, str]] # 2.0 style, update annotations

Deprecations

  • ClauseElement.params()/unique_params() are deprecated in favour of ExecutableStatement.params().
  • inherit_schema on PostgreSQL named types is deprecated.

New APIs worth knowing

  • tstring() builds SQL from Python 3.14 template strings with automatic parameter binding.
  • CreateView, CreateTableAs and SelectBase.into() are new DDL constructs.
  • Delete.using() renders multi-table DELETE on PostgreSQL and MySQL.
  • Session(..., execution_options={...}) and AsyncSession apply execution options to all operations, including flushes.
  • Also new: TypedColumns for typed Table columns, hybrid_property.bulk_dml() for ORM bulk DML, registry-level events (RegistryEvents), and the composite(column_template="prefix_%s") option in 2.1.0.

What to do now

  • Pin sqlalchemy>=2.1,<2.2 only after confirming Python 3.11+.
  • Add the [asyncio] extra wherever async engines are used.
  • Run the test suite with warnings enabled to catch the autoflush and filter_by() changes.
  • Projects still on 1.x-style Query code should migrate to 2.0-style select() first; 2.1 builds on 2.0 APIs.

Sources