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 /// ?,
452 /// cake::Model {
453 /// id: 1,
454 /// name: "Apple Pie".to_owned(),
455 /// }
456 /// );
457 ///
458 /// assert_eq!(
459 /// db.into_transaction_log()[0].statements()[0].sql,
460 /// r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id", "name""#
461 /// );
462 /// #
463 /// # Ok(())
464 /// # }
465 /// ```
466 fn insert<A>(model: A) -> Insert<A>
467 where
468 A: ActiveModelTrait<Entity = Self>,
469 {
470 Insert::one(model)
471 }
472
473 /// Insert many models into database
474 ///
475 /// # Example (Postgres)
476 ///
477 /// ```
478 /// # use sea_orm::{error::*, tests_cfg::*, *};
479 /// #
480 /// # #[cfg(feature = "mock")]
481 /// # pub fn main() -> Result<(), DbErr> {
482 /// #
483 /// # let db = MockDatabase::new(DbBackend::Postgres)
484 /// # .append_query_results([[maplit::btreemap! {
485 /// # "id" => Into::<Value>::into(28),
486 /// # }]])
487 /// # .into_connection();
488 /// #
489 /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
490 ///
491 /// let apple = cake::ActiveModel {
492 /// name: Set("Apple Pie".to_owned()),
493 /// ..Default::default()
494 /// };
495 /// let orange = cake::ActiveModel {
496 /// name: Set("Orange Scone".to_owned()),
497 /// ..Default::default()
498 /// };
499 ///
500 /// let insert_result = cake::Entity::insert_many::<cake::ActiveModel, _>([])
501 /// .exec(&db)
502 /// ?;
503 ///
504 /// assert_eq!(insert_result.last_insert_id, None);
505 ///
506 /// let insert_result = cake::Entity::insert_many([apple, orange]).exec(&db)?;
507 ///
508 /// assert_eq!(insert_result.last_insert_id, Some(28));
509 ///
510 /// assert_eq!(
511 /// db.into_transaction_log(),
512 /// [Transaction::from_sql_and_values(
513 /// DbBackend::Postgres,
514 /// r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id""#,
515 /// ["Apple Pie".into(), "Orange Scone".into()]
516 /// )]
517 /// );
518 /// #
519 /// # Ok(())
520 /// # }
521 /// ```
522 ///
523 /// # Example (MySQL)
524 ///
525 /// ```
526 /// # use sea_orm::{error::*, tests_cfg::*, *};
527 /// #
528 /// # #[cfg(feature = "mock")]
529 /// # pub fn main() -> Result<(), DbErr> {
530 /// #
531 /// # let db = MockDatabase::new(DbBackend::MySql)
532 /// # .append_exec_results([
533 /// # MockExecResult {
534 /// # last_insert_id: 28,
535 /// # rows_affected: 2,
536 /// # },
537 /// # ])
538 /// # .into_connection();
539 /// #
540 /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
541 ///
542 /// let apple = cake::ActiveModel {
543 /// name: Set("Apple Pie".to_owned()),
544 /// ..Default::default()
545 /// };
546 /// let orange = cake::ActiveModel {
547 /// name: Set("Orange Scone".to_owned()),
548 /// ..Default::default()
549 /// };
550 ///
551 /// let insert_result = cake::Entity::insert_many([apple, orange]).exec(&db)?;
552 ///
553 /// assert_eq!(insert_result.last_insert_id, Some(28));
554 ///
555 /// assert_eq!(
556 /// db.into_transaction_log(),
557 /// [Transaction::from_sql_and_values(
558 /// DbBackend::MySql,
559 /// r#"INSERT INTO `cake` (`name`) VALUES (?), (?)"#,
560 /// ["Apple Pie".into(), "Orange Scone".into()]
561 /// )]
562 /// );
563 /// #
564 /// # Ok(())
565 /// # }
566 /// ```
567 ///
568 /// Before 1.1.3, if the active models have different column set, this method would panic.
569 /// Now, it'd attempt to fill in the missing columns with null
570 /// (which may or may not be correct, depending on whether the column is nullable):
571 ///
572 /// ```
573 /// use sea_orm::{
574 /// DbBackend,
575 /// entity::*,
576 /// query::*,
577 /// tests_cfg::{cake, cake_filling},
578 /// };
579 ///
580 /// assert_eq!(
581 /// cake::Entity::insert_many([
582 /// cake::ActiveModel {
583 /// id: NotSet,
584 /// name: Set("Apple Pie".to_owned()),
585 /// },
586 /// cake::ActiveModel {
587 /// id: NotSet,
588 /// name: Set("Orange Scone".to_owned()),
589 /// }
590 /// ])
591 /// .build(DbBackend::Postgres)
592 /// .to_string(),
593 /// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
594 /// );
595 ///
596 /// assert_eq!(
597 /// cake_filling::Entity::insert_many([
598 /// cake_filling::ActiveModel {
599 /// cake_id: ActiveValue::set(2),
600 /// filling_id: ActiveValue::NotSet,
601 /// },
602 /// cake_filling::ActiveModel {
603 /// cake_id: ActiveValue::NotSet,
604 /// filling_id: ActiveValue::set(3),
605 /// }
606 /// ])
607 /// .build(DbBackend::Postgres)
608 /// .to_string(),
609 /// r#"INSERT INTO "cake_filling" ("cake_id", "filling_id") VALUES (2, NULL), (NULL, 3)"#,
610 /// );
611 /// ```
612 ///
613 /// To get back inserted Models
614 ///
615 /// ```
616 /// # use sea_orm::{error::*, tests_cfg::*, *};
617 /// #
618 /// # #[cfg(feature = "mock")]
619 /// # pub fn main() -> Result<(), DbErr> {
620 /// #
621 /// # let db = MockDatabase::new(DbBackend::Postgres)
622 /// # .append_query_results([
623 /// # [cake::Model {
624 /// # id: 1,
625 /// # name: "Apple Pie".to_owned(),
626 /// # }, cake::Model {
627 /// # id: 2,
628 /// # name: "Choco Pie".to_owned(),
629 /// # }],
630 /// # ])
631 /// # .into_connection();
632 /// #
633 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
634 ///
635 /// assert_eq!(
636 /// cake::Entity::insert_many([
637 /// cake::ActiveModel {
638 /// id: NotSet,
639 /// name: Set("Apple Pie".to_owned()),
640 /// },
641 /// cake::ActiveModel {
642 /// id: NotSet,
643 /// name: Set("Choco Pie".to_owned()),
644 /// },
645 /// ])
646 /// .exec_with_returning(&db)
647 /// ?,
648 /// [
649 /// cake::Model {
650 /// id: 1,
651 /// name: "Apple Pie".to_owned(),
652 /// },
653 /// cake::Model {
654 /// id: 2,
655 /// name: "Choco Pie".to_owned(),
656 /// }
657 /// ]
658 /// );
659 ///
660 /// assert_eq!(
661 /// db.into_transaction_log()[0].statements()[0].sql,
662 /// r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id", "name""#
663 /// );
664 /// #
665 /// # Ok(())
666 /// # }
667 /// ```
668 fn insert_many<A, I>(models: I) -> InsertMany<A>
669 where
670 A: ActiveModelTrait<Entity = Self>,
671 I: IntoIterator<Item = A>,
672 {
673 InsertMany::many(models)
674 }
675
676 /// Update a model in database
677 ///
678 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
679 ///
680 /// # Example (Postgres)
681 ///
682 /// ```
683 /// # use sea_orm::{error::*, tests_cfg::*, *};
684 /// #
685 /// # #[cfg(feature = "mock")]
686 /// # pub fn main() -> Result<(), DbErr> {
687 /// #
688 /// # let db = MockDatabase::new(DbBackend::Postgres)
689 /// # .append_query_results([
690 /// # [fruit::Model {
691 /// # id: 1,
692 /// # name: "Orange".to_owned(),
693 /// # cake_id: None,
694 /// # }],
695 /// # ])
696 /// # .into_connection();
697 /// #
698 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
699 ///
700 /// let orange = fruit::ActiveModel {
701 /// id: Set(1),
702 /// name: Set("Orange".to_owned()),
703 /// ..Default::default()
704 /// };
705 ///
706 /// assert_eq!(
707 /// fruit::Entity::update(orange.clone())
708 /// .validate()?
709 /// .filter(fruit::Column::Name.contains("orange"))
710 /// .exec(&db)
711 /// ?,
712 /// fruit::Model {
713 /// id: 1,
714 /// name: "Orange".to_owned(),
715 /// cake_id: None,
716 /// }
717 /// );
718 ///
719 /// assert_eq!(
720 /// db.into_transaction_log(),
721 /// [Transaction::from_sql_and_values(
722 /// DbBackend::Postgres,
723 /// r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 AND "fruit"."name" LIKE $3 RETURNING "id", "name", "cake_id""#,
724 /// ["Orange".into(), 1i32.into(), "%orange%".into()]
725 /// )]);
726 /// #
727 /// # Ok(())
728 /// # }
729 /// ```
730 ///
731 /// # Example (MySQL)
732 ///
733 /// ```
734 /// # use sea_orm::{error::*, tests_cfg::*, *};
735 /// #
736 /// # #[cfg(feature = "mock")]
737 /// # pub fn main() -> Result<(), DbErr> {
738 /// #
739 /// # let db = MockDatabase::new(DbBackend::MySql)
740 /// # .append_exec_results([
741 /// # MockExecResult {
742 /// # last_insert_id: 0,
743 /// # rows_affected: 1,
744 /// # },
745 /// # ])
746 /// # .append_query_results([
747 /// # [fruit::Model {
748 /// # id: 1,
749 /// # name: "Orange".to_owned(),
750 /// # cake_id: None,
751 /// # }],
752 /// # ])
753 /// # .into_connection();
754 /// #
755 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
756 ///
757 /// let orange = fruit::ActiveModel {
758 /// id: Set(1),
759 /// name: Set("Orange".to_owned()),
760 /// ..Default::default()
761 /// };
762 ///
763 /// assert_eq!(
764 /// fruit::Entity::update(orange.clone())
765 /// .validate()?
766 /// .filter(fruit::Column::Name.contains("orange"))
767 /// .exec(&db)
768 /// ?,
769 /// fruit::Model {
770 /// id: 1,
771 /// name: "Orange".to_owned(),
772 /// cake_id: None,
773 /// }
774 /// );
775 ///
776 /// assert_eq!(
777 /// db.into_transaction_log(),
778 /// [
779 /// Transaction::from_sql_and_values(
780 /// DbBackend::MySql,
781 /// r#"UPDATE `fruit` SET `name` = ? WHERE `fruit`.`id` = ? AND `fruit`.`name` LIKE ?"#,
782 /// ["Orange".into(), 1i32.into(), "%orange%".into()]
783 /// ),
784 /// Transaction::from_sql_and_values(
785 /// DbBackend::MySql,
786 /// r#"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = ? LIMIT ?"#,
787 /// [1i32.into(), 1u64.into()]
788 /// )]);
789 /// #
790 /// # Ok(())
791 /// # }
792 /// ```
793 fn update<A>(model: A) -> UpdateOne<A>
794 where
795 A: ActiveModelTrait<Entity = Self>,
796 {
797 Update::one(model)
798 }
799
800 /// Update many models in database
801 ///
802 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
803 ///
804 /// # Example
805 ///
806 /// ```
807 /// # use sea_orm::{error::*, tests_cfg::*, *};
808 /// #
809 /// # #[cfg(feature = "mock")]
810 /// # pub fn main() -> Result<(), DbErr> {
811 /// #
812 /// # let db = MockDatabase::new(DbBackend::Postgres)
813 /// # .append_exec_results([
814 /// # MockExecResult {
815 /// # last_insert_id: 0,
816 /// # rows_affected: 5,
817 /// # },
818 /// # ])
819 /// # .into_connection();
820 /// #
821 /// use sea_orm::{
822 /// entity::*,
823 /// query::*,
824 /// sea_query::{Expr, Value},
825 /// tests_cfg::fruit,
826 /// };
827 ///
828 /// let update_result = fruit::Entity::update_many()
829 /// .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
830 /// .filter(fruit::Column::Name.contains("Apple"))
831 /// .exec(&db)
832 /// ?;
833 ///
834 /// assert_eq!(update_result.rows_affected, 5);
835 ///
836 /// assert_eq!(
837 /// db.into_transaction_log(),
838 /// [Transaction::from_sql_and_values(
839 /// DbBackend::Postgres,
840 /// r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#,
841 /// [Value::Int(None), "%Apple%".into()]
842 /// )]
843 /// );
844 /// #
845 /// # Ok(())
846 /// # }
847 /// ```
848 fn update_many() -> UpdateMany<Self> {
849 Update::many(Self::default())
850 }
851
852 /// Delete a model from database
853 ///
854 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
855 ///
856 /// # Example
857 ///
858 /// ```
859 /// # use sea_orm::{error::*, tests_cfg::*, *};
860 /// #
861 /// # #[cfg(feature = "mock")]
862 /// # pub fn main() -> Result<(), DbErr> {
863 /// #
864 /// # let db = MockDatabase::new(DbBackend::Postgres)
865 /// # .append_exec_results([
866 /// # MockExecResult {
867 /// # last_insert_id: 0,
868 /// # rows_affected: 1,
869 /// # },
870 /// # ])
871 /// # .into_connection();
872 /// #
873 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
874 ///
875 /// let orange = fruit::ActiveModel {
876 /// id: Set(3),
877 /// ..Default::default()
878 /// };
879 ///
880 /// let delete_result = fruit::Entity::delete(orange).exec(&db)?;
881 ///
882 /// assert_eq!(delete_result.rows_affected, 1);
883 ///
884 /// assert_eq!(
885 /// db.into_transaction_log(),
886 /// [Transaction::from_sql_and_values(
887 /// DbBackend::Postgres,
888 /// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
889 /// [3i32.into()]
890 /// )]
891 /// );
892 /// #
893 /// # Ok(())
894 /// # }
895 /// ```
896 fn delete<A>(model: A) -> DeleteOne<Self>
897 where
898 A: ActiveModelTrait<Entity = Self>,
899 {
900 Delete::one(model)
901 }
902
903 /// Delete many models from database
904 ///
905 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
906 ///
907 /// # Example
908 ///
909 /// ```
910 /// # use sea_orm::{error::*, tests_cfg::*, *};
911 /// #
912 /// # #[cfg(feature = "mock")]
913 /// # pub fn main() -> Result<(), DbErr> {
914 /// #
915 /// # let db = MockDatabase::new(DbBackend::Postgres)
916 /// # .append_exec_results([
917 /// # MockExecResult {
918 /// # last_insert_id: 0,
919 /// # rows_affected: 5,
920 /// # },
921 /// # ])
922 /// # .append_query_results([
923 /// # [cake::Model {
924 /// # id: 15,
925 /// # name: "Apple Pie".to_owned(),
926 /// # }],
927 /// # ])
928 /// # .into_connection();
929 /// #
930 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
931 ///
932 /// let delete_result = fruit::Entity::delete_many()
933 /// .filter(fruit::Column::Name.contains("Apple"))
934 /// .exec(&db)
935 /// ?;
936 ///
937 /// assert_eq!(delete_result.rows_affected, 5);
938 ///
939 /// assert_eq!(
940 /// db.into_transaction_log(),
941 /// [Transaction::from_sql_and_values(
942 /// DbBackend::Postgres,
943 /// r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#,
944 /// ["%Apple%".into()]
945 /// )]
946 /// );
947 /// #
948 /// # Ok(())
949 /// # }
950 /// ```
951 fn delete_many() -> DeleteMany<Self> {
952 Delete::many(Self::default())
953 }
954
955 /// Delete a model based on primary key
956 ///
957 /// # Example
958 ///
959 /// ```
960 /// # use sea_orm::{error::*, tests_cfg::*, *};
961 /// #
962 /// # #[cfg(feature = "mock")]
963 /// # pub fn main() -> Result<(), DbErr> {
964 /// #
965 /// # let db = MockDatabase::new(DbBackend::Postgres)
966 /// # .append_exec_results([
967 /// # MockExecResult {
968 /// # last_insert_id: 0,
969 /// # rows_affected: 1,
970 /// # },
971 /// # ])
972 /// # .into_connection();
973 /// #
974 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
975 ///
976 /// let delete_result = fruit::Entity::delete_by_id(1).exec(&db)?;
977 ///
978 /// assert_eq!(delete_result.rows_affected, 1);
979 ///
980 /// assert_eq!(
981 /// db.into_transaction_log(),
982 /// [Transaction::from_sql_and_values(
983 /// DbBackend::Postgres,
984 /// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
985 /// [1i32.into()]
986 /// )]
987 /// );
988 /// #
989 /// # Ok(())
990 /// # }
991 /// ```
992 /// Delete by composite key
993 /// ```
994 /// # use sea_orm::{error::*, tests_cfg::*, *};
995 /// #
996 /// # #[cfg(feature = "mock")]
997 /// # pub fn main() -> Result<(), DbErr> {
998 ///
999 /// # let db = MockDatabase::new(DbBackend::Postgres)
1000 /// # .append_exec_results([
1001 /// # MockExecResult {
1002 /// # last_insert_id: 0,
1003 /// # rows_affected: 1,
1004 /// # },
1005 /// # ])
1006 /// # .into_connection();
1007 /// #
1008 /// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
1009 ///
1010 /// let delete_result = cake_filling::Entity::delete_by_id((2, 3)).exec(&db)?;
1011 ///
1012 /// assert_eq!(delete_result.rows_affected, 1);
1013 ///
1014 /// assert_eq!(
1015 /// db.into_transaction_log(),
1016 /// [Transaction::from_sql_and_values(
1017 /// DbBackend::Postgres,
1018 /// r#"DELETE FROM "cake_filling" WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
1019 /// [2i32.into(), 3i32.into()]
1020 /// )]
1021 /// );
1022 /// #
1023 /// # Ok(())
1024 /// # }
1025 /// ```
1026 fn delete_by_id<T>(values: T) -> ValidatedDeleteOne<Self>
1027 where
1028 T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
1029 {
1030 let mut am = Self::ActiveModel::default();
1031 let mut keys = Self::PrimaryKey::iter();
1032 for v in values.into().into_value_tuple() {
1033 if let Some(key) = keys.next() {
1034 let col = key.into_column();
1035 am.set(col, v);
1036 } else {
1037 unreachable!("primary key arity mismatch");
1038 }
1039 }
1040 Delete::one(am).validate().expect("Must be valid")
1041 }
1042}
1043
1044#[cfg(test)]
1045mod tests {
1046 #[test]
1047 fn test_delete_by_id_1() {
1048 use crate::tests_cfg::cake;
1049 use crate::{DbBackend, entity::*, query::*};
1050 assert_eq!(
1051 cake::Entity::delete_by_id(1)
1052 .build(DbBackend::Sqlite)
1053 .to_string(),
1054 r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
1055 );
1056 }
1057
1058 #[test]
1059 fn test_delete_by_id_2() {
1060 use crate::tests_cfg::cake_filling_price;
1061 use crate::{DbBackend, entity::*, query::*};
1062 assert_eq!(
1063 cake_filling_price::Entity::delete_by_id((1, 2))
1064 .build(DbBackend::Sqlite)
1065 .to_string(),
1066 r#"DELETE FROM "public"."cake_filling_price" WHERE "cake_filling_price"."cake_id" = 1 AND "cake_filling_price"."filling_id" = 2"#,
1067 );
1068 }
1069
1070 #[test]
1071 #[cfg(feature = "macros")]
1072 fn entity_model_1() {
1073 use crate::entity::*;
1074
1075 mod hello {
1076 use crate as sea_orm;
1077 use crate::entity::prelude::*;
1078
1079 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1080 #[sea_orm(table_name = "hello")]
1081 pub struct Model {
1082 #[sea_orm(primary_key)]
1083 pub id: i32,
1084 }
1085
1086 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1087 pub enum Relation {}
1088
1089 impl ActiveModelBehavior for ActiveModel {}
1090 }
1091
1092 assert_eq!(hello::Entity.table_name(), "hello");
1093 assert_eq!(hello::Entity.schema_name(), None);
1094 }
1095
1096 #[test]
1097 #[cfg(feature = "macros")]
1098 fn entity_model_2() {
1099 use crate::entity::*;
1100
1101 mod hello {
1102 use crate as sea_orm;
1103 use crate::entity::prelude::*;
1104
1105 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1106 #[sea_orm(table_name = "hello", schema_name = "world")]
1107 pub struct Model {
1108 #[sea_orm(primary_key)]
1109 pub id: i32,
1110 }
1111
1112 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1113 pub enum Relation {}
1114
1115 impl ActiveModelBehavior for ActiveModel {}
1116 }
1117
1118 assert_eq!(hello::Entity.table_name(), "hello");
1119 assert_eq!(hello::Entity.schema_name(), Some("world"));
1120 }
1121
1122 #[test]
1123 #[cfg(feature = "macros")]
1124 fn entity_model_3() {
1125 use crate::{DbBackend, entity::*, query::*};
1126 use std::borrow::Cow;
1127
1128 mod hello {
1129 use crate as sea_orm;
1130 use crate::entity::prelude::*;
1131
1132 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1133 #[sea_orm(table_name = "hello", schema_name = "world")]
1134 pub struct Model {
1135 #[sea_orm(primary_key, auto_increment = false)]
1136 pub id: String,
1137 }
1138
1139 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1140 pub enum Relation {}
1141
1142 impl ActiveModelBehavior for ActiveModel {}
1143 }
1144
1145 fn delete_by_id<T>(value: T)
1146 where
1147 T: Into<<<hello::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType>,
1148 {
1149 assert_eq!(
1150 hello::Entity::delete_by_id(value)
1151 .build(DbBackend::Sqlite)
1152 .to_string(),
1153 r#"DELETE FROM "world"."hello" WHERE "hello"."id" = 'UUID'"#
1154 );
1155 }
1156
1157 delete_by_id("UUID".to_string());
1158 delete_by_id("UUID");
1159 delete_by_id(Cow::from("UUID"));
1160 }
1161
1162 #[test]
1163 fn test_find_by_id() {
1164 use crate::tests_cfg::{cake, cake_filling};
1165 use crate::{DbBackend, EntityTrait, MockDatabase};
1166
1167 let db = MockDatabase::new(DbBackend::MySql).into_connection();
1168
1169 cake::Entity::find_by_id(1).all(&db).ok();
1170 cake_filling::Entity::find_by_id((2, 3)).all(&db).ok();
1171
1172 // below does not compile:
1173
1174 // cake::Entity::find_by_id((1, 2)).all(&db).ok();
1175 // cake_filling::Entity::find_by_id(1).all(&db).ok();
1176 // cake_filling::Entity::find_by_id((1, 2, 3))
1177 // .all(&db)
1178 //
1179 // .ok();
1180 }
1181
1182 #[test]
1183 fn test_triangle() {
1184 mod triangle {
1185 use crate as sea_orm;
1186 use sea_orm::entity::prelude::*;
1187 use serde::{Deserialize, Serialize};
1188
1189 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1190 #[sea_orm(table_name = "triangle")]
1191 pub struct Model {
1192 #[sea_orm(primary_key)]
1193 pub id: i32,
1194 pub p1: Point,
1195 pub p2: Point,
1196 pub p3: Point,
1197 }
1198
1199 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1200 pub enum Relation {}
1201
1202 impl ActiveModelBehavior for ActiveModel {}
1203
1204 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, FromJsonQueryResult)]
1205 pub struct Point {
1206 pub x: f64,
1207 pub y: f64,
1208 }
1209 }
1210 use triangle::{Model as Triangle, Point};
1211
1212 impl Triangle {
1213 fn area(&self) -> f64 {
1214 let a = self.p1.distance_to(&self.p2);
1215 let b = self.p2.distance_to(&self.p3);
1216 let c = self.p3.distance_to(&self.p1);
1217 let s = (a + b + c) / 2.0;
1218 (s * (s - a) * (s - b) * (s - c)).sqrt()
1219 }
1220 }
1221
1222 impl Point {
1223 fn distance_to(&self, p: &Point) -> f64 {
1224 let dx = self.x - p.x;
1225 let dy = self.y - p.y;
1226 (dx * dx + dy * dy).sqrt()
1227 }
1228 }
1229
1230 assert!(
1231 (Triangle {
1232 id: 1,
1233 p1: Point { x: 0., y: 0. },
1234 p2: Point { x: 2., y: 0. },
1235 p3: Point { x: 0., y: 2. },
1236 }
1237 .area()
1238 - 2.)
1239 .abs()
1240 < 0.00000001
1241 );
1242 }
1243}