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