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