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