sea_orm/entity/model.rs
1use crate::{
2 ActiveModelBehavior, ActiveModelTrait, ConnectionTrait, DbErr, DeleteResult, EntityTrait,
3 IntoActiveModel, Linked, QueryFilter, QueryResult, Related, Select, SelectModel, SelectorRaw,
4 Statement, TryGetError,
5};
6use async_trait::async_trait;
7pub use sea_query::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 /// Delete a model
41 async fn delete<'a, A, C>(self, db: &'a C) -> Result<DeleteResult, DbErr>
42 where
43 Self: IntoActiveModel<A>,
44 C: ConnectionTrait,
45 A: ActiveModelTrait<Entity = Self::Entity> + ActiveModelBehavior + Send + 'a,
46 {
47 self.into_active_model().delete(db).await
48 }
49}
50
51/// A Trait for implementing a [QueryResult]
52pub trait FromQueryResult: Sized {
53 /// Instantiate a Model from a [QueryResult]
54 ///
55 /// NOTE: Please also override `from_query_result_nullable` when manually implementing.
56 /// The future default implementation will be along the lines of:
57 ///
58 /// ```rust,ignore
59 /// fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr> {
60 /// (Self::from_query_result_nullable(res, pre)?)
61 /// }
62 /// ```
63 fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr>;
64
65 /// Transform the error from instantiating a Model from a [QueryResult]
66 /// and converting it to an [Option]
67 fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, DbErr> {
68 Ok(Self::from_query_result(res, pre).ok())
69
70 // would really like to do the following, but can't without version bump:
71 // match Self::from_query_result_nullable(res, pre) {
72 // Ok(v) => Ok(Some(v)),
73 // Err(TryGetError::Null(_)) => Ok(None),
74 // Err(TryGetError::DbErr(err)) => Err(err),
75 // }
76 }
77
78 /// Transform the error from instantiating a Model from a [QueryResult]
79 /// and converting it to an [Option]
80 ///
81 /// NOTE: This will most likely stop being a provided method in the next major version!
82 fn from_query_result_nullable(res: &QueryResult, pre: &str) -> Result<Self, TryGetError> {
83 Self::from_query_result(res, pre).map_err(TryGetError::DbErr)
84 }
85
86 /// ```
87 /// # use sea_orm::{error::*, tests_cfg::*, *};
88 /// #
89 /// # #[smol_potat::main]
90 /// # #[cfg(feature = "mock")]
91 /// # pub async fn main() -> Result<(), DbErr> {
92 /// #
93 /// # let db = MockDatabase::new(DbBackend::Postgres)
94 /// # .append_query_results([[
95 /// # maplit::btreemap! {
96 /// # "name" => Into::<Value>::into("Chocolate Forest"),
97 /// # "num_of_cakes" => Into::<Value>::into(2),
98 /// # },
99 /// # ]])
100 /// # .into_connection();
101 /// #
102 /// use sea_orm::{query::*, FromQueryResult};
103 ///
104 /// #[derive(Debug, PartialEq, FromQueryResult)]
105 /// struct SelectResult {
106 /// name: String,
107 /// num_of_cakes: i32,
108 /// }
109 ///
110 /// let res: Vec<SelectResult> = SelectResult::find_by_statement(Statement::from_sql_and_values(
111 /// DbBackend::Postgres,
112 /// r#"SELECT "name", COUNT(*) AS "num_of_cakes" FROM "cake" GROUP BY("name")"#,
113 /// [],
114 /// ))
115 /// .all(&db)
116 /// .await?;
117 ///
118 /// assert_eq!(
119 /// res,
120 /// [SelectResult {
121 /// name: "Chocolate Forest".to_owned(),
122 /// num_of_cakes: 2,
123 /// },]
124 /// );
125 /// #
126 /// # assert_eq!(
127 /// # db.into_transaction_log(),
128 /// # [Transaction::from_sql_and_values(
129 /// # DbBackend::Postgres,
130 /// # r#"SELECT "name", COUNT(*) AS "num_of_cakes" FROM "cake" GROUP BY("name")"#,
131 /// # []
132 /// # ),]
133 /// # );
134 /// #
135 /// # Ok(())
136 /// # }
137 /// ```
138 fn find_by_statement(stmt: Statement) -> SelectorRaw<SelectModel<Self>> {
139 SelectorRaw::<SelectModel<Self>>::from_statement(stmt)
140 }
141}
142
143impl<T: FromQueryResult> FromQueryResult for Option<T> {
144 fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr> {
145 Ok(Self::from_query_result_nullable(res, pre)?)
146 }
147
148 fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, DbErr> {
149 match Self::from_query_result_nullable(res, pre) {
150 Ok(v) => Ok(Some(v)),
151 Err(TryGetError::Null(_)) => Ok(None),
152 Err(TryGetError::DbErr(err)) => Err(err),
153 }
154 }
155
156 fn from_query_result_nullable(res: &QueryResult, pre: &str) -> Result<Self, TryGetError> {
157 match T::from_query_result_nullable(res, pre) {
158 Ok(v) => Ok(Some(v)),
159 Err(TryGetError::Null(_)) => Ok(None),
160 Err(err @ TryGetError::DbErr(_)) => Err(err),
161 }
162 }
163}
164
165/// A Trait for any type that can be converted into an Model
166pub trait TryIntoModel<M>
167where
168 M: ModelTrait,
169{
170 /// Method to call to perform the conversion
171 fn try_into_model(self) -> Result<M, DbErr>;
172}
173
174impl<M> TryIntoModel<M> for M
175where
176 M: ModelTrait,
177{
178 fn try_into_model(self) -> Result<M, DbErr> {
179 Ok(self)
180 }
181}