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