"reflect: Field index out of range" panic on update

Viewed 2180

I get a panic implementing a basic update with Gorm and I couldn't find any information about it. Following Gorm's documentation, I'm not looking like doing wrong.

module achiever

go 1.15

require (
    gorm.io/driver/postgres v1.0.5
    gorm.io/gorm v1.20.7
)
type Task struct {
    ID    uint   `json:"id" gorm:"primary_key"`
    Title string `json:"title"`
}

type UpdateTaskInput struct {
    Title string `json:"title"`
}

func UpdateTask(id uint, input UpdateTaskInput) bool {
    var task Task
    if err := models.DB.Where("id = ?", id).First(&task).Error; err != nil {
        return false
    }

    models.DB.Model(&task).Updates(input)

    return true
}
reflect: Field index out of range
/usr/local/go/src/reflect/value.go:854 (0x4bf758)
    Value.Field: panic("reflect: Field index out of range")
/home/raphael/gows/pkg/mod/gorm.io/gorm@v1.20.7/schema/field.go:388 (0x57b984)
    (*Field).setupValuerAndSetter.func1: fieldValue := reflect.Indirect(value).Field(field.StructField.Index[0])
/home/raphael/gows/pkg/mod/gorm.io/gorm@v1.20.7/callbacks/update.go:230 (0x881670)
    ConvertToAssignments: value, isZero := field.ValueOf(updatingValue)
/home/raphael/gows/pkg/mod/gorm.io/gorm@v1.20.7/callbacks/update.go:64 (0x87f259)
    Update: if set := ConvertToAssignments(db.Statement); len(set) != 0 {
/home/raphael/gows/pkg/mod/gorm.io/gorm@v1.20.7/callbacks.go:105 (0x5a65bc)
    (*processor).Execute: f(db)
/home/raphael/gows/pkg/mod/gorm.io/gorm@v1.20.7/finisher_api.go:303 (0x5ae2c6)
    (*DB).Updates: tx.callbacks.Update().Execute(tx)
/home/raphael/ws/achiever/server/controllers/tasks.go:74 (0xad62cd)
    UpdateTask: models.DB.Model(&task).Updates(input)

I can't figure out where the error is coming from.

3 Answers

I was stuck with the same problem, as jugendhacker says: you need to construct the model's struct inside the update function:

models.DB.Model(&task).Updates(&Task{Title: input.Title})

This is stated in the GORM docs, if you are building an API, it would be more adequate to implement this as a PUT method, as you are passing a new object instead of patching an existing one.

Adding ID field to the UpdateTaskInput struct can fix this issue

type UpdateTaskInput struct {
    ID uint `json:"-"`
    Title string `json:"title"`
}

As far as I know the Update does not support "Smart Select Fields" so you need to construct the original struct first.

models.DB.Model(&task).Updates(&Task{Title: input.Title})
Related