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