Updating Records
To change field values on a record, use the toasty::update! macro.
The macro takes a target — a loaded model instance or a query that
selects which records to change — and a list of field assignments. It
returns a builder; call .exec(&mut db) to execute the update.
To load an instance to update, see Querying Records. To insert new records, see Creating Records.
Updating an instance
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: u64,
name: String,
#[unique]
email: String,
}
async fn __example(mut db: toasty::Db) -> toasty::Result<()> {
let mut user = toasty::create!(User {
name: "Alice",
email: "alice@example.com",
})
.exec(&mut db)
.await?;
toasty::update!(user { name: "Alice Smith" })
.exec(&mut db)
.await?;
assert_eq!(user.name, "Alice Smith");
Ok(())
}
}
The instance reflects the new values after .exec(). Fields not
named in the macro keep their current values.
The generated SQL:
UPDATE users SET name = 'Alice Smith' WHERE id = 1;
Updating multiple fields
List each field in the brace block:
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: u64,
name: String,
#[unique]
email: String,
}
async fn __example(mut db: toasty::Db) -> toasty::Result<()> {
let mut user = toasty::create!(User { name: "Alice", email: "alice@example.com" })
.exec(&mut db).await?;
toasty::update!(user {
name: "Alice Smith",
email: "alice.smith@example.com",
})
.exec(&mut db)
.await?;
Ok(())
}
}
Field shorthand
When a local variable has the same name as a field, write the name alone — the same shorthand Rust struct literals use:
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: u64,
name: String,
}
async fn __example(mut db: toasty::Db) -> toasty::Result<()> {
let mut user = toasty::create!(User { name: "Alice" })
.exec(&mut db).await?;
let name = "Alice Smith";
toasty::update!(user { name })
.exec(&mut db)
.await?;
Ok(())
}
}
Modifying Vec<scalar> fields
Collection mutations live in toasty::stmt. The macro reaches them
as method calls on the field:
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct Article {
#[key]
#[auto]
id: u64,
tags: Vec<String>,
}
async fn __example(mut db: toasty::Db) -> toasty::Result<()> {
let mut article = toasty::create!(Article { tags: vec!["rust".to_string()] })
.exec(&mut db).await?;
toasty::update!(article { tags.push("toasty") })
.exec(&mut db)
.await?;
Ok(())
}
}
tags.push("toasty") lowers to tags: toasty::stmt::push("toasty").
The same syntax reaches every function in toasty::stmt:
tags.extend([...]), tags.pop(), tags.clear(),
tags.remove("x"). See Vec<scalar> Fields
for the full list and per-driver support.
Relative numeric updates
Arithmetic builders in toasty::stmt update a numeric field relative
to its stored value — incrementing a counter, crediting a balance.
The macro reaches them as method calls on the field, the same shape
as the collection mutations above:
| Method | What it does |
|---|---|
field.increment() | Add 1 to the field. |
field.decrement() | Subtract 1 from the field. |
field.add(value) | Add value to the field. |
field.subtract(value) | Subtract value from the field. |
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct Account {
#[key]
#[auto]
id: u64,
balance: i64,
login_count: i64,
}
async fn __example(mut db: toasty::Db) -> toasty::Result<()> {
let mut account = toasty::create!(Account { balance: 1000, login_count: 0 })
.exec(&mut db).await?;
// Credit an account by 100.
toasty::update!(account { balance.add(100) })
.exec(&mut db)
.await?;
// Debit by 25.
toasty::update!(account { balance.subtract(25) })
.exec(&mut db)
.await?;
// Bump a login counter.
toasty::update!(account { login_count.increment() })
.exec(&mut db)
.await?;
Ok(())
}
}
Each operation is atomic against the existing column value — the database applies it to whatever the row currently holds, not to a client-side snapshot. Reading, modifying, and writing back from Rust is a three-step round trip a concurrent writer can interleave, dropping one of two updates on the same row; the arithmetic builders fold the read and write into one statement.
These builders work on every backend and on every primitive numeric type Toasty supports.
Updating embedded fields
A brace block on the right side of field: updates the named
sub-fields of an embedded struct:
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Embed)]
struct Metadata {
version: i64,
status: String,
}
#[derive(Debug, toasty::Model)]
struct Document {
#[key]
#[auto]
id: u64,
title: String,
meta: Metadata,
}
async fn __example(mut db: toasty::Db) -> toasty::Result<()> {
let mut doc = toasty::create!(Document {
title: "Doc",
meta: Metadata { version: 1, status: "draft".to_string() },
}).exec(&mut db).await?;
toasty::update!(doc {
meta: { version: 2, status: "published" },
})
.exec(&mut db)
.await?;
Ok(())
}
}
Sub-fields not listed keep their values. Brace blocks nest for embedded types within embedded types.
To replace an embedded value wholesale, pass the typed value:
toasty::update!(doc {
meta: Metadata { version: 2, status: "published".into() },
})
.exec(&mut db).await?;
Inserting has-many children
A bracket-of-braces literal on a has-many field
inserts new children. Each { ... } is a create builder for the
child model:
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: u64,
name: String,
#[has_many]
todos: toasty::Deferred<Vec<Todo>>,
}
#[derive(Debug, toasty::Model)]
struct Todo {
#[key]
#[auto]
id: u64,
title: String,
#[index]
user_id: u64,
#[belongs_to(key = user_id, references = id)]
user: toasty::Deferred<User>,
}
async fn __example(mut db: toasty::Db) -> toasty::Result<()> {
let mut user = User::create().name("Alice").exec(&mut db).await?;
toasty::update!(user {
todos: [{ title: "buy milk" }, { title: "walk dog" }],
})
.exec(&mut db)
.await?;
Ok(())
}
}
Items can mix brace-block builders with stmt::* values:
toasty::update!(user {
todos: [
{ title: "new todo" },
toasty::stmt::remove(&old_todo),
],
})
.exec(&mut db).await?;
Updating by query
update! accepts any query builder as a target. The update applies
to every record the query matches:
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: u64,
#[index]
name: String,
#[unique]
email: String,
}
async fn __example(mut db: toasty::Db, user_id: u64) -> toasty::Result<()> {
toasty::update!(User::filter_by_id(user_id) { name: "Bob" })
.exec(&mut db)
.await?;
Ok(())
}
}
Scoped queries like user.todos().filter_by_done(false) are query
builders too. The query runs as part of the UPDATE statement; no
records are loaded first.
Update by indexed field
Toasty generates update_by_* convenience methods for key and
indexed fields:
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: u64,
name: String,
#[unique]
email: String,
}
async fn __example(mut db: toasty::Db, user_id: u64) -> toasty::Result<()> {
User::update_by_id(user_id)
.name("Bob")
.exec(&mut db)
.await?;
Ok(())
}
}
User::update_by_id(user_id) is shorthand for
User::filter_by_id(user_id).update(). Toasty generates update_by_*
for each field marked #[key], #[unique], or #[index].
Setting an optional field to None
Pass None with the field’s type annotation:
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: u64,
name: String,
bio: Option<String>,
}
async fn __example(mut db: toasty::Db) -> toasty::Result<()> {
let mut user = toasty::create!(User { name: "Alice", bio: "Likes Rust" })
.exec(&mut db).await?;
toasty::update!(user { bio: Option::<String>::None })
.exec(&mut db)
.await?;
Ok(())
}
}
Concurrency control
When the model has a #[version] field,
instance updates condition the write on the version the instance
was loaded with and increment it atomically. If a concurrent writer
has modified the record, .exec() returns an error.
Building updates programmatically
The macro lists every field at the macro call site. To set fields based on a runtime condition, use the update builder directly:
#![allow(unused)]
fn main() {
use toasty::Model;
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: u64,
name: String,
bio: Option<String>,
}
async fn __example(mut db: toasty::Db) -> toasty::Result<()> {
let mut user = toasty::create!(User { name: "Alice" }).exec(&mut db).await?;
let mut builder = user.update().name("Alice Smith");
if true /* some condition */ {
builder = builder.bio("Likes Rust");
}
builder.exec(&mut db).await?;
Ok(())
}
}
The chain form is also available for users who prefer it:
user.update()
.name("Alice Smith")
.exec(&mut db)
.await?;
What #[derive(Model)] generates
For a model User with #[key] on id and #[unique] on email,
the derive generates:
user.update()— instance method (takes&mut self), returns an update builder. After.exec(), the instance reloads with the new values.User::update_by_id(id)— returns an update builder for the record matching the given key.User::update_by_email(email)— returns an update builder for the record matching the given email..update()on any query builder — converts the query into an update builder.
The update builder has a setter for each field. The UPDATE
statement includes only the fields you set.