sea_orm/entity/
base_entity.rs

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