pub trait Model:
FromRow
+ Sized
+ Send
+ Sync
+ Unpin
+ 'static {
type Key: ModelKey;
const TABLE: &'static str;
const COLUMNS: &'static [&'static str];
const SOFT_DELETES: bool = false;
const SELECT_ALL: bool = false;
const SEARCHABLE: &'static [&'static str] = _;
const SEARCH_LANGUAGE: &'static str = "english";
Show 26 methods
// Required method
fn id(&self) -> Self::Key;
// Provided methods
fn replicate(&self) -> Self
where Self: Clone { ... }
fn default_scope(query: Query<Self>) -> Query<Self> { ... }
fn saving(&mut self, _creating: bool) -> Result { ... }
fn saved(&self, _created: bool) -> impl Future<Output = Result> + Send { ... }
fn deleting(&self) -> Result { ... }
fn deleted(&self) -> impl Future<Output = Result> + Send { ... }
fn query() -> Query<Self> { ... }
fn unscoped() -> Query<Self> { ... }
fn refresh(&mut self, db: &Db) -> impl Future<Output = Result<()>> + Send { ... }
fn search(words: &str) -> Query<Self> { ... }
fn where_eq(column: &str, value: impl ToDbValue) -> Query<Self> { ... }
fn all<'c, E: Executor<'c>>(
db: E,
) -> impl Future<Output = Result<Vec<Self>>> + Send { ... }
fn find<'c, E: Executor<'c>>(
db: E,
id: Self::Key,
) -> impl Future<Output = Result<Option<Self>>> + Send { ... }
fn find_many<'c, E: Executor<'c>>(
db: E,
ids: impl IntoIterator<Item = Self::Key>,
) -> impl Future<Output = Result<Vec<Self>>> + Send { ... }
fn insert_many<'c, E: Executor<'c>>(
db: E,
models: Vec<Self>,
) -> impl Future<Output = Result<u64>> + Send { ... }
fn upsert<'c, E: Executor<'c>>(
db: E,
models: Vec<Self>,
unique_by: &[&str],
update: &[&str],
) -> impl Future<Output = Result<u64>> + Send { ... }
fn find_or_404<'c, E: Executor<'c>>(
db: E,
id: Self::Key,
) -> impl Future<Output = Result<Self>> + Send { ... }
fn create<'c, E: Executor<'c>>(
db: E,
model: Self,
) -> impl Future<Output = Result<Self>> + Send { ... }
fn insert<'c, E: Executor<'c>>(
&mut self,
db: E,
) -> impl Future<Output = Result> + Send { ... }
fn save<'c, E: Executor<'c>>(
&mut self,
db: E,
) -> impl Future<Output = Result> + Send { ... }
fn save_only<'c, E: Executor<'c>>(
&mut self,
db: E,
columns: &[&str],
) -> impl Future<Output = Result> + Send { ... }
fn save_changes<'c, E: Executor<'c>>(
&mut self,
db: E,
original: &Self,
) -> impl Future<Output = Result<bool>> + Send { ... }
fn delete<'c, E: Executor<'c>>(
&mut self,
db: E,
) -> impl Future<Output = Result> + Send { ... }
fn force_delete<'c, E: Executor<'c>>(
&self,
db: E,
) -> impl Future<Output = Result> + Send { ... }
fn restore<'c, E: Executor<'c>>(
&mut self,
db: E,
) -> impl Future<Output = Result> + Send { ... }
}Expand description
A struct stored as a row in a table. Derive it with #[derive(Model)].
#[derive(Model, Serialize, Default)]
#[model(table = "products", soft_deletes)]
struct Product {
id: i64,
name: String,
price: i64,
created_at: Option<DateTime>,
updated_at: Option<DateTime>,
deleted_at: Option<DateTime>,
}
let mut coffee = Product { name: "Coffee".into(), price: 18_000, ..Default::default() };
coffee.save(&db).await?; // INSERT, sets id and timestamps
let cheap = Product::query().where_op("price", "<", 20_000).get(&db).await?;Implement it with #[derive(Model)]: the hidden items it writes may
change in a minor release.
The primary key is the id column. Its type is the id field’s: i64
(numbered by the database; 0 means “not saved yet”), or a
Ulid, a UUID or a String (see ModelKey).
Required Associated Constants§
Provided Associated Constants§
Sourceconst SOFT_DELETES: bool = false
const SOFT_DELETES: bool = false
delete() sets deleted_at instead of removing the row, and queries
skip deleted rows unless asked with with_trashed() / only_trashed().
Sourceconst SELECT_ALL: bool = false
const SELECT_ALL: bool = false
Select every column (*) instead of COLUMNS, so from_row also
sees columns the struct doesn’t list, and queries may filter on them.
The built-in User does this to keep the app’s own columns.
Sourceconst SEARCHABLE: &'static [&'static str] = _
const SEARCHABLE: &'static [&'static str] = _
The text columns full-text search looks in, most important first
(they weigh more in the ranking); empty: the model can’t be
searched. Set it with #[model(search = "title, body")]; see
renox::db::search.
Sourceconst SEARCH_LANGUAGE: &'static str = "english"
const SEARCH_LANGUAGE: &'static str = "english"
The language full-text search stems words for: english (the
default) or a PostgreSQL text search configuration such as simple
(no stemming) or spanish. Set it with
#[model(search_language = "simple")].
Required Associated Types§
Required Methods§
Provided Methods§
Sourcefn replicate(&self) -> Selfwhere
Self: Clone,
fn replicate(&self) -> Selfwhere
Self: Clone,
A copy of the model that isn’t saved yet (Laravel’s replicate):
the same values, with an unsaved id, no deleted_at and, when they
are Options, no timestamps, so save inserts a new row. Change
what must differ (a unique SKU, a name) before saving it, or show it
in the “new” form for someone to finish (“Duplicate”).
let original = Product::find_or_404(&db, 1).await?;
let mut copy = original.replicate();
copy.sku = format!("{}-COPY", original.sku);
copy.save(&db).await?; // a new row, with its own idSourcefn default_scope(query: Query<Self>) -> Query<Self>
fn default_scope(query: Query<Self>) -> Query<Self>
Conditions every query of this model starts with, e.g. the current
tenant, read from renox::context. query(),
find, all, where_eq and the relation loaders apply it;
Model::unscoped doesn’t. Saving, deleting and restoring a loaded
model work by its id. Set it with #[model(default_scope = "…")]:
#[derive(Clone)]
struct CurrentTeam(i64);
#[derive(Model, serde::Serialize, Default)]
#[model(table = "projects", default_scope = "team_only")]
struct Project { id: i64, team_id: i64, name: String }
fn team_only(query: renox::db::Query<Project>) -> renox::db::Query<Project> {
match renox::context::get::<CurrentTeam>() {
Some(team) => query.where_eq("team_id", team.0),
None => query.none(), // no team, no rows: fail closed
}
}Sourcefn saving(&mut self, _creating: bool) -> Result
fn saving(&mut self, _creating: bool) -> Result
Runs before the row is written; an error stops the save. Implement
ModelHooks and add #[model(hooks)] rather than overriding it.
Sourcefn saved(&self, _created: bool) -> impl Future<Output = Result> + Send
fn saved(&self, _created: bool) -> impl Future<Output = Result> + Send
Runs after the row is written (inside the caller’s transaction, if
any); an error is returned by save. See ModelHooks.
Sourcefn deleting(&self) -> Result
fn deleting(&self) -> Result
Runs before delete/force_delete; an error stops it. See ModelHooks.
Sourcefn deleted(&self) -> impl Future<Output = Result> + Send
fn deleted(&self) -> impl Future<Output = Result> + Send
Runs after delete/force_delete. See ModelHooks.
Sourcefn query() -> Query<Self>
fn query() -> Query<Self>
A query with the default scope applied (see Model::default_scope).
Sourcefn unscoped() -> Query<Self>
fn unscoped() -> Query<Self>
A query without the default scope, e.g. for an admin who sees every tenant. Soft-deleted rows stay hidden unless asked for.
Sourcefn refresh(&mut self, db: &Db) -> impl Future<Output = Result<()>> + Send
fn refresh(&mut self, db: &Db) -> impl Future<Output = Result<()>> + Send
Reloads the model’s row (e.g. after an increment or another request
changed it); a deleted row is a 404.
Sourcefn search(words: &str) -> Query<Self>
fn search(words: &str) -> Query<Self>
The rows matching a full-text search, best matches first:
shorthand for query().search(words). The model needs
#[model(search = "…")] and its index (see
renox::db::search).
#[derive(Model, serde::Serialize, Default)]
#[model(table = "posts", search = "title, body")]
struct Post { id: i64, title: String, body: String }
let posts = Post::search(&q).limit(20).get(&db).await?;Sourcefn where_eq(column: &str, value: impl ToDbValue) -> Query<Self>
fn where_eq(column: &str, value: impl ToDbValue) -> Query<Self>
Shorthand for query().where_eq(column, value).
Sourcefn all<'c, E: Executor<'c>>(
db: E,
) -> impl Future<Output = Result<Vec<Self>>> + Send
fn all<'c, E: Executor<'c>>( db: E, ) -> impl Future<Output = Result<Vec<Self>>> + Send
Every row, in id order.
Sourcefn find<'c, E: Executor<'c>>(
db: E,
id: Self::Key,
) -> impl Future<Output = Result<Option<Self>>> + Send
fn find<'c, E: Executor<'c>>( db: E, id: Self::Key, ) -> impl Future<Output = Result<Option<Self>>> + Send
The row with this id, or None.
Sourcefn find_many<'c, E: Executor<'c>>(
db: E,
ids: impl IntoIterator<Item = Self::Key>,
) -> impl Future<Output = Result<Vec<Self>>> + Send
fn find_many<'c, E: Executor<'c>>( db: E, ids: impl IntoIterator<Item = Self::Key>, ) -> impl Future<Output = Result<Vec<Self>>> + Send
The rows with these ids, in id order (missing ids are skipped).
Sourcefn insert_many<'c, E: Executor<'c>>(
db: E,
models: Vec<Self>,
) -> impl Future<Output = Result<u64>> + Send
fn insert_many<'c, E: Executor<'c>>( db: E, models: Vec<Self>, ) -> impl Future<Output = Result<u64>> + Send
Inserts many new models with a few statements (ids aren’t returned;
use create when you need them). Timestamps are set; unsaved ULID
and UUID keys are made, String keys must be set, i64 keys come
from the database. Returns the number of rows inserted.
Sourcefn upsert<'c, E: Executor<'c>>(
db: E,
models: Vec<Self>,
unique_by: &[&str],
update: &[&str],
) -> impl Future<Output = Result<u64>> + Send
fn upsert<'c, E: Executor<'c>>( db: E, models: Vec<Self>, unique_by: &[&str], update: &[&str], ) -> impl Future<Output = Result<u64>> + Send
Inserts models, or updates the rows they clash with on the
unique_by columns (which need a unique index), setting update
columns (and updated_at when the model has it). Returns the rows
written.
let feed = vec![Stock { sku: "COFFEE-1".into(), qty: 12, ..Default::default() }];
Stock::upsert(&db, feed, &["sku"], &["qty"]).await?;Sourcefn find_or_404<'c, E: Executor<'c>>(
db: E,
id: Self::Key,
) -> impl Future<Output = Result<Self>> + Send
fn find_or_404<'c, E: Executor<'c>>( db: E, id: Self::Key, ) -> impl Future<Output = Result<Self>> + Send
Like find, but a missing row becomes a 404 response.
Sourcefn create<'c, E: Executor<'c>>(
db: E,
model: Self,
) -> impl Future<Output = Result<Self>> + Send
fn create<'c, E: Executor<'c>>( db: E, model: Self, ) -> impl Future<Output = Result<Self>> + Send
Inserts a new model and returns it with its id and timestamps.
Sourcefn insert<'c, E: Executor<'c>>(
&mut self,
db: E,
) -> impl Future<Output = Result> + Send
fn insert<'c, E: Executor<'c>>( &mut self, db: E, ) -> impl Future<Output = Result> + Send
Inserts the model as a new row, whatever its id: an unsaved id gets a
new key (from the database for i64, a new ULID or UUID v7), a set
one is written as it is (a String key must be set).
Sourcefn save<'c, E: Executor<'c>>(
&mut self,
db: E,
) -> impl Future<Output = Result> + Send
fn save<'c, E: Executor<'c>>( &mut self, db: E, ) -> impl Future<Output = Result> + Send
Inserts the model if its id is unsaved (0, an empty ULID…),
otherwise updates its row.
Sourcefn save_only<'c, E: Executor<'c>>(
&mut self,
db: E,
columns: &[&str],
) -> impl Future<Output = Result> + Send
fn save_only<'c, E: Executor<'c>>( &mut self, db: E, columns: &[&str], ) -> impl Future<Output = Result> + Send
Updates only columns (and updated_at, if the model has it) of a
saved model, so a concurrent change to another column isn’t
overwritten. A column the saving hook changes is saved only if
it’s listed.
post.title = "New title".into();
post.save_only(&db, &["title"]).await?; // leaves `views` aloneSourcefn save_changes<'c, E: Executor<'c>>(
&mut self,
db: E,
original: &Self,
) -> impl Future<Output = Result<bool>> + Send
fn save_changes<'c, E: Executor<'c>>( &mut self, db: E, original: &Self, ) -> impl Future<Output = Result<bool>> + Send
Saves the columns that differ from original (the model as it was
loaded), including those the saving hook changes, and returns
whether anything was written. When nothing changed there’s no query
and no saved hook.
let original = Post::find_or_404(&db, 1).await?;
let mut post = original.clone();
post.title = "New title".into();
post.save_changes(&db, &original).await?; // UPDATE posts SET title = ?Sourcefn delete<'c, E: Executor<'c>>(
&mut self,
db: E,
) -> impl Future<Output = Result> + Send
fn delete<'c, E: Executor<'c>>( &mut self, db: E, ) -> impl Future<Output = Result> + Send
Deletes the row, or marks it deleted for models with soft deletes.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".