Changelog:
Highlights
#[document] fields. Store an embedded struct in one document column without giving up typed queries into its fields. PostgreSQL uses JSONB, MySQL uses JSON, SQLite and Turso use JSON text, and DynamoDB uses a map. The same generated path works across backends:
#[derive(Model)]
struct User {
#[document]
address: Address,
// ... other fields
}
let users = User::filter(
User::fields().address().city().eq("Seattle"),
)
.exec(&mut db)
.await?;Atomic upserts. #[derive(Model)] now generates upsert_by_* builders for primary keys and unique constraints. An upsert can share assignments across both branches, set create- and update-specific values, apply mutations such as increment(), or use or_ignore():
let user = User::upsert_by_email("alice@example.com")
.on_create(|user| user.name("Alice").login_count(0))
.on_update(|user| {
user.login_count(toasty::stmt::increment())
})
.exec(&mut db)
.await?;PostgreSQL, SQLite, and Turso support the complete API. DynamoDB supports primary-key forms; MySQL rejects upserts because its conflict behavior cannot preserve Toasty's targeted semantics. Read about upserts.
Dynamic and native JSON fields. Models can use serde_json::Value directly for payloads whose structure is not known to Toasty. Both serde_json::Value and toasty::Json<T> can select text, PostgreSQL JSONB, or native JSON storage:
#[column(type = jsonb)]
payload: serde_json::Value,Use #[document] when Toasty should generate typed paths into a fixed structure; use JSON fields for opaque or dynamic values that are read and replaced as a whole. Read about JSON encoding.
Filtered and ordered includes. Association paths passed to .include() now accept .filter() and .order_by(), so applications can preload exactly the related records they need in the required order:
let users = User::all()
.include(
User::fields()
.todos()
.filter(Todo::fields().complete().eq(false))
.order_by(Todo::fields().priority().desc()),
)
.exec(&mut db)
.await?;Turso Sync. The Turso driver can connect a local database to Turso Cloud or a local sync server. Applications configure authentication and bootstrap behavior on the driver, then explicitly call push() and pull() when they want to exchange changes. Read about Turso Sync.
Embedded enum improvements. Enum variants can declare explicitly shared fields with #[shared(name)] and define enum-level indexes or unique constraints over those columns. Embedded enums also support rename_all, explicit integer discriminants, and scalar storage for unit enums.
Added
- support order_by in includes (#1109)
- relation link/unlink return a builder instead of executing eagerly (#1118)
- support serde_json::Value fields (#1116)
- support native JSON and JSONB column storage (#1114)
- [breaking] require explicit column types for JSON fields (#1106)
- support temporal Vec fields (#1105)
- filter associations in include (#1089)
- introduce Expr::Static for inline SQL literals, hook up LIMIT/OFFSET (#1001)
- support integer storage for enum discriminants (#1101)
- add upsert support (#1091)
- add #[shared] variant fields and enum-level #[index]/#[unique] (#1078)
- implement Scalar for unit enum embeds (#1082)
- add #[document] storage for embedded types with nested-path filtering (#1028)
Fixed
- type indexed key discovery as primary keys on DynamoDB (#1113)
- serialize unconstrained numeric migration snapshots (#1115)
- roll back transactions when finalization fails (#1102)
- support any() on many-to-many relations (#1097)
- store Vec as a native enum array on Postgres (#1092)
- compare composite-FK include filter against target fields (#1086)
- return None for optional belongs_to with NULL foreign key (#1090)
- lower IN-list over an embedded-field projection (#1084)