Golang中gorm无法将字段更新为空值
在使用gorm将一个字段更新为空的时候,发现并不生效,不了解具体什么原因,所以此时需要打开debug模式,查看原生SQL是如何执行的。
type Student struct { Model Email string `form:"email" json:"email"` Name string `form:"name" json:"name"` } func(c *Content) update(content Content) (err) { err = db.Model(&Content{}).Debug().Where("id = ?", 123).Update(&content).Error }
查看日志便知,此时如果name为空字符串,那么update的sql语句中并不会set,后查阅,方知gorm对于空字符和0这种数据,认为是不需要处理的,所以。。。
遇到这个问题,有两种解决方案:
A.更新传值的时候通过map来指定;
B.修改gorm的源码包,让它支持自定义是否可以设置为空值;
上述两种方案,第一种比较简单,不过感觉比较low,所以我选择尝试第二种。
当然第二种,也有它的问题,比如被更新之后,得手动去调整回来。
下面重点讲解第二种方案的实施。
1、找到gorm包下的scope.go文件
func convertInterfaceToMap(values interface{}, withIgnoredField bool, db *DB) map[string]interface{} { var attrs = map[string]interface{}{} switch value := values.(type) { case map[string]interface{}: return value case []interface{}: for _, v := range value { for key, value := range convertInterfaceToMap(v, withIgnoredField, db) { attrs[key] = value } } case interface{}: reflectValue := reflect.ValueOf(values) switch reflectValue.Kind() { case reflect.Map: for _, key := range reflectValue.MapKeys() { attrs[ToColumnName(key.Interface().(string))] = reflectValue.MapIndex(key).Interface() } default: for _, field := range (&Scope{Value: values, db: db}).Fields() { if !field.IsBlank && (withIgnoredField || !field.IsIgnored) { attrs[field.DBName] = field.Field.Interface() } } } } return attrs }
上面代码表示我们传递过来的数据会被转为map型,然后再进行数据库字段更新,这个代码很简单,就是把满足条件保存到map。
我们要解决的是空值能够更新,则和field.IsBlank相关联,接着找到下一个方法;
// Fields get value's fields func (scope *Scope) Fields() []*Field { if scope.fields == nil { var ( fields []*Field indirectScopeValue = scope.IndirectValue() isStruct = indirectScopeValue.Kind() == reflect.Struct ) for _, structField := range scope.GetModelStruct().StructFields { if isStruct { fieldValue := indirectScopeValue for _, name := range structField.Names { if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { fieldValue.Set(reflect.New(fieldValue.Type().Elem())) } fieldValue = reflect.Indirect(fieldValue).FieldByName(name) } //判断model中字段有没有force字段,此处使用Force,是因为解析tag时,统一转成了大写 _, ok := structField.TagSettingsGet("FORCE") //如果字段为空值,且字段存在则设定为false fields = append(fields, &Field{StructField: structField, Field: fieldValue, IsBlank: isBlank(fieldValue) && !ok}) } else { fields = append(fields, &Field{StructField: structField, IsBlank: true}) } } scope.fields = &fields } return *scope.fields }
注意上面带注释的两行,作用就不再赘述了。
2、修改我们的model
type Student struct { Model Email string `gorm:"force" form:"email" json:"email"` Name string `gorm:"force" form:"name" json:"name"` } func(c *Content) update(content Content) (err) { err = db.Model(&Content{}).Debug().Where("id = ?", 123).Update(&content).Error }
此时执行则顺利完成,SQL语句也包含了所有字段。
3、你有没有感到奇怪,为什么scope里面校验的是FORCE,而我在model中则定义的是force?
这就要说到另一个文件 gorm包下的 model_struct.go
func parseTagSetting(tags reflect.StructTag) map[string]string { setting := map[string]string{} for _, str := range []string{tags.Get("sql"), tags.Get("gorm")} { if str == "" { continue } tags := strings.Split(str, ";") for _, value := range tags { v := strings.Split(value, ":") k := strings.TrimSpace(strings.ToUpper(v[0])) if len(v) >= 2 { setting[k] = strings.Join(v[1:], ":") } else { setting[k] = k } } } return setting }
这个方法表示将orm中定义的tag标签,全部解析并转化为大写。
到此这篇关于Golang中gorm无法将字段更新为空值的文章就介绍到这了,更多相关gorm 字段更新为空值内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
详解Go语言中for循环,break和continue的使用
这篇文章主要通过一些示例为大家介绍一下Go语言中for循环、break和continue的基本语法以及使用,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下2022-06-06
最新评论