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::{Alias, 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) => (Alias::new(schema).into_iden(), 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 /// .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 /// .filter(fruit::Column::Name.contains("orange"))
723 /// .exec(&db)
724 /// .await?,
725 /// fruit::Model {
726 /// id: 1,
727 /// name: "Orange".to_owned(),
728 /// cake_id: None,
729 /// }
730 /// );
731 ///
732 /// assert_eq!(
733 /// db.into_transaction_log(),
734 /// [
735 /// Transaction::from_sql_and_values(
736 /// DbBackend::MySql,
737 /// r#"UPDATE `fruit` SET `name` = ? WHERE `fruit`.`id` = ? AND `fruit`.`name` LIKE ?"#,
738 /// ["Orange".into(), 1i32.into(), "%orange%".into()]
739 /// ),
740 /// Transaction::from_sql_and_values(
741 /// DbBackend::MySql,
742 /// r#"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = ? LIMIT ?"#,
743 /// [1i32.into(), 1u64.into()]
744 /// )]);
745 /// #
746 /// # Ok(())
747 /// # }
748 /// ```
749 fn update<A>(model: A) -> UpdateOne<A>
750 where
751 A: ActiveModelTrait<Entity = Self>,
752 {
753 Update::one(model)
754 }
755
756 /// Update many models in database
757 ///
758 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
759 ///
760 /// # Example
761 ///
762 /// ```
763 /// # use sea_orm::{error::*, tests_cfg::*, *};
764 /// #
765 /// # #[smol_potat::main]
766 /// # #[cfg(feature = "mock")]
767 /// # pub async fn main() -> Result<(), DbErr> {
768 /// #
769 /// # let db = MockDatabase::new(DbBackend::Postgres)
770 /// # .append_exec_results([
771 /// # MockExecResult {
772 /// # last_insert_id: 0,
773 /// # rows_affected: 5,
774 /// # },
775 /// # ])
776 /// # .into_connection();
777 /// #
778 /// use sea_orm::{
779 /// entity::*,
780 /// query::*,
781 /// sea_query::{Expr, Value},
782 /// tests_cfg::fruit,
783 /// };
784 ///
785 /// let update_result = fruit::Entity::update_many()
786 /// .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
787 /// .filter(fruit::Column::Name.contains("Apple"))
788 /// .exec(&db)
789 /// .await?;
790 ///
791 /// assert_eq!(update_result.rows_affected, 5);
792 ///
793 /// assert_eq!(
794 /// db.into_transaction_log(),
795 /// [Transaction::from_sql_and_values(
796 /// DbBackend::Postgres,
797 /// r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#,
798 /// [Value::Int(None), "%Apple%".into()]
799 /// )]
800 /// );
801 /// #
802 /// # Ok(())
803 /// # }
804 /// ```
805 fn update_many() -> UpdateMany<Self> {
806 Update::many(Self::default())
807 }
808
809 /// Delete a model from database
810 ///
811 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
812 ///
813 /// # Example
814 ///
815 /// ```
816 /// # use sea_orm::{error::*, tests_cfg::*, *};
817 /// #
818 /// # #[smol_potat::main]
819 /// # #[cfg(feature = "mock")]
820 /// # pub async fn main() -> Result<(), DbErr> {
821 /// #
822 /// # let db = MockDatabase::new(DbBackend::Postgres)
823 /// # .append_exec_results([
824 /// # MockExecResult {
825 /// # last_insert_id: 0,
826 /// # rows_affected: 1,
827 /// # },
828 /// # ])
829 /// # .into_connection();
830 /// #
831 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
832 ///
833 /// let orange = fruit::ActiveModel {
834 /// id: Set(3),
835 /// ..Default::default()
836 /// };
837 ///
838 /// let delete_result = fruit::Entity::delete(orange).exec(&db).await?;
839 ///
840 /// assert_eq!(delete_result.rows_affected, 1);
841 ///
842 /// assert_eq!(
843 /// db.into_transaction_log(),
844 /// [Transaction::from_sql_and_values(
845 /// DbBackend::Postgres,
846 /// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
847 /// [3i32.into()]
848 /// )]
849 /// );
850 /// #
851 /// # Ok(())
852 /// # }
853 /// ```
854 fn delete<A>(model: A) -> DeleteOne<A>
855 where
856 A: ActiveModelTrait<Entity = Self>,
857 {
858 Delete::one(model)
859 }
860
861 /// Delete many models from database
862 ///
863 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
864 ///
865 /// # Example
866 ///
867 /// ```
868 /// # use sea_orm::{error::*, tests_cfg::*, *};
869 /// #
870 /// # #[smol_potat::main]
871 /// # #[cfg(feature = "mock")]
872 /// # pub async fn main() -> Result<(), DbErr> {
873 /// #
874 /// # let db = MockDatabase::new(DbBackend::Postgres)
875 /// # .append_exec_results([
876 /// # MockExecResult {
877 /// # last_insert_id: 0,
878 /// # rows_affected: 5,
879 /// # },
880 /// # ])
881 /// # .append_query_results([
882 /// # [cake::Model {
883 /// # id: 15,
884 /// # name: "Apple Pie".to_owned(),
885 /// # }],
886 /// # ])
887 /// # .into_connection();
888 /// #
889 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
890 ///
891 /// let delete_result = fruit::Entity::delete_many()
892 /// .filter(fruit::Column::Name.contains("Apple"))
893 /// .exec(&db)
894 /// .await?;
895 ///
896 /// assert_eq!(delete_result.rows_affected, 5);
897 ///
898 /// assert_eq!(
899 /// db.into_transaction_log(),
900 /// [Transaction::from_sql_and_values(
901 /// DbBackend::Postgres,
902 /// r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#,
903 /// ["%Apple%".into()]
904 /// )]
905 /// );
906 /// #
907 /// # Ok(())
908 /// # }
909 /// ```
910 fn delete_many() -> DeleteMany<Self> {
911 Delete::many(Self::default())
912 }
913
914 /// Delete a model based on primary key
915 ///
916 /// # Example
917 ///
918 /// ```
919 /// # use sea_orm::{error::*, tests_cfg::*, *};
920 /// #
921 /// # #[smol_potat::main]
922 /// # #[cfg(feature = "mock")]
923 /// # pub async fn main() -> Result<(), DbErr> {
924 /// #
925 /// # let db = MockDatabase::new(DbBackend::Postgres)
926 /// # .append_exec_results([
927 /// # MockExecResult {
928 /// # last_insert_id: 0,
929 /// # rows_affected: 1,
930 /// # },
931 /// # ])
932 /// # .into_connection();
933 /// #
934 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
935 ///
936 /// let delete_result = fruit::Entity::delete_by_id(1).exec(&db).await?;
937 ///
938 /// assert_eq!(delete_result.rows_affected, 1);
939 ///
940 /// assert_eq!(
941 /// db.into_transaction_log(),
942 /// [Transaction::from_sql_and_values(
943 /// DbBackend::Postgres,
944 /// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
945 /// [1i32.into()]
946 /// )]
947 /// );
948 /// #
949 /// # Ok(())
950 /// # }
951 /// ```
952 /// Delete by composite key
953 /// ```
954 /// # use sea_orm::{error::*, tests_cfg::*, *};
955 /// #
956 /// # #[smol_potat::main]
957 /// # #[cfg(feature = "mock")]
958 /// # pub async fn main() -> Result<(), DbErr> {
959 ///
960 /// # let db = MockDatabase::new(DbBackend::Postgres)
961 /// # .append_exec_results([
962 /// # MockExecResult {
963 /// # last_insert_id: 0,
964 /// # rows_affected: 1,
965 /// # },
966 /// # ])
967 /// # .into_connection();
968 /// #
969 /// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
970 ///
971 /// let delete_result = cake_filling::Entity::delete_by_id((2, 3)).exec(&db).await?;
972 ///
973 /// assert_eq!(delete_result.rows_affected, 1);
974 ///
975 /// assert_eq!(
976 /// db.into_transaction_log(),
977 /// [Transaction::from_sql_and_values(
978 /// DbBackend::Postgres,
979 /// r#"DELETE FROM "cake_filling" WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
980 /// [2i32.into(), 3i32.into()]
981 /// )]
982 /// );
983 /// #
984 /// # Ok(())
985 /// # }
986 /// ```
987 ///
988 /// # Panics
989 ///
990 /// Panics if arity of input values don't match arity of primary key
991 fn delete_by_id<T>(values: T) -> DeleteMany<Self>
992 where
993 T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
994 {
995 let mut delete = Self::delete_many();
996 let mut keys = Self::PrimaryKey::iter();
997 for v in values.into().into_value_tuple() {
998 if let Some(key) = keys.next() {
999 let col = key.into_column();
1000 delete = delete.filter(col.eq(v));
1001 } else {
1002 unreachable!("primary key arity mismatch");
1003 }
1004 }
1005 delete
1006 }
1007}
1008
1009#[cfg(test)]
1010mod tests {
1011 #[test]
1012 fn test_delete_by_id_1() {
1013 use crate::tests_cfg::cake;
1014 use crate::{DbBackend, entity::*, query::*};
1015 assert_eq!(
1016 cake::Entity::delete_by_id(1)
1017 .build(DbBackend::Sqlite)
1018 .to_string(),
1019 r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
1020 );
1021 }
1022
1023 #[test]
1024 fn test_delete_by_id_2() {
1025 use crate::tests_cfg::cake_filling_price;
1026 use crate::{DbBackend, entity::*, query::*};
1027 assert_eq!(
1028 cake_filling_price::Entity::delete_by_id((1, 2))
1029 .build(DbBackend::Sqlite)
1030 .to_string(),
1031 r#"DELETE FROM "public"."cake_filling_price" WHERE "cake_filling_price"."cake_id" = 1 AND "cake_filling_price"."filling_id" = 2"#,
1032 );
1033 }
1034
1035 #[test]
1036 #[cfg(feature = "macros")]
1037 fn entity_model_1() {
1038 use crate::entity::*;
1039
1040 mod hello {
1041 use crate as sea_orm;
1042 use crate::entity::prelude::*;
1043
1044 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1045 #[sea_orm(table_name = "hello")]
1046 pub struct Model {
1047 #[sea_orm(primary_key)]
1048 pub id: i32,
1049 }
1050
1051 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1052 pub enum Relation {}
1053
1054 impl ActiveModelBehavior for ActiveModel {}
1055 }
1056
1057 assert_eq!(hello::Entity.table_name(), "hello");
1058 assert_eq!(hello::Entity.schema_name(), None);
1059 }
1060
1061 #[test]
1062 #[cfg(feature = "macros")]
1063 fn entity_model_2() {
1064 use crate::entity::*;
1065
1066 mod hello {
1067 use crate as sea_orm;
1068 use crate::entity::prelude::*;
1069
1070 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1071 #[sea_orm(table_name = "hello", schema_name = "world")]
1072 pub struct Model {
1073 #[sea_orm(primary_key)]
1074 pub id: i32,
1075 }
1076
1077 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1078 pub enum Relation {}
1079
1080 impl ActiveModelBehavior for ActiveModel {}
1081 }
1082
1083 assert_eq!(hello::Entity.table_name(), "hello");
1084 assert_eq!(hello::Entity.schema_name(), Some("world"));
1085 }
1086
1087 #[test]
1088 #[cfg(feature = "macros")]
1089 fn entity_model_3() {
1090 use crate::{DbBackend, entity::*, query::*};
1091 use std::borrow::Cow;
1092
1093 mod hello {
1094 use crate as sea_orm;
1095 use crate::entity::prelude::*;
1096
1097 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1098 #[sea_orm(table_name = "hello", schema_name = "world")]
1099 pub struct Model {
1100 #[sea_orm(primary_key, auto_increment = false)]
1101 pub id: String,
1102 }
1103
1104 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1105 pub enum Relation {}
1106
1107 impl ActiveModelBehavior for ActiveModel {}
1108 }
1109
1110 fn delete_by_id<T>(value: T)
1111 where
1112 T: Into<<<hello::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType>,
1113 {
1114 assert_eq!(
1115 hello::Entity::delete_by_id(value)
1116 .build(DbBackend::Sqlite)
1117 .to_string(),
1118 r#"DELETE FROM "world"."hello" WHERE "hello"."id" = 'UUID'"#
1119 );
1120 }
1121
1122 delete_by_id("UUID".to_string());
1123 delete_by_id("UUID");
1124 delete_by_id(Cow::from("UUID"));
1125 }
1126
1127 #[smol_potat::test]
1128 async fn test_find_by_id() {
1129 use crate::tests_cfg::{cake, cake_filling};
1130 use crate::{DbBackend, EntityTrait, MockDatabase};
1131
1132 let db = MockDatabase::new(DbBackend::MySql).into_connection();
1133
1134 cake::Entity::find_by_id(1).all(&db).await.ok();
1135 cake_filling::Entity::find_by_id((2, 3)).all(&db).await.ok();
1136
1137 // below does not compile:
1138
1139 // cake::Entity::find_by_id((1, 2)).all(&db).await.ok();
1140 // cake_filling::Entity::find_by_id(1).all(&db).await.ok();
1141 // cake_filling::Entity::find_by_id((1, 2, 3))
1142 // .all(&db)
1143 // .await
1144 // .ok();
1145 }
1146}