Search code examples
gogo-gorm

GORM updating null field when calling Updates()?


According to GORM's docs:

Updates supports update with struct or map[string]interface{}, when updating with struct it will only update non-zero fields by default

I have an entry in my database already for the Service with ID, abc123. I am attempting to take an object that looks like the one below:

Service{
  ID: "abc123",
  Name: "new service name",
  CreatedAt: nil,
}

And use it to update the my existing record. But when I call:

tx.Model(&service).Updates(service)

the CreatedAt value in the database is overwritten with nil. How can I update my database record without overwritting the CreatedAt value?

Update: Below is my Service struct

type Service struct {
  ID        string  `gorm:"not null;type:char(32);primary_key;column:id"`
  Name      string  `json:"name" gorm:"size:50;not null;"`
  CreatedAt *time.Time
  UpdatedAt time.Time
  DeletedAt *time.Time `gorm:"index"`
}

I've tried two different variations for my Service struct. the other is with CreatedAt being of type time.Time instead of *time.Time. With *time.Time it will overwrite the value in my DB with a null value. With time.Time it attempts to overwrite the value in the DB with an uninitialized time value and throws the error: Error 1292: Incorrect datetime value: '0000-00-00' for column 'created_at' at row 1


Solution

  • A zero value, or a default value, for a time.Time field type inside a struct is time.Time{}. When using Updates, either don't populate the CreatedAt field, or assign time.Time{} value to it.

    In the example below, the default or zero value is printed out for CreatedAt field in both cases.

    package main
    
    import (
        "fmt"
        "time"
    )
    
    type T struct {
       CreatedAt time.Time
       C int
       S string
    }
    
    func main() {
        fmt.Println(T{C: 1, S: "one"})
        fmt.Println(T{C: 2, S: "two", CreatedAt: time.Time{}})
    }
    
    // {0001-01-01 00:00:00 +0000 UTC 1 one}
    // {0001-01-01 00:00:00 +0000 UTC 2 two} 
    

    EDIT: Also, I'm not sure how even CreatedAt: nil, compiles if the CreatedAt field is of time.Time type, and not *time.Time.

    Since you've updated the Service struct and CreatedAt field type to *time.Time, following should work:

    tx.Model(&service).Updates(Service{Name: service.Name}) // add all fields that you want to be updated.
    
    // resulting query
    // UPDATE services SET name = 'new service name' WHERE id = 'abc123';
    

    An official GORM example is here

    Additionally, you can omit the created_at field like this:

    tx.Model(&service).Omit("created_at").Updates(service)