7urkm3n
7urkm3n

Reputation: 6321

GORM doesnt update boolean field to false

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)

Upvotes: 29

Views: 35120

Answers (8)

Tung Luong Thanh
Tung Luong Thanh

Reputation: 473

you can use pointer field and mapstructure tag. so when you need a update data, you can implement a function like this:

func (m *Model) ToMap() map[string]interface{} {
    var updateData map[string]interface{}
    err := mapstructure.Decode(m, &updateData)
    if err != nil {
        return nil
    }
    return updateData
}

This implementation also work when you create a Filter struct

query := txn.Model(&Model{}).Clauses(clause.Returning{})
conditions := filter.ToMap()
if len(conditions) > 0 {
    query = query.Where(conditions)
}

Upvotes: 0

Alexander Ryzhov
Alexander Ryzhov

Reputation: 2928

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
})

Upvotes: 1

pramod
pramod

Reputation: 1493

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:"-"`
}

Upvotes: 19

do thuan
do thuan

Reputation: 475

Simply add `Select(*)

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

This way is much more simpler than others.

Upvotes: -1

hamed dehghan
hamed dehghan

Reputation: 595

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})

Upvotes: 3

7urkm3n
7urkm3n

Reputation: 6321

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
}

Upvotes: 32

Narendranath Reddy
Narendranath Reddy

Reputation: 4133

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

Upvotes: 0

DragoRoff
DragoRoff

Reputation: 65

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

Upvotes: 0

Related Questions