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