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 a 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 ///
374 /// To get back inserted Model
375 ///
376 /// ```
377 /// # use sea_orm::{error::*, tests_cfg::*, *};
378 /// #
379 /// # #[smol_potat::main]
380 /// # #[cfg(feature = "mock")]
381 /// # pub async fn main() -> Result<(), DbErr> {
382 /// #
383 /// # let db = MockDatabase::new(DbBackend::Postgres)
384 /// # .append_query_results([
385 /// # [cake::Model {
386 /// # id: 1,
387 /// # name: "Apple Pie".to_owned(),
388 /// # }],
389 /// # ])
390 /// # .into_connection();
391 /// #
392 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
393 ///
394 /// assert_eq!(
395 /// cake::Entity::insert(cake::ActiveModel {
396 /// id: NotSet,
397 /// name: Set("Apple Pie".to_owned()),
398 /// })
399 /// .exec_with_returning(&db)
400 /// .await?,
401 /// cake::Model {
402 /// id: 1,
403 /// name: "Apple Pie".to_owned(),
404 /// }
405 /// );
406 ///
407 /// assert_eq!(
408 /// db.into_transaction_log()[0].statements()[0].sql,
409 /// r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id", "name""#
410 /// );
411 /// #
412 /// # Ok(())
413 /// # }
414 /// ```
415 fn insert<A>(model: A) -> Insert<A>
416 where
417 A: ActiveModelTrait<Entity = Self>,
418 {
419 Insert::one(model)
420 }
421
422 /// Insert many models into database
423 ///
424 /// # Example (Postgres)
425 ///
426 /// ```
427 /// # use sea_orm::{error::*, tests_cfg::*, *};
428 /// #
429 /// # #[smol_potat::main]
430 /// # #[cfg(feature = "mock")]
431 /// # pub async fn main() -> Result<(), DbErr> {
432 /// #
433 /// # let db = MockDatabase::new(DbBackend::Postgres)
434 /// # .append_query_results([[maplit::btreemap! {
435 /// # "id" => Into::<Value>::into(28),
436 /// # }]])
437 /// # .into_connection();
438 /// #
439 /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
440 ///
441 /// let apple = cake::ActiveModel {
442 /// name: Set("Apple Pie".to_owned()),
443 /// ..Default::default()
444 /// };
445 /// let orange = cake::ActiveModel {
446 /// name: Set("Orange Scone".to_owned()),
447 /// ..Default::default()
448 /// };
449 ///
450 /// let insert_result = cake::Entity::insert_many([apple, orange]).exec(&db).await?;
451 ///
452 /// assert_eq!(insert_result.last_insert_id, 28);
453 ///
454 /// assert_eq!(
455 /// db.into_transaction_log(),
456 /// [Transaction::from_sql_and_values(
457 /// DbBackend::Postgres,
458 /// r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id""#,
459 /// ["Apple Pie".into(), "Orange Scone".into()]
460 /// )]
461 /// );
462 /// #
463 /// # Ok(())
464 /// # }
465 /// ```
466 ///
467 /// # Example (MySQL)
468 ///
469 /// ```
470 /// # use sea_orm::{error::*, tests_cfg::*, *};
471 /// #
472 /// # #[smol_potat::main]
473 /// # #[cfg(feature = "mock")]
474 /// # pub async fn main() -> Result<(), DbErr> {
475 /// #
476 /// # let db = MockDatabase::new(DbBackend::MySql)
477 /// # .append_exec_results([
478 /// # MockExecResult {
479 /// # last_insert_id: 28,
480 /// # rows_affected: 2,
481 /// # },
482 /// # ])
483 /// # .into_connection();
484 /// #
485 /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
486 ///
487 /// let apple = cake::ActiveModel {
488 /// name: Set("Apple Pie".to_owned()),
489 /// ..Default::default()
490 /// };
491 /// let orange = cake::ActiveModel {
492 /// name: Set("Orange Scone".to_owned()),
493 /// ..Default::default()
494 /// };
495 ///
496 /// let insert_result = cake::Entity::insert_many([apple, orange]).exec(&db).await?;
497 ///
498 /// assert_eq!(insert_result.last_insert_id, 28);
499 ///
500 /// assert_eq!(
501 /// db.into_transaction_log(),
502 /// [Transaction::from_sql_and_values(
503 /// DbBackend::MySql,
504 /// r#"INSERT INTO `cake` (`name`) VALUES (?), (?)"#,
505 /// ["Apple Pie".into(), "Orange Scone".into()]
506 /// )]
507 /// );
508 /// #
509 /// # Ok(())
510 /// # }
511 /// ```
512 ///
513 /// Before 1.1.3, if the active models have different column set, this method would panic.
514 /// Now, it'd attempt to fill in the missing columns with null
515 /// (which may or may not be correct, depending on whether the column is nullable):
516 ///
517 /// ```
518 /// use sea_orm::{
519 /// entity::*,
520 /// query::*,
521 /// tests_cfg::{cake, cake_filling},
522 /// DbBackend,
523 /// };
524 ///
525 /// assert_eq!(
526 /// cake::Entity::insert_many([
527 /// cake::ActiveModel {
528 /// id: NotSet,
529 /// name: Set("Apple Pie".to_owned()),
530 /// },
531 /// cake::ActiveModel {
532 /// id: NotSet,
533 /// name: Set("Orange Scone".to_owned()),
534 /// }
535 /// ])
536 /// .build(DbBackend::Postgres)
537 /// .to_string(),
538 /// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
539 /// );
540 ///
541 /// assert_eq!(
542 /// cake_filling::Entity::insert_many([
543 /// cake_filling::ActiveModel {
544 /// cake_id: ActiveValue::set(2),
545 /// filling_id: ActiveValue::NotSet,
546 /// },
547 /// cake_filling::ActiveModel {
548 /// cake_id: ActiveValue::NotSet,
549 /// filling_id: ActiveValue::set(3),
550 /// }
551 /// ])
552 /// .build(DbBackend::Postgres)
553 /// .to_string(),
554 /// r#"INSERT INTO "cake_filling" ("cake_id", "filling_id") VALUES (2, NULL), (NULL, 3)"#,
555 /// );
556 /// ```
557 ///
558 /// To get back inserted Models
559 ///
560 /// ```
561 /// # use sea_orm::{error::*, tests_cfg::*, *};
562 /// #
563 /// # #[smol_potat::main]
564 /// # #[cfg(feature = "mock")]
565 /// # pub async fn main() -> Result<(), DbErr> {
566 /// #
567 /// # let db = MockDatabase::new(DbBackend::Postgres)
568 /// # .append_query_results([
569 /// # [cake::Model {
570 /// # id: 1,
571 /// # name: "Apple Pie".to_owned(),
572 /// # }, cake::Model {
573 /// # id: 2,
574 /// # name: "Choco Pie".to_owned(),
575 /// # }],
576 /// # ])
577 /// # .into_connection();
578 /// #
579 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
580 ///
581 /// assert_eq!(
582 /// cake::Entity::insert_many([
583 /// cake::ActiveModel {
584 /// id: NotSet,
585 /// name: Set("Apple Pie".to_owned()),
586 /// },
587 /// cake::ActiveModel {
588 /// id: NotSet,
589 /// name: Set("Choco Pie".to_owned()),
590 /// },
591 /// ])
592 /// .exec_with_returning_many(&db)
593 /// .await?,
594 /// [
595 /// cake::Model {
596 /// id: 1,
597 /// name: "Apple Pie".to_owned(),
598 /// },
599 /// cake::Model {
600 /// id: 2,
601 /// name: "Choco Pie".to_owned(),
602 /// }
603 /// ]
604 /// );
605 ///
606 /// assert_eq!(
607 /// db.into_transaction_log()[0].statements()[0].sql,
608 /// r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id", "name""#
609 /// );
610 /// #
611 /// # Ok(())
612 /// # }
613 /// ```
614 fn insert_many<A, I>(models: I) -> Insert<A>
615 where
616 A: ActiveModelTrait<Entity = Self>,
617 I: IntoIterator<Item = A>,
618 {
619 Insert::many(models)
620 }
621
622 /// Update a model in database
623 ///
624 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
625 ///
626 /// # Example (Postgres)
627 ///
628 /// ```
629 /// # use sea_orm::{error::*, tests_cfg::*, *};
630 /// #
631 /// # #[smol_potat::main]
632 /// # #[cfg(feature = "mock")]
633 /// # pub async fn main() -> Result<(), DbErr> {
634 /// #
635 /// # let db = MockDatabase::new(DbBackend::Postgres)
636 /// # .append_query_results([
637 /// # [fruit::Model {
638 /// # id: 1,
639 /// # name: "Orange".to_owned(),
640 /// # cake_id: None,
641 /// # }],
642 /// # ])
643 /// # .into_connection();
644 /// #
645 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
646 ///
647 /// let orange = fruit::ActiveModel {
648 /// id: Set(1),
649 /// name: Set("Orange".to_owned()),
650 /// ..Default::default()
651 /// };
652 ///
653 /// assert_eq!(
654 /// fruit::Entity::update(orange.clone())
655 /// .filter(fruit::Column::Name.contains("orange"))
656 /// .exec(&db)
657 /// .await?,
658 /// fruit::Model {
659 /// id: 1,
660 /// name: "Orange".to_owned(),
661 /// cake_id: None,
662 /// }
663 /// );
664 ///
665 /// assert_eq!(
666 /// db.into_transaction_log(),
667 /// [Transaction::from_sql_and_values(
668 /// DbBackend::Postgres,
669 /// r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 AND "fruit"."name" LIKE $3 RETURNING "id", "name", "cake_id""#,
670 /// ["Orange".into(), 1i32.into(), "%orange%".into()]
671 /// )]);
672 /// #
673 /// # Ok(())
674 /// # }
675 /// ```
676 ///
677 /// # Example (MySQL)
678 ///
679 /// ```
680 /// # use sea_orm::{error::*, tests_cfg::*, *};
681 /// #
682 /// # #[smol_potat::main]
683 /// # #[cfg(feature = "mock")]
684 /// # pub async fn main() -> Result<(), DbErr> {
685 /// #
686 /// # let db = MockDatabase::new(DbBackend::MySql)
687 /// # .append_exec_results([
688 /// # MockExecResult {
689 /// # last_insert_id: 0,
690 /// # rows_affected: 1,
691 /// # },
692 /// # ])
693 /// # .append_query_results([
694 /// # [fruit::Model {
695 /// # id: 1,
696 /// # name: "Orange".to_owned(),
697 /// # cake_id: None,
698 /// # }],
699 /// # ])
700 /// # .into_connection();
701 /// #
702 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
703 ///
704 /// let orange = fruit::ActiveModel {
705 /// id: Set(1),
706 /// name: Set("Orange".to_owned()),
707 /// ..Default::default()
708 /// };
709 ///
710 /// assert_eq!(
711 /// fruit::Entity::update(orange.clone())
712 /// .filter(fruit::Column::Name.contains("orange"))
713 /// .exec(&db)
714 /// .await?,
715 /// fruit::Model {
716 /// id: 1,
717 /// name: "Orange".to_owned(),
718 /// cake_id: None,
719 /// }
720 /// );
721 ///
722 /// assert_eq!(
723 /// db.into_transaction_log(),
724 /// [
725 /// Transaction::from_sql_and_values(
726 /// DbBackend::MySql,
727 /// r#"UPDATE `fruit` SET `name` = ? WHERE `fruit`.`id` = ? AND `fruit`.`name` LIKE ?"#,
728 /// ["Orange".into(), 1i32.into(), "%orange%".into()]
729 /// ),
730 /// Transaction::from_sql_and_values(
731 /// DbBackend::MySql,
732 /// r#"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = ? LIMIT ?"#,
733 /// [1i32.into(), 1u64.into()]
734 /// )]);
735 /// #
736 /// # Ok(())
737 /// # }
738 /// ```
739 fn update<A>(model: A) -> UpdateOne<A>
740 where
741 A: ActiveModelTrait<Entity = Self>,
742 {
743 Update::one(model)
744 }
745
746 /// Update many models in database
747 ///
748 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
749 ///
750 /// # Example
751 ///
752 /// ```
753 /// # use sea_orm::{error::*, tests_cfg::*, *};
754 /// #
755 /// # #[smol_potat::main]
756 /// # #[cfg(feature = "mock")]
757 /// # pub async fn main() -> Result<(), DbErr> {
758 /// #
759 /// # let db = MockDatabase::new(DbBackend::Postgres)
760 /// # .append_exec_results([
761 /// # MockExecResult {
762 /// # last_insert_id: 0,
763 /// # rows_affected: 5,
764 /// # },
765 /// # ])
766 /// # .into_connection();
767 /// #
768 /// use sea_orm::{
769 /// entity::*,
770 /// query::*,
771 /// sea_query::{Expr, Value},
772 /// tests_cfg::fruit,
773 /// };
774 ///
775 /// let update_result = fruit::Entity::update_many()
776 /// .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
777 /// .filter(fruit::Column::Name.contains("Apple"))
778 /// .exec(&db)
779 /// .await?;
780 ///
781 /// assert_eq!(update_result.rows_affected, 5);
782 ///
783 /// assert_eq!(
784 /// db.into_transaction_log(),
785 /// [Transaction::from_sql_and_values(
786 /// DbBackend::Postgres,
787 /// r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#,
788 /// [Value::Int(None), "%Apple%".into()]
789 /// )]
790 /// );
791 /// #
792 /// # Ok(())
793 /// # }
794 /// ```
795 fn update_many() -> UpdateMany<Self> {
796 Update::many(Self::default())
797 }
798
799 /// Delete a model from database
800 ///
801 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
802 ///
803 /// # Example
804 ///
805 /// ```
806 /// # use sea_orm::{error::*, tests_cfg::*, *};
807 /// #
808 /// # #[smol_potat::main]
809 /// # #[cfg(feature = "mock")]
810 /// # pub async fn main() -> Result<(), DbErr> {
811 /// #
812 /// # let db = MockDatabase::new(DbBackend::Postgres)
813 /// # .append_exec_results([
814 /// # MockExecResult {
815 /// # last_insert_id: 0,
816 /// # rows_affected: 1,
817 /// # },
818 /// # ])
819 /// # .into_connection();
820 /// #
821 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
822 ///
823 /// let orange = fruit::ActiveModel {
824 /// id: Set(3),
825 /// ..Default::default()
826 /// };
827 ///
828 /// let delete_result = fruit::Entity::delete(orange).exec(&db).await?;
829 ///
830 /// assert_eq!(delete_result.rows_affected, 1);
831 ///
832 /// assert_eq!(
833 /// db.into_transaction_log(),
834 /// [Transaction::from_sql_and_values(
835 /// DbBackend::Postgres,
836 /// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
837 /// [3i32.into()]
838 /// )]
839 /// );
840 /// #
841 /// # Ok(())
842 /// # }
843 /// ```
844 fn delete<A>(model: A) -> DeleteOne<A>
845 where
846 A: ActiveModelTrait<Entity = Self>,
847 {
848 Delete::one(model)
849 }
850
851 /// Delete many models from database
852 ///
853 /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
854 ///
855 /// # Example
856 ///
857 /// ```
858 /// # use sea_orm::{error::*, tests_cfg::*, *};
859 /// #
860 /// # #[smol_potat::main]
861 /// # #[cfg(feature = "mock")]
862 /// # pub async fn main() -> Result<(), DbErr> {
863 /// #
864 /// # let db = MockDatabase::new(DbBackend::Postgres)
865 /// # .append_exec_results([
866 /// # MockExecResult {
867 /// # last_insert_id: 0,
868 /// # rows_affected: 5,
869 /// # },
870 /// # ])
871 /// # .append_query_results([
872 /// # [cake::Model {
873 /// # id: 15,
874 /// # name: "Apple Pie".to_owned(),
875 /// # }],
876 /// # ])
877 /// # .into_connection();
878 /// #
879 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
880 ///
881 /// let delete_result = fruit::Entity::delete_many()
882 /// .filter(fruit::Column::Name.contains("Apple"))
883 /// .exec(&db)
884 /// .await?;
885 ///
886 /// assert_eq!(delete_result.rows_affected, 5);
887 ///
888 /// assert_eq!(
889 /// db.into_transaction_log(),
890 /// [Transaction::from_sql_and_values(
891 /// DbBackend::Postgres,
892 /// r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#,
893 /// ["%Apple%".into()]
894 /// )]
895 /// );
896 /// #
897 /// # Ok(())
898 /// # }
899 /// ```
900 fn delete_many() -> DeleteMany<Self> {
901 Delete::many(Self::default())
902 }
903
904 /// Delete a model based on primary key
905 ///
906 /// # Example
907 ///
908 /// ```
909 /// # use sea_orm::{error::*, tests_cfg::*, *};
910 /// #
911 /// # #[smol_potat::main]
912 /// # #[cfg(feature = "mock")]
913 /// # pub async fn main() -> Result<(), DbErr> {
914 /// #
915 /// # let db = MockDatabase::new(DbBackend::Postgres)
916 /// # .append_exec_results([
917 /// # MockExecResult {
918 /// # last_insert_id: 0,
919 /// # rows_affected: 1,
920 /// # },
921 /// # ])
922 /// # .into_connection();
923 /// #
924 /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
925 ///
926 /// let delete_result = fruit::Entity::delete_by_id(1).exec(&db).await?;
927 ///
928 /// assert_eq!(delete_result.rows_affected, 1);
929 ///
930 /// assert_eq!(
931 /// db.into_transaction_log(),
932 /// [Transaction::from_sql_and_values(
933 /// DbBackend::Postgres,
934 /// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
935 /// [1i32.into()]
936 /// )]
937 /// );
938 /// #
939 /// # Ok(())
940 /// # }
941 /// ```
942 /// Delete by composite key
943 /// ```
944 /// # use sea_orm::{error::*, tests_cfg::*, *};
945 /// #
946 /// # #[smol_potat::main]
947 /// # #[cfg(feature = "mock")]
948 /// # pub async fn main() -> Result<(), DbErr> {
949 ///
950 /// # let db = MockDatabase::new(DbBackend::Postgres)
951 /// # .append_exec_results([
952 /// # MockExecResult {
953 /// # last_insert_id: 0,
954 /// # rows_affected: 1,
955 /// # },
956 /// # ])
957 /// # .into_connection();
958 /// #
959 /// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
960 ///
961 /// let delete_result = cake_filling::Entity::delete_by_id((2, 3)).exec(&db).await?;
962 ///
963 /// assert_eq!(delete_result.rows_affected, 1);
964 ///
965 /// assert_eq!(
966 /// db.into_transaction_log(),
967 /// [Transaction::from_sql_and_values(
968 /// DbBackend::Postgres,
969 /// r#"DELETE FROM "cake_filling" WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
970 /// [2i32.into(), 3i32.into()]
971 /// )]
972 /// );
973 /// #
974 /// # Ok(())
975 /// # }
976 /// ```
977 ///
978 /// # Panics
979 ///
980 /// Panics if arity of input values don't match arity of primary key
981 fn delete_by_id<T>(values: T) -> DeleteMany<Self>
982 where
983 T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
984 {
985 let mut delete = Self::delete_many();
986 let mut keys = Self::PrimaryKey::iter();
987 for v in values.into().into_value_tuple() {
988 if let Some(key) = keys.next() {
989 let col = key.into_column();
990 delete = delete.filter(col.eq(v));
991 } else {
992 panic!("primary key arity mismatch");
993 }
994 }
995 if keys.next().is_some() {
996 panic!("primary key arity mismatch");
997 }
998 delete
999 }
1000}
1001
1002#[cfg(test)]
1003mod tests {
1004 #[test]
1005 fn test_delete_by_id_1() {
1006 use crate::tests_cfg::cake;
1007 use crate::{entity::*, query::*, DbBackend};
1008 assert_eq!(
1009 cake::Entity::delete_by_id(1)
1010 .build(DbBackend::Sqlite)
1011 .to_string(),
1012 r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
1013 );
1014 }
1015
1016 #[test]
1017 fn test_delete_by_id_2() {
1018 use crate::tests_cfg::cake_filling_price;
1019 use crate::{entity::*, query::*, DbBackend};
1020 assert_eq!(
1021 cake_filling_price::Entity::delete_by_id((1, 2))
1022 .build(DbBackend::Sqlite)
1023 .to_string(),
1024 r#"DELETE FROM "public"."cake_filling_price" WHERE "cake_filling_price"."cake_id" = 1 AND "cake_filling_price"."filling_id" = 2"#,
1025 );
1026 }
1027
1028 #[test]
1029 #[cfg(feature = "macros")]
1030 fn entity_model_1() {
1031 use crate::entity::*;
1032
1033 mod hello {
1034 use crate as sea_orm;
1035 use crate::entity::prelude::*;
1036
1037 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1038 #[sea_orm(table_name = "hello")]
1039 pub struct Model {
1040 #[sea_orm(primary_key)]
1041 pub id: i32,
1042 }
1043
1044 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1045 pub enum Relation {}
1046
1047 impl ActiveModelBehavior for ActiveModel {}
1048 }
1049
1050 assert_eq!(hello::Entity.table_name(), "hello");
1051 assert_eq!(hello::Entity.schema_name(), None);
1052 }
1053
1054 #[test]
1055 #[cfg(feature = "macros")]
1056 fn entity_model_2() {
1057 use crate::entity::*;
1058
1059 mod hello {
1060 use crate as sea_orm;
1061 use crate::entity::prelude::*;
1062
1063 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1064 #[sea_orm(table_name = "hello", schema_name = "world")]
1065 pub struct Model {
1066 #[sea_orm(primary_key)]
1067 pub id: i32,
1068 }
1069
1070 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1071 pub enum Relation {}
1072
1073 impl ActiveModelBehavior for ActiveModel {}
1074 }
1075
1076 assert_eq!(hello::Entity.table_name(), "hello");
1077 assert_eq!(hello::Entity.schema_name(), Some("world"));
1078 }
1079
1080 #[test]
1081 #[cfg(feature = "macros")]
1082 fn entity_model_3() {
1083 use crate::{entity::*, query::*, DbBackend};
1084 use std::borrow::Cow;
1085
1086 mod hello {
1087 use crate as sea_orm;
1088 use crate::entity::prelude::*;
1089
1090 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1091 #[sea_orm(table_name = "hello", schema_name = "world")]
1092 pub struct Model {
1093 #[sea_orm(primary_key, auto_increment = false)]
1094 pub id: String,
1095 }
1096
1097 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1098 pub enum Relation {}
1099
1100 impl ActiveModelBehavior for ActiveModel {}
1101 }
1102
1103 fn delete_by_id<T>(value: T)
1104 where
1105 T: Into<<<hello::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType>,
1106 {
1107 assert_eq!(
1108 hello::Entity::delete_by_id(value)
1109 .build(DbBackend::Sqlite)
1110 .to_string(),
1111 r#"DELETE FROM "world"."hello" WHERE "hello"."id" = 'UUID'"#
1112 );
1113 }
1114
1115 delete_by_id("UUID".to_string());
1116 delete_by_id("UUID");
1117 delete_by_id(Cow::from("UUID"));
1118 }
1119}