Skip to main content

sea_orm/entity/
base_entity.rs

1use crate::{
2    ActiveModelBehavior, ActiveModelTrait, ColumnTrait, Delete, DeleteMany, DeleteOne,
3    FromQueryResult, Identity, Insert, InsertMany, ModelTrait, PrimaryKeyArity, PrimaryKeyToColumn,
4    PrimaryKeyTrait, QueryFilter, Related, RelationBuilder, RelationTrait, RelationType, Select,
5    Update, UpdateMany, UpdateOne, ValidatedDeleteOne,
6};
7use sea_query::{Iden, IntoIden, IntoTableRef, IntoValueTuple, TableRef};
8use std::fmt::Debug;
9pub use strum::IntoEnumIterator as Iterable;
10
11/// Ensure the identifier for an Entity can be converted to a static str
12pub trait IdenStatic: Iden + Copy + Debug + 'static {
13    /// Method to call to get the static string identity
14    fn as_str(&self) -> &'static str;
15}
16
17/// A Trait for mapping an Entity to a database table
18pub trait EntityName: IdenStatic + Default {
19    /// Method to get the name for the schema, defaults to [Option::None] if not set
20    fn schema_name(&self) -> Option<&str> {
21        None
22    }
23
24    /// Method to get the comment for the schema, defaults to [Option::None] if not set
25    fn comment(&self) -> Option<&str> {
26        None
27    }
28
29    /// Get the name of the table
30    fn table_name(&self) -> &'static str;
31
32    /// Get the [TableRef] from invoking the `self.schema_name()`
33    fn table_ref(&self) -> TableRef {
34        match self.schema_name() {
35            Some(schema) => (schema.to_owned(), self.into_iden()).into_table_ref(),
36            None => self.into_table_ref(),
37        }
38    }
39}
40
41/// An abstract base class for defining Entities.
42///
43/// This trait provides an API for you to inspect it's properties
44/// - Column (implemented [`ColumnTrait`])
45/// - Relation (implemented [`RelationTrait`])
46/// - Primary Key (implemented [`PrimaryKeyTrait`] and [`PrimaryKeyToColumn`])
47///
48/// This trait also provides an API for CRUD actions
49/// - Select: `find`, `find_*`
50/// - Insert: `insert`, `insert_*`
51/// - Update: `update`, `update_*`
52/// - Delete: `delete`, `delete_*`
53pub trait EntityTrait: EntityName {
54    #[allow(missing_docs)]
55    type Model: ModelTrait<Entity = Self> + FromQueryResult;
56
57    #[allow(missing_docs)]
58    type ModelEx: ModelTrait<Entity = Self>;
59
60    #[allow(missing_docs)]
61    type ActiveModel: ActiveModelBehavior<Entity = Self>;
62
63    #[allow(missing_docs)]
64    type ActiveModelEx: ActiveModelTrait<Entity = Self>;
65
66    #[allow(missing_docs)]
67    type Column: ColumnTrait;
68
69    #[allow(missing_docs)]
70    type Relation: RelationTrait;
71
72    #[allow(missing_docs)]
73    type PrimaryKey: PrimaryKeyTrait + PrimaryKeyToColumn<Column = Self::Column>;
74
75    /// Construct a belongs to relation, where this table has a foreign key to
76    /// another table.
77    fn belongs_to<R>(related: R) -> RelationBuilder<Self, R>
78    where
79        R: EntityTrait,
80    {
81        RelationBuilder::new(RelationType::HasOne, Self::default(), related, false)
82    }
83
84    /// Construct a has one relation
85    fn has_one<R>(_: R) -> RelationBuilder<Self, R>
86    where
87        R: EntityTrait + Related<Self>,
88    {
89        RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true)
90    }
91
92    /// Construct a has many relation
93    fn has_many<R>(_: R) -> RelationBuilder<Self, R>
94    where
95        R: EntityTrait + Related<Self>,
96    {
97        RelationBuilder::from_rel(RelationType::HasMany, R::to().rev(), true)
98    }
99
100    /// Construct a has many relation, with the Relation provided.
101    /// This is for the case where `Related<Self>` is not possible.
102    fn has_many_via<R, T>(_: R, rel: T) -> RelationBuilder<Self, R>
103    where
104        R: EntityTrait,
105        T: RelationTrait,
106    {
107        RelationBuilder::from_rel(RelationType::HasMany, rel.def().rev(), true)
108    }
109
110    /// Construct select statement to find one / all models
111    ///
112    /// - To select columns, join tables and group by expressions, see [`QuerySelect`](crate::query::QuerySelect)
113    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
114    /// - To apply order by expressions, see [`QueryOrder`](crate::query::QueryOrder)
115    ///
116    /// # Example
117    ///
118    /// ```
119    /// # use sea_orm::{error::*, tests_cfg::*, *};
120    /// #
121    /// # #[cfg(feature = "mock")]
122    /// # pub fn main() -> Result<(), DbErr> {
123    /// #
124    /// # let db = MockDatabase::new(DbBackend::Postgres)
125    /// #     .append_query_results([
126    /// #         vec![
127    /// #             cake::Model {
128    /// #                 id: 1,
129    /// #                 name: "New York Cheese".to_owned(),
130    /// #             },
131    /// #         ],
132    /// #         vec![
133    /// #             cake::Model {
134    /// #                 id: 1,
135    /// #                 name: "New York Cheese".to_owned(),
136    /// #             },
137    /// #             cake::Model {
138    /// #                 id: 2,
139    /// #                 name: "Chocolate Forest".to_owned(),
140    /// #             },
141    /// #         ],
142    /// #     ])
143    /// #     .into_connection();
144    /// #
145    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
146    ///
147    /// assert_eq!(
148    ///     cake::Entity::find().one(&db)?,
149    ///     Some(cake::Model {
150    ///         id: 1,
151    ///         name: "New York Cheese".to_owned(),
152    ///     })
153    /// );
154    ///
155    /// assert_eq!(
156    ///     cake::Entity::find().all(&db)?,
157    ///     [
158    ///         cake::Model {
159    ///             id: 1,
160    ///             name: "New York Cheese".to_owned(),
161    ///         },
162    ///         cake::Model {
163    ///             id: 2,
164    ///             name: "Chocolate Forest".to_owned(),
165    ///         },
166    ///     ]
167    /// );
168    ///
169    /// assert_eq!(
170    ///     db.into_transaction_log(),
171    ///     [
172    ///         Transaction::from_sql_and_values(
173    ///             DbBackend::Postgres,
174    ///             r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
175    ///             [1u64.into()]
176    ///         ),
177    ///         Transaction::from_sql_and_values(
178    ///             DbBackend::Postgres,
179    ///             r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
180    ///             []
181    ///         ),
182    ///     ]
183    /// );
184    /// #
185    /// # Ok(())
186    /// # }
187    /// ```
188    fn find() -> Select<Self> {
189        Select::new()
190    }
191
192    /// Same as `find_related`, but using the other Entity's relation definition.
193    /// Not stable.
194    #[doc(hidden)]
195    fn find_related_rev<R>() -> Select<R>
196    where
197        R: EntityTrait,
198        R: Related<Self>,
199    {
200        use crate::{JoinType, QuerySelect};
201        Select::<R>::new().join_join(JoinType::InnerJoin, R::to(), R::via())
202    }
203
204    /// Find a model by primary key
205    ///
206    /// # Example
207    ///
208    /// ```
209    /// # use sea_orm::{error::*, tests_cfg::*, *};
210    /// #
211    /// # #[cfg(feature = "mock")]
212    /// # pub fn main() -> Result<(), DbErr> {
213    /// #
214    /// # let db = MockDatabase::new(DbBackend::Postgres)
215    /// #     .append_query_results([
216    /// #         [
217    /// #             cake::Model {
218    /// #                 id: 11,
219    /// #                 name: "Sponge Cake".to_owned(),
220    /// #             },
221    /// #         ],
222    /// #     ])
223    /// #     .into_connection();
224    /// #
225    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
226    ///
227    /// assert_eq!(
228    ///     cake::Entity::find_by_id(11).all(&db)?,
229    ///     [cake::Model {
230    ///         id: 11,
231    ///         name: "Sponge Cake".to_owned(),
232    ///     }]
233    /// );
234    ///
235    /// assert_eq!(
236    ///     db.into_transaction_log(),
237    ///     [Transaction::from_sql_and_values(
238    ///         DbBackend::Postgres,
239    ///         r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = $1"#,
240    ///         [11i32.into()]
241    ///     )]
242    /// );
243    /// #
244    /// # Ok(())
245    /// # }
246    /// ```
247    /// Find by composite key
248    /// ```
249    /// # use sea_orm::{error::*, tests_cfg::*, *};
250    /// #
251    /// # #[cfg(feature = "mock")]
252    /// # pub fn main() -> Result<(), DbErr> {
253    /// #
254    /// # let db = MockDatabase::new(DbBackend::Postgres)
255    /// #     .append_query_results([
256    /// #         [
257    /// #             cake_filling::Model {
258    /// #                 cake_id: 2,
259    /// #                 filling_id: 3,
260    /// #             },
261    /// #         ],
262    /// #     ])
263    /// #     .into_connection();
264    /// #
265    /// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
266    ///
267    /// assert_eq!(
268    ///     cake_filling::Entity::find_by_id((2, 3)).all(&db)?,
269    ///     [cake_filling::Model {
270    ///         cake_id: 2,
271    ///         filling_id: 3,
272    ///     }]
273    /// );
274    ///
275    /// assert_eq!(
276    ///     db.into_transaction_log(),
277    ///     [Transaction::from_sql_and_values(
278    ///         DbBackend::Postgres,
279    ///         [
280    ///             r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
281    ///             r#"WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
282    ///         ].join(" ").as_str(),
283    ///         [2i32.into(), 3i32.into()]
284    ///     )]);
285    /// #
286    /// # Ok(())
287    /// # }
288    /// ```
289    fn find_by_id<T>(values: T) -> Select<Self>
290    where
291        T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
292    {
293        let mut select = Self::find();
294        let mut keys = Self::PrimaryKey::iter();
295        for v in values.into().into_value_tuple() {
296            if let Some(key) = keys.next() {
297                let col = key.into_column();
298                select = select.filter(col.eq(v));
299            } else {
300                unreachable!("primary key arity mismatch");
301            }
302        }
303        select
304    }
305
306    /// Get primary key as Identity
307    fn primary_key_identity() -> Identity {
308        let mut cols = Self::PrimaryKey::iter();
309        macro_rules! next {
310            () => {
311                cols.next()
312                    .expect("Already checked arity")
313                    .into_column()
314                    .as_column_ref()
315                    .1
316            };
317        }
318        match <<Self::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY {
319            1 => {
320                let s1 = next!();
321                Identity::Unary(s1)
322            }
323            2 => {
324                let s1 = next!();
325                let s2 = next!();
326                Identity::Binary(s1, s2)
327            }
328            3 => {
329                let s1 = next!();
330                let s2 = next!();
331                let s3 = next!();
332                Identity::Ternary(s1, s2, s3)
333            }
334            len => {
335                let mut vec = Vec::with_capacity(len);
336                for _ in 0..len {
337                    let s = next!();
338                    vec.push(s);
339                }
340                Identity::Many(vec)
341            }
342        }
343    }
344
345    /// Insert a model into database
346    ///
347    /// # Example (Postgres)
348    ///
349    /// ```
350    /// # use sea_orm::{error::*, tests_cfg::*, *};
351    /// #
352    /// # #[cfg(feature = "mock")]
353    /// # pub fn main() -> Result<(), DbErr> {
354    /// #
355    /// # let db = MockDatabase::new(DbBackend::Postgres)
356    /// #     .append_query_results([[maplit::btreemap! {
357    /// #         "id" => Into::<Value>::into(15),
358    /// #     }]])
359    /// #     .into_connection();
360    /// #
361    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
362    ///
363    /// let apple = cake::ActiveModel {
364    ///     name: Set("Apple Pie".to_owned()),
365    ///     ..Default::default()
366    /// };
367    ///
368    /// let insert_result = cake::Entity::insert(apple).exec(&db)?;
369    ///
370    /// assert_eq!(dbg!(insert_result.last_insert_id), 15);
371    ///
372    /// assert_eq!(
373    ///     db.into_transaction_log(),
374    ///     [Transaction::from_sql_and_values(
375    ///         DbBackend::Postgres,
376    ///         r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id""#,
377    ///         ["Apple Pie".into()]
378    ///     )]
379    /// );
380    /// #
381    /// # Ok(())
382    /// # }
383    /// ```
384    ///
385    /// # Example (MySQL)
386    ///
387    /// ```
388    /// # use sea_orm::{error::*, tests_cfg::*, *};
389    /// #
390    /// # #[cfg(feature = "mock")]
391    /// # pub fn main() -> Result<(), DbErr> {
392    /// #
393    /// # let db = MockDatabase::new(DbBackend::MySql)
394    /// #     .append_exec_results([
395    /// #         MockExecResult {
396    /// #             last_insert_id: 15,
397    /// #             rows_affected: 1,
398    /// #         },
399    /// #     ])
400    /// #     .into_connection();
401    /// #
402    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
403    ///
404    /// let apple = cake::ActiveModel {
405    ///     name: Set("Apple Pie".to_owned()),
406    ///     ..Default::default()
407    /// };
408    ///
409    /// let insert_result = cake::Entity::insert(apple).exec(&db)?;
410    ///
411    /// assert_eq!(insert_result.last_insert_id, 15);
412    ///
413    /// assert_eq!(
414    ///     db.into_transaction_log(),
415    ///     [Transaction::from_sql_and_values(
416    ///         DbBackend::MySql,
417    ///         r#"INSERT INTO `cake` (`name`) VALUES (?)"#,
418    ///         ["Apple Pie".into()]
419    ///     )]
420    /// );
421    /// #
422    /// # Ok(())
423    /// # }
424    /// ```
425    ///
426    /// To get back inserted Model
427    ///
428    /// ```
429    /// # use sea_orm::{error::*, tests_cfg::*, *};
430    /// #
431    /// # #[cfg(feature = "mock")]
432    /// # pub fn main() -> Result<(), DbErr> {
433    /// #
434    /// # let db = MockDatabase::new(DbBackend::Postgres)
435    /// #     .append_query_results([
436    /// #         [cake::Model {
437    /// #             id: 1,
438    /// #             name: "Apple Pie".to_owned(),
439    /// #         }],
440    /// #     ])
441    /// #     .into_connection();
442    /// #
443    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
444    ///
445    /// assert_eq!(
446    ///     cake::Entity::insert(cake::ActiveModel {
447    ///         id: NotSet,
448    ///         name: Set("Apple Pie".to_owned()),
449    ///     })
450    ///     .exec_with_returning(&db)?,
451    ///     cake::Model {
452    ///         id: 1,
453    ///         name: "Apple Pie".to_owned(),
454    ///     }
455    /// );
456    ///
457    /// assert_eq!(
458    ///     db.into_transaction_log()[0].statements()[0].sql,
459    ///     r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id", "name""#
460    /// );
461    /// #
462    /// # Ok(())
463    /// # }
464    /// ```
465    fn insert<A>(model: A) -> Insert<A>
466    where
467        A: ActiveModelTrait<Entity = Self>,
468    {
469        Insert::one(model)
470    }
471
472    /// Insert many models into database
473    ///
474    /// # Example (Postgres)
475    ///
476    /// ```
477    /// # use sea_orm::{error::*, tests_cfg::*, *};
478    /// #
479    /// # #[cfg(feature = "mock")]
480    /// # pub fn main() -> Result<(), DbErr> {
481    /// #
482    /// # let db = MockDatabase::new(DbBackend::Postgres)
483    /// #     .append_query_results([[maplit::btreemap! {
484    /// #         "id" => Into::<Value>::into(28),
485    /// #     }]])
486    /// #     .into_connection();
487    /// #
488    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
489    ///
490    /// let apple = cake::ActiveModel {
491    ///     name: Set("Apple Pie".to_owned()),
492    ///     ..Default::default()
493    /// };
494    /// let orange = cake::ActiveModel {
495    ///     name: Set("Orange Scone".to_owned()),
496    ///     ..Default::default()
497    /// };
498    ///
499    /// let insert_result = cake::Entity::insert_many::<cake::ActiveModel, _>([]).exec(&db)?;
500    ///
501    /// assert_eq!(insert_result.last_insert_id, None);
502    ///
503    /// let insert_result = cake::Entity::insert_many([apple, orange]).exec(&db)?;
504    ///
505    /// assert_eq!(insert_result.last_insert_id, Some(28));
506    ///
507    /// assert_eq!(
508    ///     db.into_transaction_log(),
509    ///     [Transaction::from_sql_and_values(
510    ///         DbBackend::Postgres,
511    ///         r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id""#,
512    ///         ["Apple Pie".into(), "Orange Scone".into()]
513    ///     )]
514    /// );
515    /// #
516    /// # Ok(())
517    /// # }
518    /// ```
519    ///
520    /// # Example (MySQL)
521    ///
522    /// ```
523    /// # use sea_orm::{error::*, tests_cfg::*, *};
524    /// #
525    /// # #[cfg(feature = "mock")]
526    /// # pub fn main() -> Result<(), DbErr> {
527    /// #
528    /// # let db = MockDatabase::new(DbBackend::MySql)
529    /// #     .append_exec_results([
530    /// #         MockExecResult {
531    /// #             last_insert_id: 28,
532    /// #             rows_affected: 2,
533    /// #         },
534    /// #     ])
535    /// #     .into_connection();
536    /// #
537    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
538    ///
539    /// let apple = cake::ActiveModel {
540    ///     name: Set("Apple Pie".to_owned()),
541    ///     ..Default::default()
542    /// };
543    /// let orange = cake::ActiveModel {
544    ///     name: Set("Orange Scone".to_owned()),
545    ///     ..Default::default()
546    /// };
547    ///
548    /// let insert_result = cake::Entity::insert_many([apple, orange]).exec(&db)?;
549    ///
550    /// assert_eq!(insert_result.last_insert_id, Some(28));
551    ///
552    /// assert_eq!(
553    ///     db.into_transaction_log(),
554    ///     [Transaction::from_sql_and_values(
555    ///         DbBackend::MySql,
556    ///         r#"INSERT INTO `cake` (`name`) VALUES (?), (?)"#,
557    ///         ["Apple Pie".into(), "Orange Scone".into()]
558    ///     )]
559    /// );
560    /// #
561    /// # Ok(())
562    /// # }
563    /// ```
564    ///
565    /// Before 1.1.3, if the active models have different column set, this method would panic.
566    /// Now, it'd attempt to fill in the missing columns with null
567    /// (which may or may not be correct, depending on whether the column is nullable):
568    ///
569    /// ```
570    /// use sea_orm::{
571    ///     DbBackend,
572    ///     entity::*,
573    ///     query::*,
574    ///     tests_cfg::{cake, cake_filling},
575    /// };
576    ///
577    /// assert_eq!(
578    ///     cake::Entity::insert_many([
579    ///         cake::ActiveModel {
580    ///             id: NotSet,
581    ///             name: Set("Apple Pie".to_owned()),
582    ///         },
583    ///         cake::ActiveModel {
584    ///             id: NotSet,
585    ///             name: Set("Orange Scone".to_owned()),
586    ///         }
587    ///     ])
588    ///     .build(DbBackend::Postgres)
589    ///     .to_string(),
590    ///     r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
591    /// );
592    ///
593    /// assert_eq!(
594    ///     cake_filling::Entity::insert_many([
595    ///         cake_filling::ActiveModel {
596    ///             cake_id: ActiveValue::set(2),
597    ///             filling_id: ActiveValue::NotSet,
598    ///         },
599    ///         cake_filling::ActiveModel {
600    ///             cake_id: ActiveValue::NotSet,
601    ///             filling_id: ActiveValue::set(3),
602    ///         }
603    ///     ])
604    ///     .build(DbBackend::Postgres)
605    ///     .to_string(),
606    ///     r#"INSERT INTO "cake_filling" ("cake_id", "filling_id") VALUES (2, NULL), (NULL, 3)"#,
607    /// );
608    /// ```
609    ///
610    /// To get back inserted Models
611    ///
612    /// ```
613    /// # use sea_orm::{error::*, tests_cfg::*, *};
614    /// #
615    /// # #[cfg(feature = "mock")]
616    /// # pub fn main() -> Result<(), DbErr> {
617    /// #
618    /// # let db = MockDatabase::new(DbBackend::Postgres)
619    /// #     .append_query_results([
620    /// #         [cake::Model {
621    /// #             id: 1,
622    /// #             name: "Apple Pie".to_owned(),
623    /// #         }, cake::Model {
624    /// #             id: 2,
625    /// #             name: "Choco Pie".to_owned(),
626    /// #         }],
627    /// #     ])
628    /// #     .into_connection();
629    /// #
630    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
631    ///
632    /// assert_eq!(
633    ///     cake::Entity::insert_many([
634    ///         cake::ActiveModel {
635    ///             id: NotSet,
636    ///             name: Set("Apple Pie".to_owned()),
637    ///         },
638    ///         cake::ActiveModel {
639    ///             id: NotSet,
640    ///             name: Set("Choco Pie".to_owned()),
641    ///         },
642    ///     ])
643    ///     .exec_with_returning(&db)?,
644    ///     [
645    ///         cake::Model {
646    ///             id: 1,
647    ///             name: "Apple Pie".to_owned(),
648    ///         },
649    ///         cake::Model {
650    ///             id: 2,
651    ///             name: "Choco Pie".to_owned(),
652    ///         }
653    ///     ]
654    /// );
655    ///
656    /// assert_eq!(
657    ///     db.into_transaction_log()[0].statements()[0].sql,
658    ///     r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id", "name""#
659    /// );
660    /// #
661    /// # Ok(())
662    /// # }
663    /// ```
664    fn insert_many<A, I>(models: I) -> InsertMany<A>
665    where
666        A: ActiveModelTrait<Entity = Self>,
667        I: IntoIterator<Item = A>,
668    {
669        InsertMany::many(models)
670    }
671
672    /// Update a model in database
673    ///
674    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
675    ///
676    /// # Example (Postgres)
677    ///
678    /// ```
679    /// # use sea_orm::{error::*, tests_cfg::*, *};
680    /// #
681    /// # #[cfg(feature = "mock")]
682    /// # pub fn main() -> Result<(), DbErr> {
683    /// #
684    /// # let db = MockDatabase::new(DbBackend::Postgres)
685    /// #     .append_query_results([
686    /// #         [fruit::Model {
687    /// #             id: 1,
688    /// #             name: "Orange".to_owned(),
689    /// #             cake_id: None,
690    /// #         }],
691    /// #     ])
692    /// #     .into_connection();
693    /// #
694    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
695    ///
696    /// let orange = fruit::ActiveModel {
697    ///     id: Set(1),
698    ///     name: Set("Orange".to_owned()),
699    ///     ..Default::default()
700    /// };
701    ///
702    /// assert_eq!(
703    ///     fruit::Entity::update(orange.clone())
704    ///         .validate()?
705    ///         .filter(fruit::Column::Name.contains("orange"))
706    ///         .exec(&db)
707    ///         ?,
708    ///     fruit::Model {
709    ///         id: 1,
710    ///         name: "Orange".to_owned(),
711    ///         cake_id: None,
712    ///     }
713    /// );
714    ///
715    /// assert_eq!(
716    ///     db.into_transaction_log(),
717    ///     [Transaction::from_sql_and_values(
718    ///         DbBackend::Postgres,
719    ///         r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 AND "fruit"."name" LIKE $3 RETURNING "id", "name", "cake_id""#,
720    ///         ["Orange".into(), 1i32.into(), "%orange%".into()]
721    ///     )]);
722    /// #
723    /// # Ok(())
724    /// # }
725    /// ```
726    ///
727    /// # Example (MySQL)
728    ///
729    /// ```
730    /// # use sea_orm::{error::*, tests_cfg::*, *};
731    /// #
732    /// # #[cfg(feature = "mock")]
733    /// # pub fn main() -> Result<(), DbErr> {
734    /// #
735    /// # let db = MockDatabase::new(DbBackend::MySql)
736    /// #     .append_exec_results([
737    /// #         MockExecResult {
738    /// #             last_insert_id: 0,
739    /// #             rows_affected: 1,
740    /// #         },
741    /// #     ])
742    /// #     .append_query_results([
743    /// #         [fruit::Model {
744    /// #             id: 1,
745    /// #             name: "Orange".to_owned(),
746    /// #             cake_id: None,
747    /// #         }],
748    /// #     ])
749    /// #     .into_connection();
750    /// #
751    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
752    ///
753    /// let orange = fruit::ActiveModel {
754    ///     id: Set(1),
755    ///     name: Set("Orange".to_owned()),
756    ///     ..Default::default()
757    /// };
758    ///
759    /// assert_eq!(
760    ///     fruit::Entity::update(orange.clone())
761    ///         .validate()?
762    ///         .filter(fruit::Column::Name.contains("orange"))
763    ///         .exec(&db)
764    ///         ?,
765    ///     fruit::Model {
766    ///         id: 1,
767    ///         name: "Orange".to_owned(),
768    ///         cake_id: None,
769    ///     }
770    /// );
771    ///
772    /// assert_eq!(
773    ///     db.into_transaction_log(),
774    ///     [
775    ///         Transaction::from_sql_and_values(
776    ///             DbBackend::MySql,
777    ///             r#"UPDATE `fruit` SET `name` = ? WHERE `fruit`.`id` = ? AND `fruit`.`name` LIKE ?"#,
778    ///             ["Orange".into(), 1i32.into(), "%orange%".into()]
779    ///         ),
780    ///         Transaction::from_sql_and_values(
781    ///             DbBackend::MySql,
782    ///             r#"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = ? LIMIT ?"#,
783    ///             [1i32.into(), 1u64.into()]
784    ///         )]);
785    /// #
786    /// # Ok(())
787    /// # }
788    /// ```
789    fn update<A>(model: A) -> UpdateOne<A>
790    where
791        A: ActiveModelTrait<Entity = Self>,
792    {
793        Update::one(model)
794    }
795
796    /// Update many models in database
797    ///
798    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
799    ///
800    /// # Example
801    ///
802    /// ```
803    /// # use sea_orm::{error::*, tests_cfg::*, *};
804    /// #
805    /// # #[cfg(feature = "mock")]
806    /// # pub fn main() -> Result<(), DbErr> {
807    /// #
808    /// # let db = MockDatabase::new(DbBackend::Postgres)
809    /// #     .append_exec_results([
810    /// #         MockExecResult {
811    /// #             last_insert_id: 0,
812    /// #             rows_affected: 5,
813    /// #         },
814    /// #     ])
815    /// #     .into_connection();
816    /// #
817    /// use sea_orm::{
818    ///     entity::*,
819    ///     query::*,
820    ///     sea_query::{Expr, Value},
821    ///     tests_cfg::fruit,
822    /// };
823    ///
824    /// let update_result = fruit::Entity::update_many()
825    ///     .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
826    ///     .filter(fruit::Column::Name.contains("Apple"))
827    ///     .exec(&db)?;
828    ///
829    /// assert_eq!(update_result.rows_affected, 5);
830    ///
831    /// assert_eq!(
832    ///     db.into_transaction_log(),
833    ///     [Transaction::from_sql_and_values(
834    ///         DbBackend::Postgres,
835    ///         r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#,
836    ///         [Value::Int(None), "%Apple%".into()]
837    ///     )]
838    /// );
839    /// #
840    /// # Ok(())
841    /// # }
842    /// ```
843    fn update_many() -> UpdateMany<Self> {
844        Update::many(Self::default())
845    }
846
847    /// Delete a model from database
848    ///
849    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
850    ///
851    /// # Example
852    ///
853    /// ```
854    /// # use sea_orm::{error::*, tests_cfg::*, *};
855    /// #
856    /// # #[cfg(feature = "mock")]
857    /// # pub fn main() -> Result<(), DbErr> {
858    /// #
859    /// # let db = MockDatabase::new(DbBackend::Postgres)
860    /// #     .append_exec_results([
861    /// #         MockExecResult {
862    /// #             last_insert_id: 0,
863    /// #             rows_affected: 1,
864    /// #         },
865    /// #     ])
866    /// #     .into_connection();
867    /// #
868    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
869    ///
870    /// let orange = fruit::ActiveModel {
871    ///     id: Set(3),
872    ///     ..Default::default()
873    /// };
874    ///
875    /// let delete_result = fruit::Entity::delete(orange).exec(&db)?;
876    ///
877    /// assert_eq!(delete_result.rows_affected, 1);
878    ///
879    /// assert_eq!(
880    ///     db.into_transaction_log(),
881    ///     [Transaction::from_sql_and_values(
882    ///         DbBackend::Postgres,
883    ///         r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
884    ///         [3i32.into()]
885    ///     )]
886    /// );
887    /// #
888    /// # Ok(())
889    /// # }
890    /// ```
891    fn delete<A>(model: A) -> DeleteOne<Self>
892    where
893        A: ActiveModelTrait<Entity = Self>,
894    {
895        Delete::one(model)
896    }
897
898    /// Delete many models from database
899    ///
900    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
901    ///
902    /// # Example
903    ///
904    /// ```
905    /// # use sea_orm::{error::*, tests_cfg::*, *};
906    /// #
907    /// # #[cfg(feature = "mock")]
908    /// # pub fn main() -> Result<(), DbErr> {
909    /// #
910    /// # let db = MockDatabase::new(DbBackend::Postgres)
911    /// #     .append_exec_results([
912    /// #         MockExecResult {
913    /// #             last_insert_id: 0,
914    /// #             rows_affected: 5,
915    /// #         },
916    /// #     ])
917    /// #     .append_query_results([
918    /// #         [cake::Model {
919    /// #             id: 15,
920    /// #             name: "Apple Pie".to_owned(),
921    /// #         }],
922    /// #     ])
923    /// #     .into_connection();
924    /// #
925    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
926    ///
927    /// let delete_result = fruit::Entity::delete_many()
928    ///     .filter(fruit::Column::Name.contains("Apple"))
929    ///     .exec(&db)?;
930    ///
931    /// assert_eq!(delete_result.rows_affected, 5);
932    ///
933    /// assert_eq!(
934    ///     db.into_transaction_log(),
935    ///     [Transaction::from_sql_and_values(
936    ///         DbBackend::Postgres,
937    ///         r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#,
938    ///         ["%Apple%".into()]
939    ///     )]
940    /// );
941    /// #
942    /// # Ok(())
943    /// # }
944    /// ```
945    fn delete_many() -> DeleteMany<Self> {
946        Delete::many(Self::default())
947    }
948
949    /// Delete a model based on primary key
950    ///
951    /// # Example
952    ///
953    /// ```
954    /// # use sea_orm::{error::*, tests_cfg::*, *};
955    /// #
956    /// # #[cfg(feature = "mock")]
957    /// # pub fn main() -> Result<(), DbErr> {
958    /// #
959    /// # let db = MockDatabase::new(DbBackend::Postgres)
960    /// #     .append_exec_results([
961    /// #         MockExecResult {
962    /// #             last_insert_id: 0,
963    /// #             rows_affected: 1,
964    /// #         },
965    /// #     ])
966    /// #     .into_connection();
967    /// #
968    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
969    ///
970    /// let delete_result = fruit::Entity::delete_by_id(1).exec(&db)?;
971    ///
972    /// assert_eq!(delete_result.rows_affected, 1);
973    ///
974    /// assert_eq!(
975    ///     db.into_transaction_log(),
976    ///     [Transaction::from_sql_and_values(
977    ///         DbBackend::Postgres,
978    ///         r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
979    ///         [1i32.into()]
980    ///     )]
981    /// );
982    /// #
983    /// # Ok(())
984    /// # }
985    /// ```
986    /// Delete by composite key
987    /// ```
988    /// # use sea_orm::{error::*, tests_cfg::*, *};
989    /// #
990    /// # #[cfg(feature = "mock")]
991    /// # pub fn main() -> Result<(), DbErr> {
992    ///
993    /// # let db = MockDatabase::new(DbBackend::Postgres)
994    /// #     .append_exec_results([
995    /// #         MockExecResult {
996    /// #             last_insert_id: 0,
997    /// #             rows_affected: 1,
998    /// #         },
999    /// #     ])
1000    /// #     .into_connection();
1001    /// #
1002    /// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
1003    ///
1004    /// let delete_result = cake_filling::Entity::delete_by_id((2, 3)).exec(&db)?;
1005    ///
1006    /// assert_eq!(delete_result.rows_affected, 1);
1007    ///
1008    /// assert_eq!(
1009    ///     db.into_transaction_log(),
1010    ///     [Transaction::from_sql_and_values(
1011    ///         DbBackend::Postgres,
1012    ///         r#"DELETE FROM "cake_filling" WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
1013    ///         [2i32.into(), 3i32.into()]
1014    ///     )]
1015    /// );
1016    /// #
1017    /// # Ok(())
1018    /// # }
1019    /// ```
1020    fn delete_by_id<T>(values: T) -> ValidatedDeleteOne<Self>
1021    where
1022        T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
1023    {
1024        let mut am = Self::ActiveModel::default();
1025        let mut keys = Self::PrimaryKey::iter();
1026        for v in values.into().into_value_tuple() {
1027            if let Some(key) = keys.next() {
1028                let col = key.into_column();
1029                am.set(col, v);
1030            } else {
1031                unreachable!("primary key arity mismatch");
1032            }
1033        }
1034        Delete::one(am).validate().expect("Must be valid")
1035    }
1036}
1037
1038#[cfg(test)]
1039mod tests {
1040    #[test]
1041    fn test_delete_by_id_1() {
1042        use crate::tests_cfg::cake;
1043        use crate::{DbBackend, entity::*, query::*};
1044        assert_eq!(
1045            cake::Entity::delete_by_id(1)
1046                .build(DbBackend::Sqlite)
1047                .to_string(),
1048            r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
1049        );
1050    }
1051
1052    #[test]
1053    fn test_delete_by_id_2() {
1054        use crate::tests_cfg::cake_filling_price;
1055        use crate::{DbBackend, entity::*, query::*};
1056        assert_eq!(
1057            cake_filling_price::Entity::delete_by_id((1, 2))
1058                .build(DbBackend::Sqlite)
1059                .to_string(),
1060            r#"DELETE FROM "public"."cake_filling_price" WHERE "cake_filling_price"."cake_id" = 1 AND "cake_filling_price"."filling_id" = 2"#,
1061        );
1062    }
1063
1064    #[test]
1065    #[cfg(feature = "macros")]
1066    fn entity_model_1() {
1067        use crate::entity::*;
1068
1069        mod hello {
1070            use crate as sea_orm;
1071            use crate::entity::prelude::*;
1072
1073            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1074            #[sea_orm(table_name = "hello")]
1075            pub struct Model {
1076                #[sea_orm(primary_key)]
1077                pub id: i32,
1078            }
1079
1080            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1081            pub enum Relation {}
1082
1083            impl ActiveModelBehavior for ActiveModel {}
1084        }
1085
1086        assert_eq!(hello::Entity.table_name(), "hello");
1087        assert_eq!(hello::Entity.schema_name(), None);
1088    }
1089
1090    #[test]
1091    #[cfg(feature = "macros")]
1092    fn entity_model_2() {
1093        use crate::entity::*;
1094
1095        mod hello {
1096            use crate as sea_orm;
1097            use crate::entity::prelude::*;
1098
1099            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1100            #[sea_orm(table_name = "hello", schema_name = "world")]
1101            pub struct Model {
1102                #[sea_orm(primary_key)]
1103                pub id: i32,
1104            }
1105
1106            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1107            pub enum Relation {}
1108
1109            impl ActiveModelBehavior for ActiveModel {}
1110        }
1111
1112        assert_eq!(hello::Entity.table_name(), "hello");
1113        assert_eq!(hello::Entity.schema_name(), Some("world"));
1114    }
1115
1116    #[test]
1117    #[cfg(feature = "macros")]
1118    fn entity_model_3() {
1119        use crate::{DbBackend, entity::*, query::*};
1120        use std::borrow::Cow;
1121
1122        mod hello {
1123            use crate as sea_orm;
1124            use crate::entity::prelude::*;
1125
1126            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1127            #[sea_orm(table_name = "hello", schema_name = "world")]
1128            pub struct Model {
1129                #[sea_orm(primary_key, auto_increment = false)]
1130                pub id: String,
1131            }
1132
1133            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1134            pub enum Relation {}
1135
1136            impl ActiveModelBehavior for ActiveModel {}
1137        }
1138
1139        fn delete_by_id<T>(value: T)
1140        where
1141            T: Into<<<hello::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType>,
1142        {
1143            assert_eq!(
1144                hello::Entity::delete_by_id(value)
1145                    .build(DbBackend::Sqlite)
1146                    .to_string(),
1147                r#"DELETE FROM "world"."hello" WHERE "hello"."id" = 'UUID'"#
1148            );
1149        }
1150
1151        delete_by_id("UUID".to_string());
1152        delete_by_id("UUID");
1153        delete_by_id(Cow::from("UUID"));
1154    }
1155
1156    #[test]
1157    fn test_find_by_id() {
1158        use crate::tests_cfg::{cake, cake_filling};
1159        use crate::{DbBackend, EntityTrait, MockDatabase};
1160
1161        let db = MockDatabase::new(DbBackend::MySql).into_connection();
1162
1163        cake::Entity::find_by_id(1).all(&db).ok();
1164        cake_filling::Entity::find_by_id((2, 3)).all(&db).ok();
1165
1166        // below does not compile:
1167
1168        // cake::Entity::find_by_id((1, 2)).all(&db).ok();
1169        // cake_filling::Entity::find_by_id(1).all(&db).ok();
1170        // cake_filling::Entity::find_by_id((1, 2, 3))
1171        //     .all(&db)
1172        //
1173        //     .ok();
1174    }
1175
1176    #[test]
1177    fn test_triangle() {
1178        mod triangle {
1179            use crate as sea_orm;
1180            use sea_orm::entity::prelude::*;
1181            use serde::{Deserialize, Serialize};
1182
1183            #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1184            #[sea_orm(table_name = "triangle")]
1185            pub struct Model {
1186                #[sea_orm(primary_key)]
1187                pub id: i32,
1188                pub p1: Point,
1189                pub p2: Point,
1190                pub p3: Point,
1191            }
1192
1193            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1194            pub enum Relation {}
1195
1196            impl ActiveModelBehavior for ActiveModel {}
1197
1198            #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, FromJsonQueryResult)]
1199            pub struct Point {
1200                pub x: f64,
1201                pub y: f64,
1202            }
1203        }
1204        use triangle::{Model as Triangle, Point};
1205
1206        impl Triangle {
1207            fn area(&self) -> f64 {
1208                let a = self.p1.distance_to(&self.p2);
1209                let b = self.p2.distance_to(&self.p3);
1210                let c = self.p3.distance_to(&self.p1);
1211                let s = (a + b + c) / 2.0;
1212                (s * (s - a) * (s - b) * (s - c)).sqrt()
1213            }
1214        }
1215
1216        impl Point {
1217            fn distance_to(&self, p: &Point) -> f64 {
1218                let dx = self.x - p.x;
1219                let dy = self.y - p.y;
1220                (dx * dx + dy * dy).sqrt()
1221            }
1222        }
1223
1224        assert!(
1225            (Triangle {
1226                id: 1,
1227                p1: Point { x: 0., y: 0. },
1228                p2: Point { x: 2., y: 0. },
1229                p3: Point { x: 0., y: 2. },
1230            }
1231            .area()
1232                - 2.)
1233                .abs()
1234                < 0.00000001
1235        );
1236    }
1237}