Skip to main content

siderite_orm/
ops.rs

1//! Instance operations: [`ModelOps`] gives every [`Model`] `save`, `delete`
2//! and `refresh` (Django `Model.save()` / `delete()` / `refresh_from_db()`).
3//!
4//! # Semantics
5//!
6//! * **One statement, signals around it.** Each operation is a single SQL
7//!   statement (`save` may be two, see below). `save` sends `pre_save` before
8//!   and `post_save` (with the `created` flag) after it; `delete` sends
9//!   `pre_delete` before and `post_delete` after a row was removed. Receivers
10//!   are those connected to [`Db::signals`]; see [`signals`](crate::signals)
11//!   for ordering and failure rules. A failing `pre_*` receiver aborts the
12//!   operation; a failing `post_*` receiver errors after the statement ran,
13//!   so wrap the call in [`Db::transaction`] to roll it back. Use
14//!   [`Db::on_commit`] for work that must run only after the surrounding
15//!   transaction commits. Bulk `QuerySet` writes send no signals.
16//! * **Atomicity.** A single statement is atomic. `save` on a manual primary
17//!   key runs `UPDATE` and, if no row matched, `INSERT`; outside a transaction
18//!   another writer can slip in between the two, so wrap concurrent upserts in
19//!   [`Db::transaction`] (or use `QuerySet::get_or_create`).
20//! * **Timestamps.** `auto_now_add` columns are stamped on insert and
21//!   `auto_now` columns on every save, with the current UTC time.
22//! * **Stored state wins.** `save` reads the stored row back (`RETURNING *`)
23//!   and replaces `*self`, so database defaults, generated keys and stamped
24//!   timestamps are visible immediately. Cached related objects of
25//!   [`ForeignKey`](crate::ForeignKey) fields are dropped by that replacement.
26
27use crate::db::Db;
28use crate::error::{OrmError, QueryError};
29use crate::model::Model;
30use crate::persist::{insert_one, pk_filter, update_one};
31use crate::signals::SignalKind;
32use crate::write::{DeletePlan, WritePlan};
33use std::future::Future;
34
35/// Persistence operations available on every [`Model`].
36pub trait ModelOps: Model {
37    /// Insert or update this object.
38    ///
39    /// An [`is_unsaved`](Model::is_unsaved) object is inserted (generated
40    /// columns omitted) and receives its key. Otherwise the row is updated by
41    /// primary key, falling back to an insert when no row exists.
42    ///
43    /// # Errors
44    /// Capability or backend errors; [`BackendError::Constraint`](crate::BackendError::Constraint)
45    /// for unique / foreign-key violations; [`OrmError::Signal`] when a
46    /// `pre_save` receiver aborts the save (nothing is written) or a
47    /// `post_save` receiver fails (the row is already written).
48    fn save(&mut self, db: &Db) -> impl Future<Output = Result<(), OrmError>> + Send;
49
50    /// Delete this object's row, returning the number of rows removed
51    /// (`0` if it no longer exists). Dependent rows follow the database's
52    /// foreign-key actions.
53    ///
54    /// # Errors
55    /// Capability or backend errors (e.g. a `RESTRICT` foreign key);
56    /// [`OrmError::Signal`] from `pre_delete` (aborts) or `post_delete`
57    /// (after the row is gone). `post_delete` is skipped when no row matched.
58    fn delete(&self, db: &Db) -> impl Future<Output = Result<u64, OrmError>> + Send;
59
60    /// Reload every column from the database, replacing `*self`.
61    ///
62    /// # Errors
63    /// [`QueryError::DoesNotExist`] if the row is gone.
64    fn refresh(&mut self, db: &Db) -> impl Future<Output = Result<(), OrmError>> + Send;
65}
66
67impl<M: Model> ModelOps for M {
68    async fn save(&mut self, db: &Db) -> Result<(), OrmError> {
69        let signals = db.signals();
70        signals
71            .send(db, &*self, SignalKind::PreSave, None, &[])
72            .await?;
73        let created = if self.is_unsaved() || !update_one(db, self).await? {
74            insert_one(db, self).await?;
75            true
76        } else {
77            false
78        };
79        signals
80            .send(db, &*self, SignalKind::PostSave { created }, None, &[])
81            .await
82    }
83
84    async fn delete(&self, db: &Db) -> Result<u64, OrmError> {
85        let plan = WritePlan::Delete(DeletePlan {
86            table: M::META.table.into(),
87            filter: Some(pk_filter::<M>(&self.pk())?),
88            returning: Vec::new(),
89        });
90        let signals = db.signals();
91        signals
92            .send(db, self, SignalKind::PreDelete, None, &[])
93            .await?;
94        let removed = db.execute(&plan).await?.rows_affected;
95        if removed > 0 {
96            signals
97                .send(db, self, SignalKind::PostDelete, None, &[])
98                .await?;
99        }
100        Ok(removed)
101    }
102
103    async fn refresh(&mut self, db: &Db) -> Result<(), OrmError> {
104        let fresh = M::objects(db)
105            .filter(pk_filter::<M>(&self.pk())?)
106            .first()
107            .await?
108            .ok_or(QueryError::DoesNotExist)?;
109        *self = fresh;
110        Ok(())
111    }
112}