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

SetColumn doesn't work as expected in BeforeSave and BeforeUpdate hooks #4990

Open
Fulmineo64 opened this issue Jan 11, 2022 · 1 comment
Open
Assignees

Comments

@Fulmineo64
Copy link

Hi jinzhu,

i'm experiencing a problem with setting values using the BeforeSave and BeforeUpdate hooks while updating the data using a map (to do a partial update).

In brief what i'm doing right now is:

  • Unmarshal the values into a map to use it for the partial update
  • Update them via the db.Model(&model).Updates(&values) methods
  • Apply some changes to the data in the BeforeSave and BeforeUpdate hooks

Applying the data to the model of the hooks here doesn't work because the values to be used are in the map.

Here's where SetColumn comes to the rescue.
It fits perfectly my use case ... but it doesn't work as expected.

Let's take this exampleModel in consideration:

exampleModel.go

...
type ExampleModel struct {
    ...
    Name string `gorm:"type:nvarchar(150);"`
    ...
}

func (ExampleModel) BeforeUpdate(db *gorm.DB) error {
    db.Statement.SetColumn("Name", "Fulmineo")
    return db.Error
    // Doesn't work returns "invalid field"
}
...

This doesn't work because the db param isn't the "parent db" that contains the Statement with the Model, Schema and Dest properties with the correct values.

The reference of the "parent db" gets lost here:

gorm/callbacks/callmethod.go

func callMethod(db *gorm.DB, fc func(value interface{}, tx *gorm.DB) bool) {
    tx := db.Session(&gorm.Session{NewDB: true}) 
    ...
}

I understand the need to create a new Session which is essential to avoid unexpected behaviours on the hook.
Unfortunately this invalidates the use of SetColumn in these cases as the Statement on which it gets called is different.

I've temporarily solved the problem like this:

gorm/callbacks/callmethod.go

func callMethod(db *gorm.DB, fc func(value interface{}, tx *gorm.DB) bool) {
    tx := db.Session(&gorm.Session{NewDB: true}) 
    tx = tx.Statement.WithContext(context.WithValue(tx.Statement.Context, "parent", tx))
    ...
}

And in the exampleModel.go

exampleModel.go

...
type ExampleModel struct {
    ...
    Name string `gorm:"type:nvarchar(150);"`
    ...
}

func (ExampleModel) BeforeUpdate(db *gorm.DB) error {
    tx := db.Statement.Context.Value("parent").(*gorm.DB)
    tx.Statement.SetColumn("Name", "Fulmineo")
    // Works flawlessly as expected, updating the value on the map and applying it to the database
    return nil
}
...

Is there a better way to do this?

The problem

The gorm's callMethod creates a new Session and this unfortunately compromises the use of SetColumn in BeforeSave and BeforeUpdate hooks like in the documentation.

Possible solution

Like my temporary solution using the gorm's Context, it could be useful to have a "parent" property inside the Statement to refer to it for updating the values when working with maps.

I'm open to discuss other possible solutions.

Thank you for your collaboration.

@github-actions github-actions bot added the type:missing reproduction steps missing reproduction steps label Jan 11, 2022
@github-actions
Copy link

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 2 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.ioSearch Before Asking

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

No branches or pull requests

3 participants