Skip to content

Releases: Foxerine/sqlmodel-ext

v0.4.0 — Enhanced AsyncSession, Decimal types, DeferredIndex

Choose a tag to compare

@Foxerine Foxerine released this 10 Jun 08:35

Sync round from the upstream foxline project (2026-05-12 → 2026-06-10).

⚠️ Breaking changes

  • safe_reset() removed — call await session.reset() on the enhanced sqlmodel_ext.AsyncSession instead (it clears FOR UPDATE lock tracking and cache-invalidation tracking automatically).
  • CachedTableBaseMixin.cache_aware_commit() / _commit_and_invalidate() / _sync_invalidate_after_commit() removed — CRUD methods no longer invalidate the cache themselves. Invalidation is now orchestrated inside AsyncSession.commit().
  • Cached-table users must switch their session factory: async_sessionmaker(engine, class_=sqlmodel_ext.AsyncSession). Plain sessions degrade to the fire-and-forget after_commit compensation hook (a brief stale-cache window). Models without CachedTableBaseMixin are unaffected.

✨ New features

  • Enhanced AsyncSession (from sqlmodel_ext import AsyncSession): cache-aware commit() (auto-registers bare session.add() / attribute-mutation / session.delete() paths and synchronously invalidates after commit), reset() with tracking-state cleanup, refresh() routed through Model.get() (Redis + STI polymorphic columns), and a raw-DML warning on execute().
  • Decimal field-type ladder: SignedDecimal38_18, NonNegativeDecimal38_18, PositiveDecimal38_18, OptionalNonNegativeDecimal38_18, SignedDecimal20_10, NonNegativeDecimal20_10, OptionalNonNegativeDecimal20_10 — float/bool input rejection, fixed-point JSON-string serialization (no 0E-18, trailing zeros stripped), null-safe nested-Annotated Optional variants, NUMERIC(p, s) column mapping.
  • DeferredIndex (+ generic CustomTableArg interception): STI base classes can declare indexes over columns that only subclasses register; materialized after STI phase 1 instead of crashing with ConstraintColumnNotFoundError.
  • New string/list aliases: Text8K, Text48K, NonEmptyStrippedStr64/128/256, BCP47LanguageCode, bounded-length List1List1024.
  • delete() @overload guard: passing neither instances nor condition is now a type-checker error, not just a runtime ValueError.

🐛 Bug fixes

  • STI ancestor cache refill: save()/update() write-through backfill now refills the cache key of the model and every cached ancestor, symmetric with invalidation. Previously only the concrete class key was refilled, so queries through an STI ancestor (e.g. a polymorphic root) could observe stale rows.
  • all_fields_optional=True constraint nesting: derived PATCH DTOs no longer crash on JSON null with Unable to apply constraint 'ge' to supplied value None; constraints (Ge/Le/max_digits/StringConstraints) nest into the inner Annotated while serializers/ORM markers stay outer, and they still apply to non-None values.

Tests

189 tests passing across Python 3.12 / 3.13 / 3.14 (31 new: Decimal invariants, all_fields_optional constraint preservation, DeferredIndex materialization, enhanced-session orchestration, expanded export smoke tests).