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