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}