Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 17 additions & 21 deletions src/entity/active_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ pub trait ActiveModelTrait: Clone + Debug {

/// Create ActiveModel from a JSON value
#[cfg(feature = "with-json")]
fn from_json(mut json: serde_json::Value) -> Result<Self, DbErr>
fn from_json(json: serde_json::Value) -> Result<Self, DbErr>
where
Self: crate::TryIntoModel<<Self::Entity as EntityTrait>::Model>,
<<Self as ActiveModelTrait>::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
Expand All @@ -474,43 +474,39 @@ pub trait ActiveModelTrait: Clone + Debug {
{
use crate::{IdenStatic, Iterable};

let serde_json::Value::Object(obj) = &json else {
let serde_json::Value::Object(mut input) = json else {
return Err(DbErr::Json(format!(
"invalid type: expected JSON object for {}",
<<Self as ActiveModelTrait>::Entity as IdenStatic>::as_str(&Default::default())
)));
};

let dummy_am = Self::default_values();
let len = <<Self::Entity as EntityTrait>::Column>::iter().len();
// Mark down which attribute exists in the JSON object
let mut json_keys: Vec<(<Self::Entity as EntityTrait>::Column, bool)> = Vec::new();
let mut json_keys = Vec::with_capacity(len);
let mut merged = serde_json::Map::with_capacity(len);

for col in <<Self::Entity as EntityTrait>::Column>::iter() {
let key = col.json_key();
let has_key = obj.contains_key(key);
let has_key = input.contains_key(key);
json_keys.push((col, has_key));
}

// Create dummy model with dummy values
let dummy_model = Self::default_values();
if let Ok(dummy_model) = dummy_model.try_into_model() {
if let Ok(mut dummy_json) = serde_json::to_value(&dummy_model) {
let serde_json::Value::Object(merged) = &mut dummy_json else {
unreachable!();
};
let serde_json::Value::Object(obj) = json else {
unreachable!();
};
// overwrite dummy values with input values
for (key, value) in obj {
merged.insert(key, value);
match dummy_am.get(col) {
ActiveValue::Unchanged(value) | ActiveValue::Set(value) => {
merged.insert(key.to_owned(), sea_query::sea_value_to_json_value(&value));
}
json = dummy_json;
_ => {}
}
}

merged.append(&mut input);
let _ = input;

let json_value = serde_json::Value::Object(merged);

// Convert JSON object into ActiveModel via Model
let model: <Self::Entity as EntityTrait>::Model =
serde_json::from_value(json).map_err(json_err)?;
serde_json::from_value(json_value).map_err(json_err)?;
let mut am = model.into_active_model();

// Transform attribute that exists in JSON object into ActiveValue::Set, otherwise ActiveValue::NotSet
Expand Down
17 changes: 8 additions & 9 deletions src/tests_cfg/serde_rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ pub mod directional_rename_all {
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[serde(rename = "user-name")]
pub user_name: String,
}

Expand All @@ -123,7 +124,6 @@ mod tests {

// from_json uses camelCase keys
let json = serde_json::json!({
"id": 1,
"firstName": "Max",
"lastName": "Hermit",
"email": "[email protected]",
Expand All @@ -132,7 +132,8 @@ mod tests {

let am = ActiveModel::from_json(json).unwrap();

assert_eq!(am.id, ActiveValue::Set(1));
// Test whether missing required fields can be handled correctly
assert_eq!(am.id, ActiveValue::NotSet);
assert_eq!(am.first_name, ActiveValue::Set("Max".to_string()));
assert_eq!(am.last_name, ActiveValue::Set("Hermit".to_string()));
assert_eq!(am.email, ActiveValue::Set("[email protected]".to_string()));
Expand All @@ -155,7 +156,6 @@ mod tests {

// from_json uses deserialize names
let json = serde_json::json!({
"id": 1,
"orderDate": "2024-01-01",
"order-id": "ORD123",
"serOnly": "ser-value",
Expand All @@ -164,7 +164,7 @@ mod tests {

let am = ActiveModel::from_json(json).unwrap();

assert_eq!(am.id, ActiveValue::Set(1));
assert_eq!(am.id, ActiveValue::NotSet);
assert_eq!(am.order_date, ActiveValue::Set("2024-01-01".to_string()));
assert_eq!(am.order_id, ActiveValue::Set("ORD123".to_string()));
assert_eq!(am.ser_only, ActiveValue::Set("ser-value".to_string()));
Expand All @@ -191,19 +191,18 @@ mod tests {

#[test]
fn test_directional_rename_all() {
use directional_rename_all::{ActiveModel, Column, Model};
use directional_rename_all::{ActiveModel, Column};

assert_eq!(Column::Id.json_key(), "id");
assert_eq!(Column::UserName.json_key(), "userName");
assert_eq!(Column::UserName.json_key(), "user-name");

let json = serde_json::json!({
"id": 1,
"userName": "test_user"
"user-name": "test_user"
});

let am = ActiveModel::from_json(json).unwrap();

assert_eq!(am.id, ActiveValue::Set(1));
assert_eq!(am.id, ActiveValue::NotSet);
assert_eq!(am.user_name, ActiveValue::Set("test_user".to_string()));
}
}
Loading