sea_orm/entity/base_entity.rs
1use crate::{
2 ActiveModelBehavior, ActiveModelTrait, ColumnTrait, Delete, DeleteMany, DeleteOne,
3 FromQueryResult, Insert, ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, Related,
4 RelationBuilder, RelationTrait, RelationType, Select, Update, UpdateMany, UpdateOne,
5};
6use sea_query::{Alias, Iden, IntoIden, IntoTableRef, IntoValueTuple, TableRef};
7use std::fmt::Debug;
8pub use strum::IntoEnumIterator as Iterable;
9
10/// Ensure the identifier for an Entity can be converted to a static str
11pub trait IdenStatic: Iden + Copy + Debug + 'static {
12 /// Method to call to get the static string identity
13 fn as_str(&self) -> &str;
14}
15
16/// A Trait for mapping an Entity to a database table
17pub trait EntityName: IdenStatic + Default {
18 /// Method to get the name for the schema, defaults to [Option::None] if not set
19 fn schema_name(&self) -> Option<&str> {
20 None
21 }
22
23 /// Method to get the comment for the schema, defaults to [Option::None] if not set
24 fn comment(&self) -> Option<&str> {
25 None
26 }
27
28 /// Get the name of the table
29 fn table_name(&self) -> &str;
30
31 /// Get the name of the module from the invoking `self.table_name()`
32 fn module_name(&self) -> &str {
33 self.table_name()
34 }
35
36 /// Get the [TableRef] from invoking the `self.schema_name()`
37 fn table_ref(&self) -> TableRef {
38 match self.schema_name() {
39 Some(schema) => (Alias::new(schema).into_iden(), self.into_iden()).into_table_ref(),
40 None => self.into_table_ref(),
41 }
42 }
43}
44
45/// An abstract base class for defining Entities.
46///
47/// This trait provides an API for you to inspect it's properties
48/// - Column (implemented [`ColumnTrait`])
49/// - Relation (implemented [`RelationTrait`])
50/// - Primary Key (implemented [`PrimaryKeyTrait`] and [`PrimaryKeyToColumn`])
51///
52/// This trait also provides an API for CRUD actions
53/// - Select: `find`, `find_*`
54/// - Insert: `insert`, `insert_*`
55/// - Update: `update`, `update_*`
56/// - Delete: `delete`, `delete_*`
57pub trait EntityTrait: EntityName {
58 #[allow(missing_docs)]
59 type Model: ModelTrait<Entity = Self> + FromQueryResult;
60
61 #[allow(missing_docs)]
62 type ActiveModel: ActiveModelBehavior<Entity = Self>;
63
64 #[allow(missing_docs)]
65 type Column: ColumnTrait;
66
67 #[allow(missing_docs)]
68 type Relation: RelationTrait;
69
70 #[allow(missing_docs)]
71 type PrimaryKey: PrimaryKeyTrait + PrimaryKeyToColumn<Column = Self::Column>;
72
73 /// Construct a belongs to relation
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 select statement to find one / all models
98 ///
99 /// - To select columns, join tables and group by expressions, see [`QuerySelect`](crate::query::QuerySelect)
100 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
101 /// - To apply order by expressions, see [`QueryOrder`](crate::query::QueryOrder)
102 ///
103 /// # Example
104 ///
105 /// ```
106 /// # use sea_orm::{error::*, tests_cfg::*, *};
107 /// #
108 /// # #[smol_potat::main]
109 /// # #[cfg(feature = "mock")]
110 /// # pub async fn main() -> Result<(), DbErr> {
111 /// #
112 /// # let db = MockDatabase::new(DbBackend::Postgres)
113 /// # .append_query_results([
114 /// # vec![
115 /// # cake::Model {
116 /// # id: 1,
117 /// # name: "New York Cheese".to_owned(),
118 /// # },
119 /// # ],
120 /// # vec![
121 /// # cake::Model {
122 /// # id: 1,
123 /// # name: "New York Cheese".to_owned(),
124 /// # },
125 /// # cake::Model {
126 /// # id: 2,
127 /// # name: "Chocolate Forest".to_owned(),
128 /// # },
129 /// # ],
130 /// # ])
131 /// # .into_connection();
132 /// #
133 /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
134 ///
135 /// assert_eq!(
136 /// cake::Entity::find().one(&db).await?,
137 /// Some(cake::Model {
138 /// id: 1,
139 /// name: "New York Cheese".to_owned(),
140 /// })
141 /// );
142 ///
143 /// assert_eq!(
144 /// cake::Entity::find().all(&db).await?,
145 /// [
146 /// cake::Model {
147 /// id: 1,
148 /// name: "New York Cheese".to_owned(),
149 /// },
150 /// cake::Model {
151 /// id: 2,
152 /// name: "Chocolate Forest".to_owned(),
153 /// },
154 /// ]
155 /// );
156 ///
157 /// assert_eq!(
158 /// db.into_transaction_log(),
159 /// [
160 /// Transaction::from_sql_and_values(
161 /// DbBackend::Postgres,
162 /// r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
163 /// [1u64.into()]
164 /// ),
165 /// Transaction::from_sql_and_values(
166 /// DbBackend::Postgres,
167 /// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
168 /// []
169 /// ),
170 /// ]
171 /// );
172 /// #
173 /// # Ok(())
174 /// # }
175 /// ```
176 fn find() -> Select<Self> {
177 Select::new()
178 }
179
180 /// Find a model by primary key
181 ///
182 /// # Example
183 ///
184 /// ```
185 /// # use sea_orm::{error::*, tests_cfg::*, *};
186 /// #
187 /// # #[smol_potat::main]
188 /// # #[cfg(feature = "mock")]
189 /// # pub async fn main() -> Result<(), DbErr> {
190 /// #
191 /// # let db = MockDatabase::new(DbBackend::Postgres)
192 /// # .append_query_results([
193 /// # [
194 /// # cake::Model {
195 /// # id: 11,
196 /// # name: "Sponge Cake".to_owned(),
197 /// # },
198 /// # ],
199 /// # ])
200 /// # .into_connection();
201 /// #
202 /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
203 ///
204 /// assert_eq!(
205 /// cake::Entity::find_by_id(11).all(&db).await?,
206 /// [cake::Model {
207 /// id: 11,
208 /// name: "Sponge Cake".to_owned(),
209 /// }]
210 /// );
211 ///
212 /// assert_eq!(
213 /// db.into_transaction_log(),
214 /// [Transaction::from_sql_and_values(
215 /// DbBackend::Postgres,
216 /// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = $1"#,
217 /// [11i32.into()]
218 /// )]
219 /// );
220 /// #
221 /// # Ok(())
222 /// # }
223 /// ```
224 /// Find by composite key
225 /// ```
226 /// # use sea_orm::{error::*, tests_cfg::*, *};
227 /// #
228 /// # #[smol_potat::main]
229 /// # #[cfg(feature = "mock")]
230 /// # pub async fn main() -> Result<(), DbErr> {
231 /// #
232 /// # let db = MockDatabase::new(DbBackend::Postgres)
233 /// # .append_query_results([
234 /// # [
235 /// # cake_filling::Model {
236 /// # cake_id: 2,
237 /// # filling_id: 3,
238 /// # },
239 /// # ],
240 /// # ])
241 /// # .into_connection();
242 /// #
243 /// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
244 ///
245 /// assert_eq!(
246 /// cake_filling::Entity::find_by_id((2, 3)).all(&db).await?,
247 /// [cake_filling::Model {
248 /// cake_id: 2,
249 /// filling_id: 3,
250 /// }]
251 /// );
252 ///
253 /// assert_eq!(
254 /// db.into_transaction_log(),
255 /// [Transaction::from_sql_and_values(
256 /// DbBackend::Postgres,
257 /// [
258 /// r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
259 /// r#"WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
260 /// ].join(" ").as_str(),
261 /// [2i32.into(), 3i32.into()]
262 /// )]);
263 /// #
264 /// # Ok(())
265 /// # }
266 /// ```
267 ///
268 /// # Panics
269 ///
270 /// Panics if arity of input values don't match arity of primary key
271 fn find_by_id<T>(values: T) -> Select<Self>
272 where
273 T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
274 {
275 let mut select = Self::find();
276 let mut keys = Self::PrimaryKey::iter();
277 for v in values.into().into_value_tuple() {
278 if let Some(key) = keys.next() {
279 let col = key.into_column();
280 select = select.filter(col.eq(v));
281 } else {
282 panic!("primary key arity mismatch");
283 }
284 }
285 if keys.next().is_some() {
286 panic!("primary key arity mismatch");
287 }
288 select
289 }
290
291 /// Insert an model into database
292 ///
293 /// # Example (Postgres)
294 ///
295 /// ```
296 /// # use sea_orm::{error::*, tests_cfg::*, *};
297 /// #
298 /// # #[smol_potat::main]
299 /// # #[cfg(feature = "mock")]
300 /// # pub async fn main() -> Result<(), DbErr> {
301 /// #
302 /// # let db = MockDatabase::new(DbBackend::Postgres)
303 /// # .append_query_results([[maplit::btreemap! {
304 /// # "id" => Into::<Value>::into(15),
305 /// # }]])
306 /// # .into_connection();
307 /// #
308 /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
309 ///
310 /// let apple = cake::ActiveModel {
311 /// name: Set("Apple Pie".to_owned()),
312 /// ..Default::default()
313 /// };
314 ///
315 /// let insert_result = cake::Entity::insert(apple).exec(&db).await?;
316 ///
317 /// assert_eq!(dbg!(insert_result.last_insert_id), 15);
318 ///
319 /// assert_eq!(
320 /// db.into_transaction_log(),
321 /// [Transaction::from_sql_and_values(
322 /// DbBackend::Postgres,
323 /// r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id""#,
324 /// ["Apple Pie".into()]
325 /// )]
326 /// );
327 /// #
328 /// # Ok(())
329 /// # }
330 /// ```
331 ///
332 /// # Example (MySQL)
333 ///
334 /// ```
335 /// # use sea_orm::{error::*, tests_cfg::*, *};
336 /// #
337 /// # #[smol_potat::main]
338 /// # #[cfg(feature = "mock")]
339 /// # pub async fn main() -> Result<(), DbErr> {
340 /// #
341 /// # let db = MockDatabase::new(DbBackend::MySql)
342 /// # .append_exec_results([
343 /// # MockExecResult {
344 /// # last_insert_id: 15,
345 /// # rows_affected: 1,
346 /// # },
347 /// # ])
348 /// # .into_connection();
349 /// #
350 /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
351 ///
352 /// let apple = cake::ActiveModel {
353 /// name: Set("Apple Pie".to_owned()),
354 /// ..Default::default()
355 /// };
356 ///
357 /// let insert_result = cake::Entity::insert(apple).exec(&db).await?;
358 ///
359 /// assert_eq!(insert_result.last_insert_id, 15);
360 ///
361 /// assert_eq!(
362 /// db.into_transaction_log(),
363 /// [Transaction::from_sql_and_values(
364 /// DbBackend::MySql,
365 /// r#"INSERT INTO `cake` (`name`) VALUES (?)"#,
366 /// ["Apple Pie".into()]
367 /// )]
368 /// );
369 /// #
370 /// # Ok(())
371 /// # }
372 /// ```
373 fn insert<A>(model: A) -> Insert<A>
374 where
375 A: ActiveModelTrait<Entity = Self>,
376 {
377 Insert::one(model)
378 }
379
380 /// Insert many models into database
381 ///
382 /// # Example (Postgres)
383 ///
384 /// ```
385 /// # use sea_orm::{error::*, tests_cfg::*, *};
386 /// #
387 /// # #[smol_potat::main]
388 /// # #[cfg(feature = "mock")]
389 /// # pub async fn main() -> Result<(), DbErr> {
390 /// #
391 /// # let db = MockDatabase::new(DbBackend::Postgres)
392 /// # .append_query_results([[maplit::btreemap! {
393 /// # "id" => Into::<Value>::into(28),
394 /// # }]])
395 /// # .into_connection();
396 /// #
397 /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
398 ///
399 /// let apple = cake::ActiveModel {
400 /// name: Set("Apple Pie".to_owned()),
401 /// ..Default::default()
402 /// };
403 /// let orange = cake::ActiveModel {
404 /// name: Set("Orange Scone".to_owned()),
405 /// ..Default::default()
406 /// };
407 ///
408 /// let insert_result = cake::Entity::insert_many([apple, orange]).exec(&db).await?;
409 ///
410 /// assert_eq!(insert_result.last_insert_id, 28);
411 ///
412 /// assert_eq!(
413 /// db.into_transaction_log(),
414 /// [Transaction::from_sql_and_values(
415 /// DbBackend::Postgres,
416 /// r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id""#,
417 /// ["Apple Pie".into(), "Orange Scone".into()]
418 /// )]
419 /// );
420 /// #
421 /// # Ok(())
422 /// # }
423 /// ```
424 ///
425 /// # Example (MySQL)
426 ///
427 /// ```
428 /// # use sea_orm::{error::*, tests_cfg::*, *};
429 /// #
430 /// # #[smol_potat::main]
431 /// # #[cfg(feature = "mock")]
432 /// # pub async fn main() -> Result<(), DbErr> {
433 /// #
434 /// # let db = MockDatabase::new(DbBackend::MySql)
435 /// # .append_exec_results([
436 /// # MockExecResult {
437 /// # last_insert_id: 28,
438 /// # rows_affected: 2,
439 /// # },
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([apple, orange]).exec(&db).await?;
455 ///
456 /// assert_eq!(insert_result.last_insert_id, 28);
457 ///
458 /// assert_eq!(
459 /// db.into_transaction_log(),
460 /// [Transaction::from_sql_and_values(
461 /// DbBackend::MySql,
462 /// r#"INSERT INTO `cake` (`name`) VALUES (?), (?)"#,
463 /// ["Apple Pie".into(), "Orange Scone".into()]
464 /// )]
465 /// );
466 /// #
467 /// # Ok(())
468 /// # }
469 /// ```
470 ///
471 /// Before 1.1.3, if the active models have different column set, this method would panic.
472 /// Now, it'd attempt to fill in the missing columns with null
473 /// (which may or may not be correct, depending on whether the column is nullable):
474 ///
475 /// ```
476 /// use sea_orm::{
477 /// entity::*,
478 /// query::*,
479 /// tests_cfg::{cake, cake_filling},
480 /// DbBackend,
481 /// };
482 ///
483 /// assert_eq!(
484 /// cake::Entity::insert_many([
485 /// cake::ActiveModel {
486 /// id: NotSet,
487 /// name: Set("Apple Pie".to_owned()),
488 /// },
489 /// cake::ActiveModel {
490 /// id: NotSet,
491 /// name: Set("Orange Scone".to_owned()),
492 /// }
493 /// ])
494 /// .build(DbBackend::Postgres)
495 /// .to_string(),
496 /// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
497 /// );
498 ///
499 /// assert_eq!(
500 /// cake_filling::Entity::insert_many([
501 /// cake_filling::ActiveModel {
502 /// cake_id: ActiveValue::set(2),
503 /// filling_id: ActiveValue::NotSet,
504 /// },
505 /// cake_filling::ActiveModel {
506 /// cake_id: ActiveValue::NotSet,
507 /// filling_id: ActiveValue::set(3),
508 /// }
509 /// ])
510 /// .build(DbBackend::Postgres)
511 /// .to_string(),
512 /// r#"INSERT INTO "cake_filling" ("cake_id", "filling_id") VALUES (2, NULL), (NULL, 3)"#,
513 /// );
514 /// ```
515 fn insert_many<A, I>(models: I) -> Insert<A>
516 where
517 A: ActiveModelTrait<Entity = Self>,
518 I: IntoIterator<Item = A>,
519 {
520 Insert::many(models)
521 }
522
523 /// Update an model in database
524 ///
525 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
526 ///
527 /// # Example (Postgres)
528 ///
529 /// ```
530 /// # use sea_orm::{error::*, tests_cfg::*, *};
531 /// #
532 /// # #[smol_potat::main]
533 /// # #[cfg(feature = "mock")]
534 /// # pub async fn main() -> Result<(), DbErr> {
535 /// #
536 /// # let db = MockDatabase::new(DbBackend::Postgres)
537 /// # .append_query_results([
538 /// # [fruit::Model {
539 /// # id: 1,
540 /// # name: "Orange".to_owned(),
541 /// # cake_id: None,
542 /// # }],
543 /// # ])
544 /// # .into_connection();
545 /// #
546 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
547 ///
548 /// let orange = fruit::ActiveModel {
549 /// id: Set(1),
550 /// name: Set("Orange".to_owned()),
551 /// ..Default::default()
552 /// };
553 ///
554 /// assert_eq!(
555 /// fruit::Entity::update(orange.clone())
556 /// .filter(fruit::Column::Name.contains("orange"))
557 /// .exec(&db)
558 /// .await?,
559 /// fruit::Model {
560 /// id: 1,
561 /// name: "Orange".to_owned(),
562 /// cake_id: None,
563 /// }
564 /// );
565 ///
566 /// assert_eq!(
567 /// db.into_transaction_log(),
568 /// [Transaction::from_sql_and_values(
569 /// DbBackend::Postgres,
570 /// r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 AND "fruit"."name" LIKE $3 RETURNING "id", "name", "cake_id""#,
571 /// ["Orange".into(), 1i32.into(), "%orange%".into()]
572 /// )]);
573 /// #
574 /// # Ok(())
575 /// # }
576 /// ```
577 ///
578 /// # Example (MySQL)
579 ///
580 /// ```
581 /// # use sea_orm::{error::*, tests_cfg::*, *};
582 /// #
583 /// # #[smol_potat::main]
584 /// # #[cfg(feature = "mock")]
585 /// # pub async fn main() -> Result<(), DbErr> {
586 /// #
587 /// # let db = MockDatabase::new(DbBackend::MySql)
588 /// # .append_exec_results([
589 /// # MockExecResult {
590 /// # last_insert_id: 0,
591 /// # rows_affected: 1,
592 /// # },
593 /// # ])
594 /// # .append_query_results([
595 /// # [fruit::Model {
596 /// # id: 1,
597 /// # name: "Orange".to_owned(),
598 /// # cake_id: None,
599 /// # }],
600 /// # ])
601 /// # .into_connection();
602 /// #
603 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
604 ///
605 /// let orange = fruit::ActiveModel {
606 /// id: Set(1),
607 /// name: Set("Orange".to_owned()),
608 /// ..Default::default()
609 /// };
610 ///
611 /// assert_eq!(
612 /// fruit::Entity::update(orange.clone())
613 /// .filter(fruit::Column::Name.contains("orange"))
614 /// .exec(&db)
615 /// .await?,
616 /// fruit::Model {
617 /// id: 1,
618 /// name: "Orange".to_owned(),
619 /// cake_id: None,
620 /// }
621 /// );
622 ///
623 /// assert_eq!(
624 /// db.into_transaction_log(),
625 /// [
626 /// Transaction::from_sql_and_values(
627 /// DbBackend::MySql,
628 /// r#"UPDATE `fruit` SET `name` = ? WHERE `fruit`.`id` = ? AND `fruit`.`name` LIKE ?"#,
629 /// ["Orange".into(), 1i32.into(), "%orange%".into()]
630 /// ),
631 /// Transaction::from_sql_and_values(
632 /// DbBackend::MySql,
633 /// r#"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = ? LIMIT ?"#,
634 /// [1i32.into(), 1u64.into()]
635 /// )]);
636 /// #
637 /// # Ok(())
638 /// # }
639 /// ```
640 fn update<A>(model: A) -> UpdateOne<A>
641 where
642 A: ActiveModelTrait<Entity = Self>,
643 {
644 Update::one(model)
645 }
646
647 /// Update many models in database
648 ///
649 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
650 ///
651 /// # Example
652 ///
653 /// ```
654 /// # use sea_orm::{error::*, tests_cfg::*, *};
655 /// #
656 /// # #[smol_potat::main]
657 /// # #[cfg(feature = "mock")]
658 /// # pub async fn main() -> Result<(), DbErr> {
659 /// #
660 /// # let db = MockDatabase::new(DbBackend::Postgres)
661 /// # .append_exec_results([
662 /// # MockExecResult {
663 /// # last_insert_id: 0,
664 /// # rows_affected: 5,
665 /// # },
666 /// # ])
667 /// # .into_connection();
668 /// #
669 /// use sea_orm::{
670 /// entity::*,
671 /// query::*,
672 /// sea_query::{Expr, Value},
673 /// tests_cfg::fruit,
674 /// };
675 ///
676 /// let update_result = fruit::Entity::update_many()
677 /// .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
678 /// .filter(fruit::Column::Name.contains("Apple"))
679 /// .exec(&db)
680 /// .await?;
681 ///
682 /// assert_eq!(update_result.rows_affected, 5);
683 ///
684 /// assert_eq!(
685 /// db.into_transaction_log(),
686 /// [Transaction::from_sql_and_values(
687 /// DbBackend::Postgres,
688 /// r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#,
689 /// [Value::Int(None), "%Apple%".into()]
690 /// )]
691 /// );
692 /// #
693 /// # Ok(())
694 /// # }
695 /// ```
696 fn update_many() -> UpdateMany<Self> {
697 Update::many(Self::default())
698 }
699
700 /// Delete an model from database
701 ///
702 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
703 ///
704 /// # Example
705 ///
706 /// ```
707 /// # use sea_orm::{error::*, tests_cfg::*, *};
708 /// #
709 /// # #[smol_potat::main]
710 /// # #[cfg(feature = "mock")]
711 /// # pub async fn main() -> Result<(), DbErr> {
712 /// #
713 /// # let db = MockDatabase::new(DbBackend::Postgres)
714 /// # .append_exec_results([
715 /// # MockExecResult {
716 /// # last_insert_id: 0,
717 /// # rows_affected: 1,
718 /// # },
719 /// # ])
720 /// # .into_connection();
721 /// #
722 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
723 ///
724 /// let orange = fruit::ActiveModel {
725 /// id: Set(3),
726 /// ..Default::default()
727 /// };
728 ///
729 /// let delete_result = fruit::Entity::delete(orange).exec(&db).await?;
730 ///
731 /// assert_eq!(delete_result.rows_affected, 1);
732 ///
733 /// assert_eq!(
734 /// db.into_transaction_log(),
735 /// [Transaction::from_sql_and_values(
736 /// DbBackend::Postgres,
737 /// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
738 /// [3i32.into()]
739 /// )]
740 /// );
741 /// #
742 /// # Ok(())
743 /// # }
744 /// ```
745 fn delete<A>(model: A) -> DeleteOne<A>
746 where
747 A: ActiveModelTrait<Entity = Self>,
748 {
749 Delete::one(model)
750 }
751
752 /// Delete many models from database
753 ///
754 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
755 ///
756 /// # Example
757 ///
758 /// ```
759 /// # use sea_orm::{error::*, tests_cfg::*, *};
760 /// #
761 /// # #[smol_potat::main]
762 /// # #[cfg(feature = "mock")]
763 /// # pub async fn main() -> Result<(), DbErr> {
764 /// #
765 /// # let db = MockDatabase::new(DbBackend::Postgres)
766 /// # .append_exec_results([
767 /// # MockExecResult {
768 /// # last_insert_id: 0,
769 /// # rows_affected: 5,
770 /// # },
771 /// # ])
772 /// # .append_query_results([
773 /// # [cake::Model {
774 /// # id: 15,
775 /// # name: "Apple Pie".to_owned(),
776 /// # }],
777 /// # ])
778 /// # .into_connection();
779 /// #
780 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
781 ///
782 /// let delete_result = fruit::Entity::delete_many()
783 /// .filter(fruit::Column::Name.contains("Apple"))
784 /// .exec(&db)
785 /// .await?;
786 ///
787 /// assert_eq!(delete_result.rows_affected, 5);
788 ///
789 /// assert_eq!(
790 /// db.into_transaction_log(),
791 /// [Transaction::from_sql_and_values(
792 /// DbBackend::Postgres,
793 /// r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#,
794 /// ["%Apple%".into()]
795 /// )]
796 /// );
797 /// #
798 /// # Ok(())
799 /// # }
800 /// ```
801 fn delete_many() -> DeleteMany<Self> {
802 Delete::many(Self::default())
803 }
804
805 /// Delete a model based on primary key
806 ///
807 /// # Example
808 ///
809 /// ```
810 /// # use sea_orm::{error::*, tests_cfg::*, *};
811 /// #
812 /// # #[smol_potat::main]
813 /// # #[cfg(feature = "mock")]
814 /// # pub async fn main() -> Result<(), DbErr> {
815 /// #
816 /// # let db = MockDatabase::new(DbBackend::Postgres)
817 /// # .append_exec_results([
818 /// # MockExecResult {
819 /// # last_insert_id: 0,
820 /// # rows_affected: 1,
821 /// # },
822 /// # ])
823 /// # .into_connection();
824 /// #
825 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
826 ///
827 /// let delete_result = fruit::Entity::delete_by_id(1).exec(&db).await?;
828 ///
829 /// assert_eq!(delete_result.rows_affected, 1);
830 ///
831 /// assert_eq!(
832 /// db.into_transaction_log(),
833 /// [Transaction::from_sql_and_values(
834 /// DbBackend::Postgres,
835 /// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
836 /// [1i32.into()]
837 /// )]
838 /// );
839 /// #
840 /// # Ok(())
841 /// # }
842 /// ```
843 /// Delete by composite key
844 /// ```
845 /// # use sea_orm::{error::*, tests_cfg::*, *};
846 /// #
847 /// # #[smol_potat::main]
848 /// # #[cfg(feature = "mock")]
849 /// # pub async fn main() -> Result<(), DbErr> {
850 ///
851 /// # let db = MockDatabase::new(DbBackend::Postgres)
852 /// # .append_exec_results([
853 /// # MockExecResult {
854 /// # last_insert_id: 0,
855 /// # rows_affected: 1,
856 /// # },
857 /// # ])
858 /// # .into_connection();
859 /// #
860 /// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
861 ///
862 /// let delete_result = cake_filling::Entity::delete_by_id((2, 3)).exec(&db).await?;
863 ///
864 /// assert_eq!(delete_result.rows_affected, 1);
865 ///
866 /// assert_eq!(
867 /// db.into_transaction_log(),
868 /// [Transaction::from_sql_and_values(
869 /// DbBackend::Postgres,
870 /// r#"DELETE FROM "cake_filling" WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
871 /// [2i32.into(), 3i32.into()]
872 /// )]
873 /// );
874 /// #
875 /// # Ok(())
876 /// # }
877 /// ```
878 ///
879 /// # Panics
880 ///
881 /// Panics if arity of input values don't match arity of primary key
882 fn delete_by_id<T>(values: T) -> DeleteMany<Self>
883 where
884 T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
885 {
886 let mut delete = Self::delete_many();
887 let mut keys = Self::PrimaryKey::iter();
888 for v in values.into().into_value_tuple() {
889 if let Some(key) = keys.next() {
890 let col = key.into_column();
891 delete = delete.filter(col.eq(v));
892 } else {
893 panic!("primary key arity mismatch");
894 }
895 }
896 if keys.next().is_some() {
897 panic!("primary key arity mismatch");
898 }
899 delete
900 }
901}
902
903#[cfg(test)]
904mod tests {
905 #[test]
906 fn test_delete_by_id_1() {
907 use crate::tests_cfg::cake;
908 use crate::{entity::*, query::*, DbBackend};
909 assert_eq!(
910 cake::Entity::delete_by_id(1)
911 .build(DbBackend::Sqlite)
912 .to_string(),
913 r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
914 );
915 }
916
917 #[test]
918 fn test_delete_by_id_2() {
919 use crate::tests_cfg::cake_filling_price;
920 use crate::{entity::*, query::*, DbBackend};
921 assert_eq!(
922 cake_filling_price::Entity::delete_by_id((1, 2))
923 .build(DbBackend::Sqlite)
924 .to_string(),
925 r#"DELETE FROM "public"."cake_filling_price" WHERE "cake_filling_price"."cake_id" = 1 AND "cake_filling_price"."filling_id" = 2"#,
926 );
927 }
928
929 #[test]
930 #[cfg(feature = "macros")]
931 fn entity_model_1() {
932 use crate::entity::*;
933
934 mod hello {
935 use crate as sea_orm;
936 use crate::entity::prelude::*;
937
938 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
939 #[sea_orm(table_name = "hello")]
940 pub struct Model {
941 #[sea_orm(primary_key)]
942 pub id: i32,
943 }
944
945 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
946 pub enum Relation {}
947
948 impl ActiveModelBehavior for ActiveModel {}
949 }
950
951 assert_eq!(hello::Entity.table_name(), "hello");
952 assert_eq!(hello::Entity.schema_name(), None);
953 }
954
955 #[test]
956 #[cfg(feature = "macros")]
957 fn entity_model_2() {
958 use crate::entity::*;
959
960 mod hello {
961 use crate as sea_orm;
962 use crate::entity::prelude::*;
963
964 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
965 #[sea_orm(table_name = "hello", schema_name = "world")]
966 pub struct Model {
967 #[sea_orm(primary_key)]
968 pub id: i32,
969 }
970
971 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
972 pub enum Relation {}
973
974 impl ActiveModelBehavior for ActiveModel {}
975 }
976
977 assert_eq!(hello::Entity.table_name(), "hello");
978 assert_eq!(hello::Entity.schema_name(), Some("world"));
979 }
980
981 #[test]
982 #[cfg(feature = "macros")]
983 fn entity_model_3() {
984 use crate::{entity::*, query::*, DbBackend};
985 use std::borrow::Cow;
986
987 mod hello {
988 use crate as sea_orm;
989 use crate::entity::prelude::*;
990
991 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
992 #[sea_orm(table_name = "hello", schema_name = "world")]
993 pub struct Model {
994 #[sea_orm(primary_key, auto_increment = false)]
995 pub id: String,
996 }
997
998 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
999 pub enum Relation {}
1000
1001 impl ActiveModelBehavior for ActiveModel {}
1002 }
1003
1004 fn delete_by_id<T>(value: T)
1005 where
1006 T: Into<<<hello::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType>,
1007 {
1008 assert_eq!(
1009 hello::Entity::delete_by_id(value)
1010 .build(DbBackend::Sqlite)
1011 .to_string(),
1012 r#"DELETE FROM "world"."hello" WHERE "hello"."id" = 'UUID'"#
1013 );
1014 }
1015
1016 delete_by_id("UUID".to_string());
1017 delete_by_id("UUID");
1018 delete_by_id(Cow::from("UUID"));
1019 }
1020}