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