Skip to content
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

Enable convert from ActiveModel to Model #725

Merged
merged 5 commits into from
Aug 25, 2022

Conversation

greenhandatsjtu
Copy link
Contributor

PR Info

Related issue: #606

Adds

  • Convert from ActiveModel to Model

Changes

@greenhandatsjtu
Copy link
Contributor Author

greenhandatsjtu commented May 13, 2022

Hi, I'm new comer and chose #606 as my first issue to work on. As @billy1624 suggested, I introduce a new trait TryIntoModel and implement it inside derive macros of active model.
Unfortunately, I met several difficulties and don't know how to fix it.

For now, cargo build will fail with 2 kinds of errors like:

error[E0277]: the trait bound `i32: From<sea_query::Value>` is not satisfied
  --> /home/sunhengke/Development/rust/sea-orm/src/tests_cfg/cake_filling_price.rs:17:48
   |
17 | #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
   |                                                ^^^^^^^^^^^^^^^^^ the trait `From<sea_query::Value>` is not implemented for `i32`
   |
   = help: the following implementations were found:
             <i32 as From<NonZeroI32>>
             <i32 as From<bool>>
             <i32 as From<i16>>
             <i32 as From<i8>>
           and 86 others
   = note: required because of the requirements on the impl of `Into<i32>` for `sea_query::Value`
   = note: this error originates in the derive macro `DeriveActiveModel` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0063]: missing field `ignored_attr` in initializer of `cake_filling_price::Model`
  --> /home/sunhengke/Development/rust/sea-orm/src/tests_cfg/cake_filling_price.rs:17:48
   |
17 | #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
   |                                                ^^^^^^^^^^^^^^^^^ missing `ignored_attr`
   |
   = note: this error originates in the derive macro `DeriveActiveModel` (in Nightly builds, run with -Z macro-backtrace for more info)

The first error is because Value doesn't implement Into<xxx>, the second error is because the model is:

#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
    pub id: i32,
    pub name: String,
    pub vendor_id: Option<i32>,
    #[sea_orm(ignore)]
    pub ignored_attr: i32,
}

the field ignored_attr is ignored in ActiveModel, so we can never get the value of ignored_attr when trying to convert ActiveModel back to Model.
I'm not sure about how to handles these errors, what's wrong in my code, and what to do next, so I create this draft PR, would appreciate it if you could give me some advice.
Thanks in advance.

@greenhandatsjtu
Copy link
Contributor Author

greenhandatsjtu commented May 14, 2022

I got an idea for the second err, if there's ignored field in a Model, then we just return DbErr in try_from(), but this may not be a good idea.

For the first err, I'm still not sure what to do. Am I supposed to call functions like is_xxx() and as_ref_xxx() on every Value? It's overwhelming but I can't think of another way

Copy link
Member

@billy1624 billy1624 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @greenhandatsjtu, I'm sorry for the delay.

the second error is because the model is: the field ignored_attr is ignored in ActiveModel, so we can never get the value of ignored_attr when trying to convert ActiveModel back to Model.

Ignored fields are assumed to have Default implemented. So, you can simply write Default::default() Just like what we did in DeriveModel procedural macros:

if *ignore {
quote! {
Default::default()
}
} else {
quote! {
row.try_get(pre, sea_orm::IdenStatic::as_str(&<<Self as sea_orm::ModelTrait>::Entity as sea_orm::entity::EntityTrait>::Column::#column_ident).into())?
}
}

Comment on lines 101 to 134
Self {
#(#field: a.#field.into_value().unwrap().into()),*
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first error is because Value doesn't implement Into<xxx>

I guess we can a.#field.unwrap().unwrap()

@greenhandatsjtu
Copy link
Contributor Author

Hi @billy1624 , thanks for your advice. I closed this PR because I'm quite busy recently and had no idea on how to handle these errors 😢. Now that you give me some advice, I think it's worthy to give it another try :) I will continue working on this PR in the next few days. Thank you again!

@greenhandatsjtu greenhandatsjtu marked this pull request as ready for review July 4, 2022 01:57
@greenhandatsjtu
Copy link
Contributor Author

Hi @billy1624 , I just had some free time last weekend and worked on it according to your suggestions, and I think I made it! Thank you so much for guiding me~

@greenhandatsjtu greenhandatsjtu changed the title [WIP] Convert from ActiveModel to Model Enable convert from ActiveModel to Model Jul 4, 2022
@billy1624 billy1624 linked an issue Jul 8, 2022 that may be closed by this pull request
Copy link
Member

@billy1624 billy1624 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @greenhandatsjtu, sorry for the delay. Could you please add some test cases to test the conversion from ActiveModel to Model?

Better include tests where some model field are annotated with #[sea_orm(ignore)].
https://www.sea-ql.org/SeaORM/docs/generate-entity/entity-structure/#ignore-attribute

Many thanks!!

@greenhandatsjtu
Copy link
Contributor Author

Hey @billy1624 , sure I will add some tests. However, I need some instructions on following two problems:

  • I noticed that one of the CI tests failed, but I can't figure out what's wrong with my code :(
  • Where to put these test cases in, and what to test? Should I write tests like below:
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
    pub id: i32,
    pub name: String,
}

let pineapple = ActiveModel {
    id: Set(1),
    name: Set("Pineapple".to_owned()),
};
let model: Model = pineapple.try_into_model().unwrap();
assert_eq!(model.id, 1);
assert_eq!(model.name, "Pineapple".to_owned());

I would appreciate it if you could give me some advice!

Copy link
Member

@billy1624 billy1624 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @greenhandatsjtu, if you execute cargo expand command. You will see:

#[automatically_derived]
impl std::convert::TryFrom<ActiveModel> for <Entity as EntityTrait>::Model {
    type Error = DbErr;
    fn try_from(a: ActiveModel) -> Result<Self, DbErr> {
        if match a.id {
            sea_orm::ActiveValue::NotSet => true,
            _ => false,
        } {
            return Err(DbErr::Custom({
                let res = ::alloc::fmt::format(::core::fmt::Arguments::new_v1(
                    &["field ", " is NotSet"],
                    &[::core::fmt::ArgumentV1::new_display(&"id")],
                ));
                res
            }));
        }
        if match a.name {
            sea_orm::ActiveValue::NotSet => true,
            _ => false,
        } {
            return Err(DbErr::Custom({
                let res = ::alloc::fmt::format(::core::fmt::Arguments::new_v1(
                    &["field ", " is NotSet"],
                    &[::core::fmt::ArgumentV1::new_display(&"name")],
                ));
                res
            }));
        }
        Ok(Self {
            id: a.id.into_value().unwrap().unwrap(),
            name: a.name.into_value().unwrap().unwrap(),
        })
    }
}
#[automatically_derived]
impl sea_orm::TryIntoModel<<Entity as EntityTrait>::Model> for ActiveModel {
    fn try_into_model(self) -> Result<<Entity as EntityTrait>::Model, DbErr> {
        self.try_into()
    }
}

When converting from ActiveModel to Model. A field is missing.

Ok(Self {
    id: a.id.into_value().unwrap().unwrap(),
    name: a.name.into_value().unwrap().unwrap(),
    // `cake_id` is missing here
})

EDIT: I fixed the error on #975. I consider it's a misuse of DeriveActiveModel that cause the error.

@billy1624
Copy link
Member

  • Where to put these test cases in, and what to test? Should I write tests like below:
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
    pub id: i32,
    pub name: String,
}

let pineapple = ActiveModel {
    id: Set(1),
    name: Set("Pineapple".to_owned()),
};
let model: Model = pineapple.try_into_model().unwrap();
assert_eq!(model.id, 1);
assert_eq!(model.name, "Pineapple".to_owned());

You can put test under here:

Basically, we want to test the success and fail cases. Also the special case, where some fields are being ignored.

@greenhandatsjtu
Copy link
Contributor Author

Hi @billy1624 , thanks for your guidance, I've add some tests for converting from ActiveModel to Model, please review :)

Copy link
Member

@billy1624 billy1624 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @greenhandatsjtu, the test cases looks nice!

I'd like to refactor the code, please check greenhandatsjtu#1

@greenhandatsjtu
Copy link
Contributor Author

Hi @billy1624 , the PR looks good to me and I've merged it, thanks!

@billy1624 billy1624 changed the base branch from master to cont-pr-725 August 25, 2022 04:41
Copy link
Member

@billy1624 billy1624 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @greenhandatsjtu, thanks again for the contributions!

I will merge this into a working branch and do a rebase before it'll be merged into master. :)

@billy1624 billy1624 merged commit 9d797e0 into SeaQL:cont-pr-725 Aug 25, 2022
tyt2y3 pushed a commit that referenced this pull request Oct 23, 2022
* feat: enable convert from ActiveModel to Model

* feat: add tests for converting from ActiveModel to Model

* cargo fmt

* Refactoring

Co-authored-by: Billy Chan <ccw.billy.123@gmail.com>
tyt2y3 added a commit that referenced this pull request Oct 23, 2022
* Changelog

* Enable convert from ActiveModel to Model (#725)

* feat: enable convert from ActiveModel to Model

* feat: add tests for converting from ActiveModel to Model

* cargo fmt

* Refactoring

Co-authored-by: Billy Chan <ccw.billy.123@gmail.com>

* Fix clippy warnings

* Use error type

Co-authored-by: Chris Tsang <chris.2y3@outlook.com>
Co-authored-by: greenhandatsjtu <40566803+greenhandatsjtu@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Convert from ActiveModel to Model
2 participants