Ошибка Gorm and Gin 500 только при обновлении

У меня довольно простой CRUD для списка задач, и пока я могу создавать, перечислять все, перечислять по идентификатору и удалять записи, но, когда я пытаюсь обновить, это дает мне следующую ошибку:

go-proj          | reflect: call of reflect.Value.Field on string Value
go-proj          | /usr/local/go/src/reflect/value.go:850 (0x4a2464)
go-proj          |      Value.Field: panic(&ValueError{"reflect.Value.Field", v.kind()})
go-proj          | /go/pkg/mod/gorm.io/[email protected]/schema/field.go:393 (0x996e50)
go-proj          |      (*Field).setupValuerAndSetter.func2: fieldValue := reflect.Indirect(value).Field(field.StructField.Index[0]).Field(field.StructField.Index[1])
go-proj          | /go/pkg/mod/gorm.io/[email protected]/callbacks/update.go:230 (0xb3e3f0)
go-proj          |      ConvertToAssignments: value, isZero := field.ValueOf(updatingValue)
go-proj          | /go/pkg/mod/gorm.io/[email protected]/callbacks/update.go:64 (0xb3bfd9)
go-proj          |      Update: if set := ConvertToAssignments(db.Statement); len(set) != 0 {
go-proj          | /go/pkg/mod/gorm.io/[email protected]/callbacks.go:105 (0x9a5b7c)
go-proj          |      (*processor).Execute: f(db)
go-proj          | /go/pkg/mod/gorm.io/[email protected]/finisher_api.go:303 (0x9ad886)
go-proj          |      (*DB).Updates: tx.callbacks.Update().Execute(tx)
go-proj          | /app/app/controllers/listController.go:70 (0xb49c7b)
go-proj          |      Update: if err := models.DB.Model(&list).Updates(input).Error; err != nil {
go-proj          | /go/pkg/mod/github.com/gin-gonic/[email protected]/context.go:161 (0x93d01a)
go-proj          |      (*Context).Next: c.handlers[c.index](c)
go-proj          | /go/pkg/mod/github.com/gin-gonic/[email protected]/recovery.go:83 (0x951004)
go-proj          |      RecoveryWithWriter.func1: c.Next()
go-proj          | /go/pkg/mod/github.com/gin-gonic/[email protected]/context.go:161 (0x93d01a)
go-proj          |      (*Context).Next: c.handlers[c.index](c)
go-proj          | /go/pkg/mod/github.com/gin-gonic/[email protected]/logger.go:241 (0x950104)
go-proj          |      LoggerWithConfig.func1: c.Next()
go-proj          | /go/pkg/mod/github.com/gin-gonic/[email protected]/context.go:161 (0x93d01a)
go-proj          |      (*Context).Next: c.handlers[c.index](c)
go-proj          | /go/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:409 (0x947359)
go-proj          |      (*Engine).handleHTTPRequest: c.Next()
go-proj          | /go/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:367 (0x946a4c)
go-proj          |      (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
go-proj          | /usr/local/go/src/net/http/server.go:2843 (0x6cd7c2)
go-proj          |      serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
go-proj          | /usr/local/go/src/net/http/server.go:1925 (0x6c8ecc)
go-proj          |      (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
go-proj          | /usr/local/go/src/runtime/asm_amd64.s:1374 (0x46cba0)
go-proj          |      goexit: BYTE    $0x90   // NOP
go-proj          | 
go-proj-nginx    | 172.27.0.1 - - [20/Nov/2020:17:32:56 +0000] "PUT /list/3 HTTP/1.1" 500 0 "-" "PostmanRuntime/7.6.0"

Я пробовал менять версии обоих фреймворков Gorm и Джин

Я буду извлекать только те части моего кода, которые имеют значение:

Маршрутизатор:

r := gin.Default()

r.PUT("/list/:id", controllers.Update)

Модель:

type List struct {
    gorm.Model
    UserId uint
    Title  string
    Status uint
}

Контроллер:

type UpdateListInput struct {
        Title  string `json:"title"`
        Status uint   `json:"status"`
}

func Update(c *gin.Context) {
        var list models.List

        if err := models.DB.Where("id = ?", c.Param("id")).First(&list).Error; err != nil {
                c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found"})
                return
        }

        var input UpdateListInput
        if err := c.ShouldBindJSON(&input); err != nil {
                c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
                return
        }

        if err := models.DB.Model(&list).Updates(input).Error; err != nil {
                c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        }

        c.JSON(http.StatusOK, gin.H{"data": list})
}

Я отправляю на /list/:id следующий JSON:

{
    "title": "Shopping List 2",
    "status": 2
}

Для получения дополнительной информации вы можете проверить папку моего репо .docker/ на GitHub, возможно, я что-то напутал Конфигурация Nginx, Postgres или даже Golang, потому что я запускаю свое приложение в контейнерах.

Edit1: Мне удалось поработать, жестко закодировав данные, которые нужно обновить, в моем контроллере:

func Update(c *gin.Context) {                                  
     var list models.List                                  
     if err := models.DB.Where("id = ?", c.Param("id")).First(&list).Error; err != nil {                                  
         c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found"})                                  
         return                                  
     }                                  
                                   
     input := map[string]interface{}{                                        
         "title":  "AAAAAAAAAa",                                                                                                                         
         "status": 3,                                                                                                                                    
     }                                                                                                                                                   
     if err := models.DB.Model(&list).Updates(input).Error; err != nil {                                                                                 
         c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})                                                                                      
         return                                                                                                                                          
     }                                                                                                                                                   
                                                                                       
      c.JSON(http.StatusOK, gin.H{"data": list})
}  

person Rafael Breno    schedule 20.11.2020    source источник
comment
Попробуйте получить значение int из c.Param("id")   -  person Alan Sereb    schedule 20.11.2020
comment
@AlanSereb тоже не сработал   -  person Rafael Breno    schedule 20.11.2020


Ответы (1)


Что ж, это обходной путь, возможно, я открою проблему на Gorm's Github, потому что я не Не думаю, что это правильный способ сделать это, единственное, что мне нужно сделать, это преобразовать из UpdateListInput struct в переменную map[string]interface{}, используя отразить пакет

Вот мой контроллер:

func Update(c *gin.Context) {
        var list models.List
        if err := models.DB.Where("id = ?", c.Param("id")).First(&list).Error; err != nil {
                c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found"})
                return
        }

        var input UpdateListInput
        if err := c.ShouldBindJSON(&input); err != nil {
                c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
                return
        }
        v := reflect.ValueOf(input)
        typeOfV := v.Type()

        inputData := map[string]interface{}{}

        for i := 0; i < v.NumField(); i++ {
                inputData[typeOfV.Field(i).Name] = v.Field(i).Interface()
        }

        if err := models.DB.Model(&list).Updates(inputData).Error; err != nil {
                c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
                return
        }

        c.JSON(http.StatusOK, gin.H{"data": list})
}
person Rafael Breno    schedule 20.11.2020