1use crate::{
2 error::*, ConnectionTrait, DeleteResult, EntityTrait, Iterable, PrimaryKeyArity,
3 PrimaryKeyToColumn, PrimaryKeyTrait, Value,
4};
5use async_trait::async_trait;
6use sea_query::{Nullable, ValueTuple};
7use std::fmt::Debug;
8
9pub use ActiveValue::{NotSet, Set, Unchanged};
10
11#[derive(Clone, Debug)]
40pub enum ActiveValue<V>
41where
42 V: Into<Value>,
43{
44 Set(V),
46 Unchanged(V),
48 NotSet,
50}
51
52#[deprecated(
54 since = "0.5.0",
55 note = "Please use [`ActiveValue::NotSet`] or [`NotSet`]"
56)]
57#[allow(non_snake_case)]
58pub fn Unset<V>(_: Option<bool>) -> ActiveValue<V>
59where
60 V: Into<Value>,
61{
62 ActiveValue::not_set()
63}
64
65#[async_trait]
69pub trait ActiveModelTrait: Clone + Debug {
70 type Entity: EntityTrait;
72
73 fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>;
75
76 fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>;
78
79 fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
81
82 fn not_set(&mut self, c: <Self::Entity as EntityTrait>::Column);
84
85 fn is_not_set(&self, c: <Self::Entity as EntityTrait>::Column) -> bool;
87
88 fn default() -> Self;
90
91 fn default_values() -> Self;
93
94 fn reset(&mut self, c: <Self::Entity as EntityTrait>::Column);
97
98 fn reset_all(mut self) -> Self {
101 for col in <Self::Entity as EntityTrait>::Column::iter() {
102 self.reset(col);
103 }
104 self
105 }
106
107 #[allow(clippy::question_mark)]
113 fn get_primary_key_value(&self) -> Option<ValueTuple> {
114 let mut cols = <Self::Entity as EntityTrait>::PrimaryKey::iter();
115 macro_rules! next {
116 () => {
117 if let Some(col) = cols.next() {
118 if let Some(val) = self.get(col.into_column()).into_value() {
119 val
120 } else {
121 return None;
122 }
123 } else {
124 return None;
125 }
126 };
127 }
128 match <<<Self::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY {
129 1 => {
130 let s1 = next!();
131 Some(ValueTuple::One(s1))
132 }
133 2 => {
134 let s1 = next!();
135 let s2 = next!();
136 Some(ValueTuple::Two(s1, s2))
137 }
138 3 => {
139 let s1 = next!();
140 let s2 = next!();
141 let s3 = next!();
142 Some(ValueTuple::Three(s1, s2, s3))
143 }
144 len => {
145 let mut vec = Vec::with_capacity(len);
146 for _ in 0..len {
147 let s = next!();
148 vec.push(s);
149 }
150 Some(ValueTuple::Many(vec))
151 }
152 }
153 }
154
155 async fn insert<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
262 where
263 <Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
264 Self: ActiveModelBehavior + 'a,
265 C: ConnectionTrait,
266 {
267 let am = ActiveModelBehavior::before_save(self, db, true).await?;
268 let model = <Self::Entity as EntityTrait>::insert(am)
269 .exec_with_returning(db)
270 .await?;
271 Self::after_save(model, db, true).await
272 }
273
274 async fn update<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
384 where
385 <Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
386 Self: ActiveModelBehavior + 'a,
387 C: ConnectionTrait,
388 {
389 let am = ActiveModelBehavior::before_save(self, db, false).await?;
390 let model: <Self::Entity as EntityTrait>::Model = Self::Entity::update(am).exec(db).await?;
391 Self::after_save(model, db, false).await
392 }
393
394 async fn save<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
397 where
398 <Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
399 Self: ActiveModelBehavior + 'a,
400 C: ConnectionTrait,
401 {
402 let mut is_update = true;
403 for key in <Self::Entity as EntityTrait>::PrimaryKey::iter() {
404 let col = key.into_column();
405 if self.is_not_set(col) {
406 is_update = false;
407 break;
408 }
409 }
410 let res = if !is_update {
411 self.insert(db).await
412 } else {
413 self.update(db).await
414 }?;
415 Ok(res.into_active_model())
416 }
417
418 async fn delete<'a, C>(self, db: &'a C) -> Result<DeleteResult, DbErr>
462 where
463 Self: ActiveModelBehavior + 'a,
464 C: ConnectionTrait,
465 {
466 let am = ActiveModelBehavior::before_delete(self, db).await?;
467 let am_clone = am.clone();
468 let delete_res = Self::Entity::delete(am).exec(db).await?;
469 ActiveModelBehavior::after_delete(am_clone, db).await?;
470 Ok(delete_res)
471 }
472
473 #[cfg(feature = "with-json")]
477 fn set_from_json(&mut self, json: serde_json::Value) -> Result<(), DbErr>
478 where
479 <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
480 for<'de> <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model:
481 serde::de::Deserialize<'de>,
482 {
483 use crate::Iterable;
484
485 let primary_key_values: Vec<(<Self::Entity as EntityTrait>::Column, ActiveValue<Value>)> =
487 <<Self::Entity as EntityTrait>::PrimaryKey>::iter()
488 .map(|pk| (pk.into_column(), self.take(pk.into_column())))
489 .collect();
490
491 *self = Self::from_json(json)?;
493
494 for (col, active_value) in primary_key_values {
496 match active_value {
497 ActiveValue::Unchanged(v) | ActiveValue::Set(v) => self.set(col, v),
498 NotSet => self.not_set(col),
499 }
500 }
501
502 Ok(())
503 }
504
505 #[cfg(feature = "with-json")]
507 fn from_json(json: serde_json::Value) -> Result<Self, DbErr>
508 where
509 <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
510 for<'de> <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model:
511 serde::de::Deserialize<'de>,
512 {
513 use crate::{IdenStatic, Iterable};
514
515 let serde_json::Value::Object(obj) = &json else {
516 return Err(DbErr::Json(format!(
517 "invalid type: expected JSON object for {}",
518 <<Self as ActiveModelTrait>::Entity as IdenStatic>::as_str(&Default::default())
519 )));
520 };
521
522 let mut json_keys: Vec<(<Self::Entity as EntityTrait>::Column, bool)> = Vec::new();
524
525 for col in <<Self::Entity as EntityTrait>::Column>::iter() {
526 let key = col.as_str();
527 let has_key = obj.contains_key(key);
528 json_keys.push((col, has_key));
529 }
530
531 let model: <Self::Entity as EntityTrait>::Model =
533 serde_json::from_value(json).map_err(json_err)?;
534 let mut am = model.into_active_model();
535
536 for (col, json_key_exists) in json_keys {
538 match (json_key_exists, am.get(col)) {
539 (true, ActiveValue::Set(value) | ActiveValue::Unchanged(value)) => {
540 am.set(col, value);
541 }
542 _ => {
543 am.not_set(col);
544 }
545 }
546 }
547
548 Ok(am)
549 }
550
551 fn is_changed(&self) -> bool {
553 <Self::Entity as EntityTrait>::Column::iter()
554 .any(|col| self.get(col).is_set() && !self.get(col).is_unchanged())
555 }
556}
557
558#[allow(unused_variables)]
586#[async_trait]
587pub trait ActiveModelBehavior: ActiveModelTrait {
588 fn new() -> Self {
590 <Self as ActiveModelTrait>::default()
591 }
592
593 async fn before_save<C>(self, db: &C, insert: bool) -> Result<Self, DbErr>
595 where
596 C: ConnectionTrait,
597 {
598 Ok(self)
599 }
600
601 async fn after_save<C>(
603 model: <Self::Entity as EntityTrait>::Model,
604 db: &C,
605 insert: bool,
606 ) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
607 where
608 C: ConnectionTrait,
609 {
610 Ok(model)
611 }
612
613 async fn before_delete<C>(self, db: &C) -> Result<Self, DbErr>
615 where
616 C: ConnectionTrait,
617 {
618 Ok(self)
619 }
620
621 async fn after_delete<C>(self, db: &C) -> Result<Self, DbErr>
623 where
624 C: ConnectionTrait,
625 {
626 Ok(self)
627 }
628}
629
630pub trait IntoActiveModel<A>
632where
633 A: ActiveModelTrait,
634{
635 fn into_active_model(self) -> A;
637}
638
639impl<A> IntoActiveModel<A> for A
640where
641 A: ActiveModelTrait,
642{
643 fn into_active_model(self) -> A {
644 self
645 }
646}
647
648pub trait IntoActiveValue<V>
650where
651 V: Into<Value>,
652{
653 fn into_active_value(self) -> ActiveValue<V>;
655}
656
657impl<V> IntoActiveValue<Option<V>> for Option<V>
658where
659 V: IntoActiveValue<V> + Into<Value> + Nullable,
660{
661 fn into_active_value(self) -> ActiveValue<Option<V>> {
662 match self {
663 Some(value) => Set(Some(value)),
664 None => NotSet,
665 }
666 }
667}
668
669impl<V> IntoActiveValue<Option<V>> for Option<Option<V>>
670where
671 V: IntoActiveValue<V> + Into<Value> + Nullable,
672{
673 fn into_active_value(self) -> ActiveValue<Option<V>> {
674 match self {
675 Some(value) => Set(value),
676 None => NotSet,
677 }
678 }
679}
680
681macro_rules! impl_into_active_value {
682 ($ty: ty) => {
683 impl IntoActiveValue<$ty> for $ty {
684 fn into_active_value(self) -> ActiveValue<$ty> {
685 Set(self)
686 }
687 }
688 };
689}
690
691impl_into_active_value!(bool);
692impl_into_active_value!(i8);
693impl_into_active_value!(i16);
694impl_into_active_value!(i32);
695impl_into_active_value!(i64);
696impl_into_active_value!(u8);
697impl_into_active_value!(u16);
698impl_into_active_value!(u32);
699impl_into_active_value!(u64);
700impl_into_active_value!(f32);
701impl_into_active_value!(f64);
702impl_into_active_value!(&'static str);
703impl_into_active_value!(String);
704impl_into_active_value!(Vec<u8>);
705
706#[cfg(feature = "with-json")]
707#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
708impl_into_active_value!(crate::prelude::Json);
709
710#[cfg(feature = "with-chrono")]
711#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
712impl_into_active_value!(crate::prelude::Date);
713
714#[cfg(feature = "with-chrono")]
715#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
716impl_into_active_value!(crate::prelude::Time);
717
718#[cfg(feature = "with-chrono")]
719#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
720impl_into_active_value!(crate::prelude::DateTime);
721
722#[cfg(feature = "with-chrono")]
723#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
724impl_into_active_value!(crate::prelude::DateTimeWithTimeZone);
725
726#[cfg(feature = "with-chrono")]
727#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
728impl_into_active_value!(crate::prelude::DateTimeUtc);
729
730#[cfg(feature = "with-chrono")]
731#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
732impl_into_active_value!(crate::prelude::DateTimeLocal);
733
734#[cfg(feature = "with-rust_decimal")]
735#[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
736impl_into_active_value!(crate::prelude::Decimal);
737
738#[cfg(feature = "with-uuid")]
739#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
740impl_into_active_value!(crate::prelude::Uuid);
741
742#[cfg(feature = "with-time")]
743#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
744impl_into_active_value!(crate::prelude::TimeDate);
745
746#[cfg(feature = "with-time")]
747#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
748impl_into_active_value!(crate::prelude::TimeTime);
749
750#[cfg(feature = "with-time")]
751#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
752impl_into_active_value!(crate::prelude::TimeDateTime);
753
754#[cfg(feature = "with-time")]
755#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
756impl_into_active_value!(crate::prelude::TimeDateTimeWithTimeZone);
757
758#[cfg(feature = "with-ipnetwork")]
759#[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
760impl_into_active_value!(crate::prelude::IpNetwork);
761
762impl<V> Default for ActiveValue<V>
763where
764 V: Into<Value>,
765{
766 fn default() -> Self {
768 Self::NotSet
769 }
770}
771
772impl<V> ActiveValue<V>
773where
774 V: Into<Value>,
775{
776 pub fn set(value: V) -> Self {
778 Self::Set(value)
779 }
780
781 pub fn is_set(&self) -> bool {
783 matches!(self, Self::Set(_))
784 }
785
786 pub fn unchanged(value: V) -> Self {
788 Self::Unchanged(value)
789 }
790
791 pub fn is_unchanged(&self) -> bool {
793 matches!(self, Self::Unchanged(_))
794 }
795
796 pub fn not_set() -> Self {
798 Self::default()
799 }
800
801 pub fn is_not_set(&self) -> bool {
803 matches!(self, Self::NotSet)
804 }
805
806 pub fn take(&mut self) -> Option<V> {
809 match std::mem::take(self) {
810 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
811 ActiveValue::NotSet => None,
812 }
813 }
814
815 pub fn unwrap(self) -> V {
821 match self {
822 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
823 ActiveValue::NotSet => panic!("Cannot unwrap ActiveValue::NotSet"),
824 }
825 }
826
827 pub fn into_value(self) -> Option<Value> {
829 match self {
830 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value.into()),
831 ActiveValue::NotSet => None,
832 }
833 }
834
835 pub fn into_wrapped_value(self) -> ActiveValue<Value> {
837 match self {
838 Self::Set(value) => ActiveValue::set(value.into()),
839 Self::Unchanged(value) => ActiveValue::unchanged(value.into()),
840 Self::NotSet => ActiveValue::not_set(),
841 }
842 }
843
844 pub fn reset(&mut self) {
847 *self = match self.take() {
848 Some(value) => ActiveValue::Set(value),
849 None => ActiveValue::NotSet,
850 };
851 }
852
853 pub fn set_if_not_equals(&mut self, value: V)
881 where
882 V: PartialEq,
883 {
884 match self {
885 ActiveValue::Unchanged(current) if &value == current => {}
886 _ => *self = ActiveValue::Set(value),
887 }
888 }
889
890 pub fn try_as_ref(&self) -> Option<&V> {
904 match self {
905 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
906 ActiveValue::NotSet => None,
907 }
908 }
909}
910
911impl<V> std::convert::AsRef<V> for ActiveValue<V>
912where
913 V: Into<Value>,
914{
915 fn as_ref(&self) -> &V {
921 match self {
922 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
923 ActiveValue::NotSet => panic!("Cannot borrow ActiveValue::NotSet"),
924 }
925 }
926}
927
928impl<V> PartialEq for ActiveValue<V>
929where
930 V: Into<Value> + std::cmp::PartialEq,
931{
932 fn eq(&self, other: &Self) -> bool {
933 match (self, other) {
934 (ActiveValue::Set(l), ActiveValue::Set(r)) => l == r,
935 (ActiveValue::Unchanged(l), ActiveValue::Unchanged(r)) => l == r,
936 (ActiveValue::NotSet, ActiveValue::NotSet) => true,
937 _ => false,
938 }
939 }
940}
941
942impl<V> From<ActiveValue<V>> for ActiveValue<Option<V>>
943where
944 V: Into<Value> + Nullable,
945{
946 fn from(value: ActiveValue<V>) -> Self {
947 match value {
948 ActiveValue::Set(value) => ActiveValue::set(Some(value)),
949 ActiveValue::Unchanged(value) => ActiveValue::unchanged(Some(value)),
950 ActiveValue::NotSet => ActiveValue::not_set(),
951 }
952 }
953}
954
955#[cfg(test)]
956mod tests {
957 use crate::{entity::*, tests_cfg::*, DbErr};
958 use pretty_assertions::assert_eq;
959
960 #[cfg(feature = "with-json")]
961 use serde_json::json;
962
963 #[test]
964 #[cfg(feature = "macros")]
965 fn test_derive_into_active_model_1() {
966 mod my_fruit {
967 pub use super::fruit::*;
968 use crate as sea_orm;
969 use crate::entity::prelude::*;
970
971 #[derive(DeriveIntoActiveModel)]
972 pub struct NewFruit {
973 pub name: String,
975 pub cake_id: i32,
977 }
978 }
979
980 assert_eq!(
981 my_fruit::NewFruit {
982 name: "Apple".to_owned(),
983 cake_id: 1,
984 }
985 .into_active_model(),
986 fruit::ActiveModel {
987 id: NotSet,
988 name: Set("Apple".to_owned()),
989 cake_id: Set(Some(1)),
990 }
991 );
992 }
993
994 #[test]
995 #[cfg(feature = "macros")]
996 fn test_derive_into_active_model_2() {
997 use crate as sea_orm;
998 use crate::entity::prelude::*;
999
1000 #[derive(DeriveIntoActiveModel)]
1001 #[sea_orm(active_model = "fruit::ActiveModel")]
1002 struct FruitName {
1003 name: String,
1004 }
1005
1006 assert_eq!(
1007 FruitName {
1008 name: "Apple Pie".to_owned(),
1009 }
1010 .into_active_model(),
1011 fruit::ActiveModel {
1012 id: NotSet,
1013 name: Set("Apple Pie".to_owned()),
1014 cake_id: NotSet,
1015 }
1016 );
1017
1018 #[derive(DeriveIntoActiveModel)]
1019 #[sea_orm(active_model = "<fruit::Entity as EntityTrait>::ActiveModel")]
1020 struct FruitCake {
1021 cake_id: Option<Option<i32>>,
1022 }
1023
1024 assert_eq!(
1025 FruitCake {
1026 cake_id: Some(Some(1)),
1027 }
1028 .into_active_model(),
1029 fruit::ActiveModel {
1030 id: NotSet,
1031 name: NotSet,
1032 cake_id: Set(Some(1)),
1033 }
1034 );
1035
1036 assert_eq!(
1037 FruitCake {
1038 cake_id: Some(None),
1039 }
1040 .into_active_model(),
1041 fruit::ActiveModel {
1042 id: NotSet,
1043 name: NotSet,
1044 cake_id: Set(None),
1045 }
1046 );
1047
1048 assert_eq!(
1049 FruitCake { cake_id: None }.into_active_model(),
1050 fruit::ActiveModel {
1051 id: NotSet,
1052 name: NotSet,
1053 cake_id: NotSet,
1054 }
1055 );
1056 }
1057
1058 #[test]
1059 #[cfg(feature = "macros")]
1060 fn test_derive_try_into_model_1() {
1061 mod my_fruit {
1062 use crate as sea_orm;
1063 use crate::entity::prelude::*;
1064
1065 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1066 #[sea_orm(table_name = "fruit")]
1067 pub struct Model {
1068 #[sea_orm(primary_key)]
1069 pub id: i32,
1070 pub name: String,
1071 pub cake_id: Option<i32>,
1072 }
1073
1074 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1075 pub enum Relation {}
1076
1077 impl ActiveModelBehavior for ActiveModel {}
1078 }
1079 assert_eq!(
1080 my_fruit::ActiveModel {
1081 id: Set(1),
1082 name: Set("Pineapple".to_owned()),
1083 cake_id: Set(None),
1084 }
1085 .try_into_model()
1086 .unwrap(),
1087 my_fruit::Model {
1088 id: 1,
1089 name: "Pineapple".to_owned(),
1090 cake_id: None,
1091 }
1092 );
1093
1094 assert_eq!(
1095 my_fruit::ActiveModel {
1096 id: Set(2),
1097 name: Set("Apple".to_owned()),
1098 cake_id: Set(Some(1)),
1099 }
1100 .try_into_model()
1101 .unwrap(),
1102 my_fruit::Model {
1103 id: 2,
1104 name: "Apple".to_owned(),
1105 cake_id: Some(1),
1106 }
1107 );
1108
1109 assert_eq!(
1110 my_fruit::ActiveModel {
1111 id: Set(1),
1112 name: NotSet,
1113 cake_id: Set(None),
1114 }
1115 .try_into_model(),
1116 Err(DbErr::AttrNotSet(String::from("name")))
1117 );
1118
1119 assert_eq!(
1120 my_fruit::ActiveModel {
1121 id: Set(1),
1122 name: Set("Pineapple".to_owned()),
1123 cake_id: NotSet,
1124 }
1125 .try_into_model(),
1126 Err(DbErr::AttrNotSet(String::from("cake_id")))
1127 );
1128 }
1129
1130 #[test]
1131 #[cfg(feature = "macros")]
1132 fn test_derive_try_into_model_2() {
1133 mod my_fruit {
1134 use crate as sea_orm;
1135 use crate::entity::prelude::*;
1136
1137 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1138 #[sea_orm(table_name = "fruit")]
1139 pub struct Model {
1140 #[sea_orm(primary_key)]
1141 pub id: i32,
1142 pub name: String,
1143 #[sea_orm(ignore)]
1144 pub cake_id: Option<i32>,
1145 }
1146
1147 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1148 pub enum Relation {}
1149
1150 impl ActiveModelBehavior for ActiveModel {}
1151 }
1152 assert_eq!(
1153 my_fruit::ActiveModel {
1154 id: Set(1),
1155 name: Set("Pineapple".to_owned()),
1156 }
1157 .try_into_model()
1158 .unwrap(),
1159 my_fruit::Model {
1160 id: 1,
1161 name: "Pineapple".to_owned(),
1162 cake_id: None,
1163 }
1164 );
1165 }
1166
1167 #[test]
1168 #[cfg(feature = "macros")]
1169 fn test_derive_try_into_model_3() {
1170 mod my_fruit {
1171 use crate as sea_orm;
1172 use crate::entity::prelude::*;
1173
1174 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1175 #[sea_orm(table_name = "fruit")]
1176 pub struct Model {
1177 #[sea_orm(primary_key)]
1178 pub id: i32,
1179 #[sea_orm(ignore)]
1180 pub name: String,
1181 pub cake_id: Option<i32>,
1182 }
1183
1184 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1185 pub enum Relation {}
1186
1187 impl ActiveModelBehavior for ActiveModel {}
1188 }
1189 assert_eq!(
1190 my_fruit::ActiveModel {
1191 id: Set(1),
1192 cake_id: Set(Some(1)),
1193 }
1194 .try_into_model()
1195 .unwrap(),
1196 my_fruit::Model {
1197 id: 1,
1198 name: "".to_owned(),
1199 cake_id: Some(1),
1200 }
1201 );
1202 }
1203
1204 #[test]
1205 #[cfg(feature = "with-json")]
1206 #[should_panic(
1207 expected = r#"called `Result::unwrap()` on an `Err` value: Json("missing field `id`")"#
1208 )]
1209 fn test_active_model_set_from_json_1() {
1210 let mut cake: cake::ActiveModel = Default::default();
1211
1212 cake.set_from_json(json!({
1213 "name": "Apple Pie",
1214 }))
1215 .unwrap();
1216 }
1217
1218 #[test]
1219 #[cfg(feature = "with-json")]
1220 fn test_active_model_set_from_json_2() -> Result<(), DbErr> {
1221 let mut fruit: fruit::ActiveModel = Default::default();
1222
1223 fruit.set_from_json(json!({
1224 "name": "Apple",
1225 }))?;
1226 assert_eq!(
1227 fruit,
1228 fruit::ActiveModel {
1229 id: ActiveValue::NotSet,
1230 name: ActiveValue::Set("Apple".to_owned()),
1231 cake_id: ActiveValue::NotSet,
1232 }
1233 );
1234
1235 assert_eq!(
1236 fruit::ActiveModel::from_json(json!({
1237 "name": "Apple",
1238 }))?,
1239 fruit::ActiveModel {
1240 id: ActiveValue::NotSet,
1241 name: ActiveValue::Set("Apple".to_owned()),
1242 cake_id: ActiveValue::NotSet,
1243 }
1244 );
1245
1246 fruit.set_from_json(json!({
1247 "name": "Apple",
1248 "cake_id": null,
1249 }))?;
1250 assert_eq!(
1251 fruit,
1252 fruit::ActiveModel {
1253 id: ActiveValue::NotSet,
1254 name: ActiveValue::Set("Apple".to_owned()),
1255 cake_id: ActiveValue::Set(None),
1256 }
1257 );
1258
1259 fruit.set_from_json(json!({
1260 "id": null,
1261 "name": "Apple",
1262 "cake_id": 1,
1263 }))?;
1264 assert_eq!(
1265 fruit,
1266 fruit::ActiveModel {
1267 id: ActiveValue::NotSet,
1268 name: ActiveValue::Set("Apple".to_owned()),
1269 cake_id: ActiveValue::Set(Some(1)),
1270 }
1271 );
1272
1273 fruit.set_from_json(json!({
1274 "id": 2,
1275 "name": "Apple",
1276 "cake_id": 1,
1277 }))?;
1278 assert_eq!(
1279 fruit,
1280 fruit::ActiveModel {
1281 id: ActiveValue::NotSet,
1282 name: ActiveValue::Set("Apple".to_owned()),
1283 cake_id: ActiveValue::Set(Some(1)),
1284 }
1285 );
1286
1287 let mut fruit = fruit::ActiveModel {
1288 id: ActiveValue::Set(1),
1289 name: ActiveValue::NotSet,
1290 cake_id: ActiveValue::NotSet,
1291 };
1292 fruit.set_from_json(json!({
1293 "id": 8,
1294 "name": "Apple",
1295 "cake_id": 1,
1296 }))?;
1297 assert_eq!(
1298 fruit,
1299 fruit::ActiveModel {
1300 id: ActiveValue::Set(1),
1301 name: ActiveValue::Set("Apple".to_owned()),
1302 cake_id: ActiveValue::Set(Some(1)),
1303 }
1304 );
1305
1306 Ok(())
1307 }
1308
1309 #[smol_potat::test]
1310 #[cfg(feature = "with-json")]
1311 async fn test_active_model_set_from_json_3() -> Result<(), DbErr> {
1312 use crate::*;
1313
1314 let db = MockDatabase::new(DbBackend::Postgres)
1315 .append_exec_results([
1316 MockExecResult {
1317 last_insert_id: 1,
1318 rows_affected: 1,
1319 },
1320 MockExecResult {
1321 last_insert_id: 1,
1322 rows_affected: 1,
1323 },
1324 ])
1325 .append_query_results([
1326 [fruit::Model {
1327 id: 1,
1328 name: "Apple".to_owned(),
1329 cake_id: None,
1330 }],
1331 [fruit::Model {
1332 id: 2,
1333 name: "Orange".to_owned(),
1334 cake_id: Some(1),
1335 }],
1336 ])
1337 .into_connection();
1338
1339 let mut fruit: fruit::ActiveModel = Default::default();
1340 fruit.set_from_json(json!({
1341 "name": "Apple",
1342 }))?;
1343 fruit.save(&db).await?;
1344
1345 let mut fruit = fruit::ActiveModel {
1346 id: Set(2),
1347 ..Default::default()
1348 };
1349 fruit.set_from_json(json!({
1350 "id": 9,
1351 "name": "Orange",
1352 "cake_id": 1,
1353 }))?;
1354 fruit.save(&db).await?;
1355
1356 assert_eq!(
1357 db.into_transaction_log(),
1358 [
1359 Transaction::from_sql_and_values(
1360 DbBackend::Postgres,
1361 r#"INSERT INTO "fruit" ("name") VALUES ($1) RETURNING "id", "name", "cake_id""#,
1362 ["Apple".into()],
1363 ),
1364 Transaction::from_sql_and_values(
1365 DbBackend::Postgres,
1366 r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
1367 ["Orange".into(), 1i32.into(), 2i32.into()],
1368 ),
1369 ]
1370 );
1371
1372 Ok(())
1373 }
1374
1375 #[test]
1376 fn test_active_model_is_changed() {
1377 let mut fruit: fruit::ActiveModel = Default::default();
1378 assert!(!fruit.is_changed());
1379
1380 fruit.set(fruit::Column::Name, "apple".into());
1381 assert!(fruit.is_changed());
1382 }
1383
1384 #[test]
1385 fn test_reset_1() {
1386 assert_eq!(
1387 fruit::Model {
1388 id: 1,
1389 name: "Apple".into(),
1390 cake_id: None,
1391 }
1392 .into_active_model(),
1393 fruit::ActiveModel {
1394 id: Unchanged(1),
1395 name: Unchanged("Apple".into()),
1396 cake_id: Unchanged(None)
1397 },
1398 );
1399
1400 assert_eq!(
1401 fruit::Model {
1402 id: 1,
1403 name: "Apple".into(),
1404 cake_id: None,
1405 }
1406 .into_active_model()
1407 .reset_all(),
1408 fruit::ActiveModel {
1409 id: Set(1),
1410 name: Set("Apple".into()),
1411 cake_id: Set(None)
1412 },
1413 );
1414
1415 assert_eq!(
1416 fruit::Model {
1417 id: 1,
1418 name: "Apple".into(),
1419 cake_id: Some(2),
1420 }
1421 .into_active_model(),
1422 fruit::ActiveModel {
1423 id: Unchanged(1),
1424 name: Unchanged("Apple".into()),
1425 cake_id: Unchanged(Some(2)),
1426 },
1427 );
1428
1429 assert_eq!(
1430 fruit::Model {
1431 id: 1,
1432 name: "Apple".into(),
1433 cake_id: Some(2),
1434 }
1435 .into_active_model()
1436 .reset_all(),
1437 fruit::ActiveModel {
1438 id: Set(1),
1439 name: Set("Apple".into()),
1440 cake_id: Set(Some(2)),
1441 },
1442 );
1443 }
1444
1445 #[smol_potat::test]
1446 async fn test_reset_2() -> Result<(), DbErr> {
1447 use crate::*;
1448
1449 let db = MockDatabase::new(DbBackend::Postgres)
1450 .append_exec_results(vec![
1451 MockExecResult {
1452 last_insert_id: 1,
1453 rows_affected: 1,
1454 },
1455 MockExecResult {
1456 last_insert_id: 1,
1457 rows_affected: 1,
1458 },
1459 ])
1460 .append_query_results(vec![
1461 vec![fruit::Model {
1462 id: 1,
1463 name: "Apple".to_owned(),
1464 cake_id: None,
1465 }],
1466 vec![fruit::Model {
1467 id: 1,
1468 name: "Apple".to_owned(),
1469 cake_id: None,
1470 }],
1471 ])
1472 .into_connection();
1473
1474 fruit::Model {
1475 id: 1,
1476 name: "Apple".into(),
1477 cake_id: None,
1478 }
1479 .into_active_model()
1480 .update(&db)
1481 .await?;
1482
1483 fruit::Model {
1484 id: 1,
1485 name: "Apple".into(),
1486 cake_id: None,
1487 }
1488 .into_active_model()
1489 .reset_all()
1490 .update(&db)
1491 .await?;
1492
1493 assert_eq!(
1494 db.into_transaction_log(),
1495 vec![
1496 Transaction::from_sql_and_values(
1497 DbBackend::Postgres,
1498 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit" WHERE "fruit"."id" = $1 LIMIT $2"#,
1499 vec![1i32.into(), 1u64.into()],
1500 ),
1501 Transaction::from_sql_and_values(
1502 DbBackend::Postgres,
1503 r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
1504 vec!["Apple".into(), Option::<i32>::None.into(), 1i32.into()],
1505 ),
1506 ]
1507 );
1508
1509 Ok(())
1510 }
1511
1512 #[test]
1513 fn test_active_model_default_values() {
1514 assert_eq!(
1515 fruit::ActiveModel::default_values(),
1516 fruit::ActiveModel {
1517 id: Set(0),
1518 name: Set("".into()),
1519 cake_id: Set(None),
1520 },
1521 );
1522
1523 assert_eq!(
1524 lunch_set::ActiveModel::default_values(),
1525 lunch_set::ActiveModel {
1526 id: Set(0),
1527 name: Set("".into()),
1528 tea: NotSet,
1529 },
1530 );
1531 }
1532}