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<A: ActiveModelTrait>(pub(crate) Result<ValidatedDeleteOne<A>, 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<A: ActiveModelTrait> {
27    pub(crate) query: DeleteStatement,
28    pub(crate) model: A,
29}
30
31impl<A: ActiveModelTrait> TryFrom<DeleteOne<A>> for ValidatedDeleteOne<A> {
32    type Error = DbErr;
33
34    fn try_from(value: DeleteOne<A>) -> Result<Self, Self::Error> {
35        value.0
36    }
37}
38
39impl<A: ActiveModelTrait> DeleteOne<A> {
40    /// Check whether the primary key is set and we can proceed with the operation.
41    pub fn validate(self) -> Result<ValidatedDeleteOne<A>, 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<A>
96    where
97        E: EntityTrait,
98        A: ActiveModelTrait<Entity = E>,
99        M: IntoActiveModel<A>,
100    {
101        let mut myself = ValidatedDeleteOne {
102            query: DeleteStatement::new()
103                .from_table(A::Entity::default().table_ref())
104                .to_owned(),
105            model: model.into_active_model(),
106        };
107        // Build the SQL condition from the primary key columns.
108        for key in <A::Entity as EntityTrait>::PrimaryKey::iter() {
109            let col = key.into_column();
110            let av = myself.model.get(col);
111            match av {
112                ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
113                    myself = myself.filter(col.eq(value));
114                }
115                ActiveValue::NotSet => {
116                    return DeleteOne(Err(DbErr::PrimaryKeyNotSet { ctx: "DeleteOne" }));
117                }
118            }
119        }
120        DeleteOne(Ok(myself))
121    }
122
123    /// Delete many ActiveModel
124    ///
125    /// ```
126    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::fruit};
127    ///
128    /// assert_eq!(
129    ///     Delete::many(fruit::Entity)
130    ///         .filter(fruit::Column::Name.contains("Apple"))
131    ///         .build(DbBackend::Postgres)
132    ///         .to_string(),
133    ///     r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE '%Apple%'"#,
134    /// );
135    /// ```
136    pub fn many<E>(entity: E) -> DeleteMany<E>
137    where
138        E: EntityTrait,
139    {
140        DeleteMany {
141            query: DeleteStatement::new()
142                .from_table(entity.table_ref())
143                .to_owned(),
144            entity: PhantomData,
145        }
146    }
147}
148
149impl<A> QueryFilter for ValidatedDeleteOne<A>
150where
151    A: ActiveModelTrait,
152{
153    type QueryStatement = DeleteStatement;
154
155    fn query(&mut self) -> &mut DeleteStatement {
156        &mut self.query
157    }
158}
159
160impl<E> QueryFilter for DeleteMany<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<A> QueryTrait for ValidatedDeleteOne<A>
172where
173    A: ActiveModelTrait,
174{
175    type QueryStatement = DeleteStatement;
176
177    fn query(&mut self) -> &mut DeleteStatement {
178        &mut self.query
179    }
180
181    fn as_query(&self) -> &DeleteStatement {
182        &self.query
183    }
184
185    fn into_query(self) -> DeleteStatement {
186        self.query
187    }
188}
189
190impl<E> QueryTrait for DeleteMany<E>
191where
192    E: EntityTrait,
193{
194    type QueryStatement = DeleteStatement;
195
196    fn query(&mut self) -> &mut DeleteStatement {
197        &mut self.query
198    }
199
200    fn as_query(&self) -> &DeleteStatement {
201        &self.query
202    }
203
204    fn into_query(self) -> DeleteStatement {
205        self.query
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use crate::tests_cfg::{cake, fruit};
212    use crate::{DbBackend, entity::*, query::*};
213
214    #[test]
215    fn delete_1() {
216        assert_eq!(
217            Delete::one(cake::Model {
218                id: 1,
219                name: "Apple Pie".to_owned(),
220            })
221            .validate()
222            .unwrap()
223            .build(DbBackend::Postgres)
224            .to_string(),
225            r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
226        );
227        assert_eq!(
228            Delete::one(cake::ActiveModel {
229                id: ActiveValue::set(1),
230                name: ActiveValue::set("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    }
239
240    #[test]
241    fn delete_2() {
242        assert_eq!(
243            Delete::many(fruit::Entity)
244                .filter(fruit::Column::Name.contains("Cheese"))
245                .build(DbBackend::Postgres)
246                .to_string(),
247            r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE '%Cheese%'"#,
248        );
249    }
250}