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

Update ActiveModelBehavior API #210

Merged
merged 8 commits into from
Oct 13, 2021
Merged

Update ActiveModelBehavior API #210

merged 8 commits into from
Oct 13, 2021

Conversation

billy1624
Copy link
Member

Resolve #161

@billy1624 billy1624 self-assigned this Sep 28, 2021
@billy1624 billy1624 marked this pull request as ready for review September 28, 2021 11:04
@MuhannadAlrusayni
Copy link
Contributor

MuhannadAlrusayni commented Oct 3, 2021

Great job @billy1624, there is a few things that would be cool to have in this PR.

1st) is to rollback db changes on failure.
2nd) I think we should change the error type (DbErr) to user defined error associated with ActiveModelBehavior::Error type or Box<dyn Error + Send + Sync + 'static>` which would result to make the user code cleaner, I would prefer to use user defined error type for more flexibility in the API. Something like this maybe?:

pub trait ActiveModelBehavior: ActiveModelTrait {
    Type Error: std::error::Error;

    fn after_save(self, insert: bool, db: &DatabaseConnection) -> Result<Self, Self::Error> {
        Ok(self)
    }

    // .. omitted
}

@billy1624
Copy link
Member Author

1st) is to rollback db changes on failure.

This requires Transaction support and it's not merged into master yet.

2nd) I think we should change the error type (DbErr) to user defined error associated with ActiveModelBehavior::Error type or Box<dyn Error + Send + Sync + 'static>` which would result to make the user code cleaner, I would prefer to use user defined error type for more flexibility in the API.

Perhaps we can introduce DbErr::Custom then user can convert their custom errors with .map_err(|e| DbErr::Custom( ... )). And we don't have to force user define extra type def ActiveModelBehavior::Error.

#[derive(Debug)]
pub enum DbErr {
    Conn(String),
    Exec(String),
    Query(String),
    Custom(Box<dyn Error>),
}

@MuhannadAlrusayni
Copy link
Contributor

Using DbError::Custom is fine pick, but I would still like the signature of ActiveModelBehavior::* to return Result<Self, Box<dyn Error>> so user code don't need to do the mapping every time they override these methods, we can do these mapping in SeaORM right ?

@MuhannadAlrusayni
Copy link
Contributor

we can also have an alias for such a result type like this:

// we can also name this BehaviorResult<T> or other good name, so we can add it in the prelude 
type Result<T> = Result<T, Box<dyn Error>>;

pub trait ActiveModelBehavior: ActiveModelTrait {
    fn before_save(self, insert: bool, db: &DatabaseConnection) -> Result<Self> { .. } 

    // .. omited
}

@MuhannadAlrusayni
Copy link
Contributor

Or another option is to impl<T: Error> From<T> for DbErr if possible so user can use ? inside these functions.

@billy1624
Copy link
Member Author

Or another option is to impl<T: Error> From<T> for DbErr if possible so user can use ? inside these functions.

But this conversion will have a long lasting impact. Converting all errors defined in third-party crate into DbErr::Custom. Is this a good idea? @tyt2y3

I think it's okay as long as we don't change this conversion afterward. Otherwise, this will be a headache for downstream users.

@tyt2y3
Copy link
Member

tyt2y3 commented Oct 8, 2021

I think it is better to not provide a blanket implementation for now.

It looks good otherwise.

@billy1624
Copy link
Member Author

Hey @MuhannadAlrusayni, I will not include db: &DatabaseConnection at this stage. Because...

  • Now the connection is not a simple enum but an async trait. So it's complicated to use it on top of another async trait ActiveModelBehavior.
    #[async_trait::async_trait]
    pub trait ConnectionTrait<'a>: Sync {
    type Stream: Stream<Item = Result<QueryResult, DbErr>>;
  • Also, doing CRUD inside ActiveModelBehavior::before_*() & ActiveModelBehavior::after_*() might cause unexpected cyclic actions.

db: &DatabaseConnection was removed at 75e625f for...

  • ActiveModelBehavior::before_*()
  • ActiveModelBehavior::after_*()

@billy1624
Copy link
Member Author

Hey @tyt2y3, this is ready to merge. Will add unit tests for this PR together with transaction once it's merged

@tyt2y3 tyt2y3 merged commit 6defdaf into master Oct 13, 2021
@tyt2y3 tyt2y3 deleted the active-model-behavior branch October 13, 2021 09:47
arpancodes pushed a commit to arpancodes/sea-orm that referenced this pull request Apr 8, 2022
* Support 'NULLS LAST' and 'NULLS FIRST'

* By defualt, don't write 'order by nulls' in QueryBuilder

* Add `Nulls` enum instead of boolean for represent nulls order

* `OrderedStatement`: rename `order_by_xxx_nulls_last` to `order_by_xxx_with_nulls`

* `OrderedStatement`: place nulls into cols tuple in `order_by_xxxs_with_nulls`

* Add `nulls order` support for Mysql backend

* fix: `NULLS FIRST` typo

* test: order by with nulls on all backends

* Rename enum `Nulls` to `NullOrdering`

Co-authored-by: Billy Chan <ccw.billy.123@gmail.com>
Co-authored-by: Chris Tsang <chris.2y3@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update ActiveModelBehavior API allowing custom validation logic
3 participants