22

On updates gorm doesnt update boolean type to false. By default it updates to true, but when i try to update to false not changes. I dont see any errors also. What can be the issue ?

type Attendee struct {
    ID             uint   `gorm:"primary_key" gorm:"AUTO_INCREMENT" json:"id,omitempty" mapstructure:"id" csv:"ID"`
    Email          string `json:"email,omitempty" mapstructure:"email" csv:"Email,required"`

    ShowDirectory  bool   `json:"show_directory,omitempty" gorm:"default:true" mapstructure:"show_directory" csv:"-"`
}


var attendee Attendee
// JSON.unmarshal lines here for the &attendee
if err := service.DB.Model(&attendee).Updates(Attendee{
        Email:         attendee.Email,
        ShowDirectory: false
}).Error; err != nil {
    return Attendee{}, err
}

Alternate Solution:

This works, but I am updating multiple attributies. So, I cant use this.

    att := Attendee{ID: 1}
    service.DB.Model(&att).Update("ShowDirectory", false)
7urkm3n
  • 6,054
  • 4
  • 29
  • 46
  • 2
    That's documented and *intended* behaviour when using *structs*, you can use a map for properties that are affected by such a strange (IMO) design decision. https://gorm.io/docs/update.html#Update-Changed-Fields (note the comments) – mkopriva Jun 18 '19 at 16:34
  • 1
    @mkopriva ohhh, Thanks, just noticed it. It says `false are blank values of their types` on struct usage – 7urkm3n Jun 18 '19 at 16:38

7 Answers7

24

As @mkopriva mentioned, by GORM Documentation

// Update attributes with `struct`, will only update non-zero fields
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 > 21:34:10' WHERE id = 111;

// Update attributes with `map`
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

NOTE When update with struct, GORM will only update non-zero fields, you might want to use map to update attributes or use Select to specify fields to update

Solved:

if err := service.DB.Model(&attendee).Updates(map[string]interface{}{
    "Email":          attendee.Email,
    "ShowDirectory": false
}).Error; err != nil {
    return Attendee{}, err
}
hashlash
  • 897
  • 8
  • 19
7urkm3n
  • 6,054
  • 4
  • 29
  • 46
16

Another convenient way would be making that field as a pointer.

NOTE all fields having a zero value, like 0, '', false or other zero values, won’t be saved into the database but will use its default value. If you want to avoid this, consider using a pointer type or scanner/valuerLink

In your case, the model would look like:

type Attendee struct {
        ID             uint   `gorm:"primary_key" gorm:"AUTO_INCREMENT" json:"id,omitempty" mapstructure:"id" csv:"ID"`
        Email          string `json:"email,omitempty" mapstructure:"email" csv:"Email,required"`
    
        ShowDirectory  *bool   `json:"show_directory,omitempty" gorm:"default:true" mapstructure:"show_directory" csv:"-"`
}
pramod
  • 1,453
  • 1
  • 14
  • 16
  • 1
    I prefer this method as I can still update my records with my models instead of using a `map[string]interface{}`. – Munosphere Feb 26 '22 at 12:49
  • 4
    can confirm this works, you just saved me after a whole day of debugging – Michael Enitan Mar 08 '22 at 05:57
  • 1
    Another way is to set the default constraint to `false`. Although, you will have to explicitly specify the value for `ShowDirectory`. Either way, it's just terrible it works like that. – Branislav Lazic Oct 28 '22 at 12:35
  • You can format your GORM attribute details as a bool type with the pointer as well, but its not necessary and this maps with and without. gorm:"default:true;type:bool" – Real.Cryptc Apr 18 '23 at 17:33
1

As it's mentioned from the document, to set a falsy value you need to use map or select the columns you need.

When update with struct, GORM will only update non-zero fields, you might want to use map to update attributes or use Select to specify fields to update

// Select with Struct (select zero value fields)
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
// UPDATE users SET name='new_name', age=0 WHERE id=111;

or you can select all columns:

// Select all fields (select all fields include zero value fields)
db.Model(&user).Select("*").Update(User{Name: "jinzhu", Role: "admin", Age: 0})
hamed dehghan
  • 471
  • 7
  • 15
0

Please do not use go struct for updating the non-zero fields such as boolean:false

The below code will not update Active: false in your database and gorm simply ignore

db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;

The below code will update the Active: false

db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

Use Map instead of go struct

Narendranath Reddy
  • 3,833
  • 3
  • 13
  • 32
  • 1
    Have you seen my answer? And whats difference between mine? – 7urkm3n Nov 03 '20 at 23:18
  • You said without a clear explanation and a best example and comparison, I just added more explanation. You know I faced the same issue and landed to this question but your answer didn’t help when I first saw your answer dint saw the actual difference at the first sight, trust me for few users my answer will help and that contains good explanation so I decided to write one more answer. Thanks for the answer – Narendranath Reddy Nov 04 '20 at 03:57
0

TL;DR: if you want to update all fields, use the Save() method instead of Updates().

As other answers have pointed out, Updates() will not update zero field values. That's because GORM can't tell the difference between Attendee{Email: "...", ShowDirectory: false} and Attendee{Email: "..."}.

To update all field values and avoid specifying field names as strings, you can read the record, change field values, and save the record. It's better to do this in a single transaction to avoid race conditions:

err := db.Transaction(func(db *gorm.DB) error {

    // read the existing record
    var a Attendee
    if res := db.Where("id = ?", attendeeId).First(&a); res.Error != nil {
        return err
    }

    // update the fields
    a.Email = newEmail
    a.ShowDirectory = newShowDirectory 

    // save the record
    if res := db.Save(&a); res.Error != nil {
        return err
    }
    return nil
})
Alexander Ryzhov
  • 2,705
  • 3
  • 19
  • 19
-1

You should write gorm type in your struct, something like this: gorm:"type:boolean; column:column_name" and for sure it will work!

DragoRoff
  • 55
  • 4
-1

Simply add `Select(*)

db.Model(&user).Select("*").Update(User{Name: "jinzhu", Role: "admin", Age: 0})

This way is much more simpler than others.

do thuan
  • 459
  • 3
  • 11