sea_orm/entity/model.rs
1use crate::{
2 find_linked_recursive, ActiveModelBehavior, ActiveModelTrait, ConnectionTrait, DbErr,
3 DeleteResult, EntityTrait, IntoActiveModel, Linked, QueryFilter, QueryResult, Related, Select,
4 SelectModel, SelectorRaw, Statement, TryGetError,
5};
6use async_trait::async_trait;
7pub use sea_query::{JoinType, Value};
8use std::fmt::Debug;
9
10/// The interface for Model, implemented by data structs
11#[async_trait]
12pub trait ModelTrait: Clone + Send + Debug {
13 #[allow(missing_docs)]
14 type Entity: EntityTrait;
15
16 /// Get the [Value] of a column from an Entity
17 fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> Value;
18
19 /// Set the [Value] of a column in an Entity
20 fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
21
22 /// Find related Models
23 fn find_related<R>(&self, _: R) -> Select<R>
24 where
25 R: EntityTrait,
26 Self::Entity: Related<R>,
27 {
28 <Self::Entity as Related<R>>::find_related().belongs_to(self)
29 }
30
31 /// Find linked Models
32 fn find_linked<L>(&self, l: L) -> Select<L::ToEntity>
33 where
34 L: Linked<FromEntity = Self::Entity>,
35 {
36 let tbl_alias = &format!("r{}", l.link().len() - 1);
37 l.find_linked().belongs_to_tbl_alias(self, tbl_alias)
38 }
39
40 #[doc(hidden)]
41 /// Find linked Models with a recursive CTE for self-referencing relation chains
42 fn find_linked_recursive<L>(&self, l: L) -> Select<L::ToEntity>
43 where
44 L: Linked<FromEntity = Self::Entity, ToEntity = Self::Entity>,
45 {
46 // Have to do this because L is not Clone
47 let link = l.link();
48 let initial_query = self.find_linked(l);
49 find_linked_recursive(initial_query, link)
50 }
51
52 /// Delete a model
53 async fn delete<'a, A, C>(self, db: &'a C) -> Result<DeleteResult, DbErr>
54 where
55 Self: IntoActiveModel<A>,
56 C: ConnectionTrait,
57 A: ActiveModelTrait<Entity = Self::Entity> + ActiveModelBehavior + Send + 'a,
58 {
59 self.into_active_model().delete(db).await
60 }
61}
62
63/// A Trait for implementing a [QueryResult]
64pub trait FromQueryResult: Sized {
65 /// Instantiate a Model from a [QueryResult]
66 ///
67 /// NOTE: Please also override `from_query_result_nullable` when manually implementing.
68 /// The future default implementation will be along the lines of:
69 ///
70 /// ```rust,ignore
71 /// fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr> {
72 /// (Self::from_query_result_nullable(res, pre)?)
73 /// }
74 /// ```
75 fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr>;
76
77 /// Transform the error from instantiating a Model from a [QueryResult]
78 /// and converting it to an [Option]
79 fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, DbErr> {
80 Ok(Self::from_query_result(res, pre).ok())
81
82 // would really like to do the following, but can't without version bump:
83 // match Self::from_query_result_nullable(res, pre) {
84 // Ok(v) => Ok(Some(v)),
85 // Err(TryGetError::Null(_)) => Ok(None),
86 // Err(TryGetError::DbErr(err)) => Err(err),
87 // }
88 }
89
90 /// Transform the error from instantiating a Model from a [QueryResult]
91 /// and converting it to an [Option]
92 ///
93 /// NOTE: This will most likely stop being a provided method in the next major version!
94 fn from_query_result_nullable(res: &QueryResult, pre: &str) -> Result<Self, TryGetError> {
95 Self::from_query_result(res, pre).map_err(TryGetError::DbErr)
96 }
97
98 /// ```
99 /// # use sea_orm::{error::*, tests_cfg::*, *};
100 /// #
101 /// # #[smol_potat::main]
102 /// # #[cfg(feature = "mock")]
103 /// # pub async fn main() -> Result<(), DbErr> {
104 /// #
105 /// # let db = MockDatabase::new(DbBackend::Postgres)
106 /// # .append_query_results([[
107 /// # maplit::btreemap! {
108 /// # "name" => Into::<Value>::into("Chocolate Forest"),
109 /// # "num_of_cakes" => Into::<Value>::into(2),
110 /// # },
111 /// # ]])
112 /// # .into_connection();
113 /// #
114 /// use sea_orm::{query::*, FromQueryResult};
115 ///
116 /// #[derive(Debug, PartialEq, FromQueryResult)]
117 /// struct SelectResult {
118 /// name: String,
119 /// num_of_cakes: i32,
120 /// }
121 ///
122 /// let res: Vec<SelectResult> = SelectResult::find_by_statement(Statement::from_sql_and_values(
123 /// DbBackend::Postgres,
124 /// r#"SELECT "name", COUNT(*) AS "num_of_cakes" FROM "cake" GROUP BY("name")"#,
125 /// [],
126 /// ))
127 /// .all(&db)
128 /// .await?;
129 ///
130 /// assert_eq!(
131 /// res,
132 /// [SelectResult {
133 /// name: "Chocolate Forest".to_owned(),
134 /// num_of_cakes: 2,
135 /// },]
136 /// );
137 /// #
138 /// # assert_eq!(
139 /// # db.into_transaction_log(),
140 /// # [Transaction::from_sql_and_values(
141 /// # DbBackend::Postgres,
142 /// # r#"SELECT "name", COUNT(*) AS "num_of_cakes" FROM "cake" GROUP BY("name")"#,
143 /// # []
144 /// # ),]
145 /// # );
146 /// #
147 /// # Ok(())
148 /// # }
149 /// ```
150 fn find_by_statement(stmt: Statement) -> SelectorRaw<SelectModel<Self>> {
151 SelectorRaw::<SelectModel<Self>>::from_statement(stmt)
152 }
153}
154
155impl<T: FromQueryResult> FromQueryResult for Option<T> {
156 fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr> {
157 Ok(Self::from_query_result_nullable(res, pre)?)
158 }
159
160 fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, DbErr> {
161 match Self::from_query_result_nullable(res, pre) {
162 Ok(v) => Ok(Some(v)),
163 Err(TryGetError::Null(_)) => Ok(None),
164 Err(TryGetError::DbErr(err)) => Err(err),
165 }
166 }
167
168 fn from_query_result_nullable(res: &QueryResult, pre: &str) -> Result<Self, TryGetError> {
169 match T::from_query_result_nullable(res, pre) {
170 Ok(v) => Ok(Some(v)),
171 Err(TryGetError::Null(_)) => Ok(None),
172 Err(err @ TryGetError::DbErr(_)) => Err(err),
173 }
174 }
175}
176
177/// A Trait for any type that can be converted into an Model
178pub trait TryIntoModel<M>
179where
180 M: ModelTrait,
181{
182 /// Method to call to perform the conversion
183 fn try_into_model(self) -> Result<M, DbErr>;
184}
185
186impl<M> TryIntoModel<M> for M
187where
188 M: ModelTrait,
189{
190 fn try_into_model(self) -> Result<M, DbErr> {
191 Ok(self)
192 }
193}