-
-
Notifications
You must be signed in to change notification settings - Fork 524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More context for ActiveModelBehaviour #962
Comments
Hey @mohs8421, thanks for the feature request! Passing However, providing the old record / model isn't a trivial task. Currently, we don't store old value in ActiveModel. One approach would be adding a
|
That approach actually matches pretty good with a part of what I did before to get the "full" record. That means not only have the changed record I get from the client, but also the data, which is stored in the database: /// When an ActiveModel is created, it does not necessarily know,
/// whether the values it contains are changed or not, comparing
/// it to the existing Model solves this problem.
pub fn align_changes<E, A>(model: E::Model, mut active_model: A) -> A
where
E: EntityTrait,
A: ActiveModelTrait<Entity = E>,
{
for column in E::Column::iter() {
let active_value = active_model.get(column);
if let ActiveValue::Unchanged(value) = active_value {
if model.get(column) != value {
active_model.set(column, value);
}
}
}
// primary keys should never be updated here, so they have to be aligned from the existing model
for primary in E::PrimaryKey::iter() {
let col = primary.into_column();
if model.get(col) != *active_model.get(col).as_ref() {
active_model.set(col, model.get(col))
}
}
active_model
} It might even be possible to do something similar to this aligning method by actively providing a filter for the entity, how to get to that record (in case it is not as simple as in my model, where I always have a reliable primary key). Anyway I think I like your suggestion. |
Hey @mohs8421, sorry, I'm not sure what the snippet above what to do. Btw... for column in E::Column::iter() {
let active_value = active_model.get(column);
if let ActiveValue::Unchanged(value) = active_value {
if model.get(column) != value {
active_model.set(column, value);
// Shouldn't the line above be...?
// active_model.set(column, model.get(column));
}
}
} |
The snippet is intended to put the two pieces together. To give you some more context on that snippet: before this is called, I have this method: pub(crate) async fn deserialize_active_model<T>(request: &mut Request) -> TideResult<T>
where
T: ActiveModelTrait,
<<T as ActiveModelTrait>::Entity as EntityTrait>::Model: IntoActiveModel<T>,
for<'de> <<T as ActiveModelTrait>::Entity as EntityTrait>::Model: serde::de::DeserializeOwned,
{
let result = match request.content_type() {
Some(c) if c == mime::FORM => {
let result: T = request
.body_form::<<<T as ActiveModelTrait>::Entity as EntityTrait>::Model>()
.await?
.into_active_model();
Ok(result)
}
Some(c) if c == mime::JSON => T::from_json(request.body_json::<serde_json::Value>().await?),
_ => {
return Err(Error::from_str(
StatusCode::NotAcceptable,
"unrecognized content type",
));
}
};
result.map_err(|error| Error::new(StatusCode::BadRequest, error))
} The important part there is, that I can only deserialize into a model, and not into an active model, because that one is generated somewhere inside of sea-orm and I cannot declare it to be Deserialize directly. Here comes the previously stated function into the game, because it now looks at the model received from the database and compares that with the active_model, which has been translated from a model from the request. Yes it is a hacky kind of glue code, but it does what it should. Nevertheless I would love to get rid of that at some point in the future though. |
Interesting! Will this be helpful? https://www.sea-ql.org/SeaORM/docs/basic-crud/insert/#set-activemodel-from-json-value |
Please note the |
Hey @billy1624
What has to be done? Maybe I could help |
@teenjuna take a look at https://github.com/SeaQL/sea-orm/blob/master/src/entity/active_model.rs#L281 as an example, there you would only need to pass C into the behavior and similar things would need to be done in the other corresponding methods, and probably all related tests need to be adapted. For this part of the change. |
Motivation
When designing entities it is pretty common to write constraints into the datalayer. Sea-orm provides the
ActiveModelBehaviour
to allow users to fill in these pieces.However, it is usually necessary to do some further analysis in these cases, like dropping some queries to check for further conditions to allow or deny a certain change. It might also be important to know the state of a record before the change that is going to be made, for example to implement a proper state transition, that might only be valid if some more data has been added to the record.
Proposed Solutions
It would be best if the
ActiveModelBehaviour
's signature would change to be like follows:It might also be convenient to reduce the amount of parameters and pack them into a parameter-object struct like this:
The text was updated successfully, but these errors were encountered: