sea_orm/query/
delete.rs

1use crate::{
2    ActiveModelTrait, ActiveValue, ColumnTrait, DbErr, EntityTrait, IntoActiveModel, Iterable,
3    PrimaryKeyToColumn, QueryFilter, QueryTrait,
4};
5use core::marker::PhantomData;
6use sea_query::DeleteStatement;
7
8/// Defines the structure for a delete operation
9#[derive(Clone, Debug)]
10pub struct Delete;
11
12/// A request to delete an [`ActiveModel`](ActiveModelTrait).
13///
14/// The primary key must be set.
15/// Otherwise, it's impossible to generate the SQL condition and find the record.
16/// In that case, [`exec`][Self::exec] will return an error and not send any queries to the database.
17///
18/// If you want to use [`QueryTrait`] and access the generated SQL query,
19/// you need to convert into [`ValidatedDeleteOne`] first.
20#[derive(Clone, Debug)]
21pub struct DeleteOne<E: EntityTrait>(pub(crate) Result<ValidatedDeleteOne<E>, DbErr>);
22
23/// A validated [`DeleteOne`] request, where the primary key is set
24/// and it's possible to generate the right SQL condition.
25#[derive(Clone, Debug)]
26pub struct ValidatedDeleteOne<E: EntityTrait> {
27    pub(crate) query: DeleteStatement,
28    pub(crate) entity: PhantomData<E>,
29}
30
31impl<E: EntityTrait> TryFrom<DeleteOne<E>> for ValidatedDeleteOne<E> {
32    type Error = DbErr;
33
34    fn try_from(value: DeleteOne<E>) -> Result<Self, Self::Error> {
35        value.0
36    }
37}
38
39impl<E: EntityTrait> DeleteOne<E> {
40    /// Check whether the primary key is set and we can proceed with the operation.
41    pub fn validate(self) -> Result<ValidatedDeleteOne<E>, DbErr> {
42        self.try_into()
43    }
44}
45
46/// Perform a delete operation on multiple models
47#[derive(Clone, Debug)]
48pub struct DeleteMany<E>
49where
50    E: EntityTrait,
51{
52    pub(crate) query: DeleteStatement,
53    pub(crate) entity: PhantomData<E>,
54}
55
56impl Delete {
57    /// Delete one Model or ActiveModel
58    ///
59    /// Model
60    /// ```
61    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
62    ///
63    /// assert_eq!(
64    ///     Delete::one(cake::Model {
65    ///         id: 1,
66    ///         name: "Apple Pie".to_owned(),
67    ///     })
68    ///     .validate()
69    ///     .unwrap()
70    ///     .build(DbBackend::Postgres)
71    ///     .to_string(),
72    ///     r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
73    /// );
74    /// ```
75    /// ActiveModel
76    /// ```
77    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
78    ///
79    /// assert_eq!(
80    ///     Delete::one(cake::ActiveModel {
81    ///         id: ActiveValue::set(1),
82    ///         name: ActiveValue::set("Apple Pie".to_owned()),
83    ///     })
84    ///     .validate()
85    ///     .unwrap()
86    ///     .build(DbBackend::Postgres)
87    ///     .to_string(),
88    ///     r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
89    /// );
90    /// ```
91    //
92    // (non-doc comment for maintainers)
93    // Ideally, we would make this method fallible instead of stashing and delaying the error.
94    // But that's a bigger breaking change.
95    pub fn one<E, A, M>(model: M) -> DeleteOne<E>
96    where
97        E: EntityTrait,
98        A: ActiveModelTrait<Entity = E>,
99        M: IntoActiveModel<A>,
100    {
101        let model = model.into_active_model();
102        let mut myself = ValidatedDeleteOne {
103            query: DeleteStatement::new()
104                .from_table(A::Entity::default().table_ref())
105                .to_owned(),
106            entity: PhantomData,
107        };
108        // Build the SQL condition from the primary key columns.
109        for key in <A::Entity as EntityTrait>::PrimaryKey::iter() {
110            let col = key.into_column();
111            let av = model.get(col);
112            match av {
113                ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
114                    myself = myself.filter(col.eq(value));
115                }
116                ActiveValue::NotSet => {
117                    return DeleteOne(Err(DbErr::PrimaryKeyNotSet { ctx: "DeleteOne" }));
118                }
119            }
120        }
121        DeleteOne(Ok(myself))
122    }
123
124    #[doc(hidden)]
125    pub fn _one_only_for_use_by_model_ex<E: EntityTrait>(entity: E) -> ValidatedDeleteOne<E> {
126        ValidatedDeleteOne {
127            query: DeleteStatement::new()
128                .from_table(entity.table_ref())
129                .to_owned(),
130            entity: PhantomData,
131        }
132    }
133
134    /// Delete many ActiveModel
135    ///
136    /// ```
137    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::fruit};
138    ///
139    /// assert_eq!(
140    ///     Delete::many(fruit::Entity)
141    ///         .filter(fruit::Column::Name.contains("Apple"))
142    ///         .build(DbBackend::Postgres)
143    ///         .to_string(),
144    ///     r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE '%Apple%'"#,
145    /// );
146    /// ```
147    pub fn many<E>(entity: E) -> DeleteMany<E>
148    where
149        E: EntityTrait,
150    {
151        DeleteMany {
152            query: DeleteStatement::new()
153                .from_table(entity.table_ref())
154                .to_owned(),
155            entity: PhantomData,
156        }
157    }
158}
159
160impl<E> QueryFilter for ValidatedDeleteOne<E>
161where
162    E: EntityTrait,
163{
164    type QueryStatement = DeleteStatement;
165
166    fn query(&mut self) -> &mut DeleteStatement {
167        &mut self.query
168    }
169}
170
171impl<E> QueryFilter for DeleteMany<E>
172where
173    E: EntityTrait,
174{
175    type QueryStatement = DeleteStatement;
176
177    fn query(&mut self) -> &mut DeleteStatement {
178        &mut self.query
179    }
180}
181
182impl<E> QueryTrait for ValidatedDeleteOne<E>
183where
184    E: EntityTrait,
185{
186    type QueryStatement = DeleteStatement;
187
188    fn query(&mut self) -> &mut DeleteStatement {
189        &mut self.query
190    }
191
192    fn as_query(&self) -> &DeleteStatement {
193        &self.query
194    }
195
196    fn into_query(self) -> DeleteStatement {
197        self.query
198    }
199}
200
201impl<E> QueryTrait for DeleteMany<E>
202where
203    E: EntityTrait,
204{
205    type QueryStatement = DeleteStatement;
206
207    fn query(&mut self) -> &mut DeleteStatement {
208        &mut self.query
209    }
210
211    fn as_query(&self) -> &DeleteStatement {
212        &self.query
213    }
214
215    fn into_query(self) -> DeleteStatement {
216        self.query
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use crate::tests_cfg::{cake, fruit};
223    use crate::{DbBackend, entity::*, query::*};
224
225    #[test]
226    fn delete_1() {
227        assert_eq!(
228            Delete::one(cake::Model {
229                id: 1,
230                name: "Apple Pie".to_owned(),
231            })
232            .validate()
233            .unwrap()
234            .build(DbBackend::Postgres)
235            .to_string(),
236            r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
237        );
238        assert_eq!(
239            Delete::one(cake::ActiveModel {
240                id: ActiveValue::set(1),
241                name: ActiveValue::set("Apple Pie".to_owned()),
242            })
243            .validate()
244            .unwrap()
245            .build(DbBackend::Postgres)
246            .to_string(),
247            r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
248        );
249    }
250
251    #[test]
252    fn delete_2() {
253        assert_eq!(
254            Delete::many(fruit::Entity)
255                .filter(fruit::Column::Name.contains("Cheese"))
256                .build(DbBackend::Postgres)
257                .to_string(),
258            r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE '%Cheese%'"#,
259        );
260    }
261}