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