Skip to content

Commit

Permalink
Only update non blank fields that has been changed
Browse files Browse the repository at this point in the history
  • Loading branch information
jinzhu committed Feb 18, 2016
1 parent 52ae6df commit 6bd0862
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 52 deletions.
2 changes: 1 addition & 1 deletion association.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (association *Association) Delete(values ...interface{}) *Association {
modelValue := reflect.New(scope.GetModelStruct().ModelType).Interface()
if results := newDB.Model(modelValue).UpdateColumn(foreignKeyMap); results.Error == nil {
if results.RowsAffected > 0 {
scope.updatedAttrsWithValues(foreignKeyMap, false)
scope.updatedAttrsWithValues(foreignKeyMap)
}
} else {
association.setErr(results.Error)
Expand Down
21 changes: 4 additions & 17 deletions callback_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,10 @@ func init() {
func assignUpdatingAttributesCallback(scope *Scope) {
if attrs, ok := scope.InstanceGet("gorm:update_interface"); ok {
if maps := convertInterfaceToMap(attrs); len(maps) > 0 {
protected, ok := scope.Get("gorm:ignore_protected_attrs")
_, updateColumn := scope.Get("gorm:update_column")
updateAttrs, hasUpdate := scope.updatedAttrsWithValues(maps, ok && protected.(bool))

if updateColumn {
scope.InstanceSet("gorm:update_attrs", maps)
} else if len(updateAttrs) > 0 {
scope.InstanceSet("gorm:update_attrs", updateAttrs)
} else if !hasUpdate {
if updateMaps, hasUpdate := scope.updatedAttrsWithValues(maps); hasUpdate {
scope.InstanceSet("gorm:update_attrs", updateMaps)
} else {
scope.SkipLeft()
return
}
}
}
Expand Down Expand Up @@ -64,13 +57,7 @@ func updateCallback(scope *Scope) {

if updateAttrs, ok := scope.InstanceGet("gorm:update_attrs"); ok {
for column, value := range updateAttrs.(map[string]interface{}) {
if field, ok := scope.FieldByName(column); ok {
if scope.changeableField(field) {
sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(value)))
}
} else {
sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(column), scope.AddToVars(value)))
}
sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(column), scope.AddToVars(value)))
}
} else {
fields := scope.Fields()
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func (s *DB) FirstOrInit(out interface{}, where ...interface{}) *DB {
}
c.NewScope(out).inlineCondition(where...).initialize()
} else {
c.NewScope(out).updatedAttrsWithValues(convertInterfaceToMap(c.search.assignAttrs), false)
c.NewScope(out).updatedAttrsWithValues(convertInterfaceToMap(c.search.assignAttrs))
}
return c
}
Expand Down
11 changes: 10 additions & 1 deletion scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,29 @@ func (scope *Scope) HasColumn(column string) bool {

// SetColumn to set the column's value
func (scope *Scope) SetColumn(column interface{}, value interface{}) error {
var updateAttrs = map[string]interface{}{}
if attrs, ok := scope.InstanceGet("gorm:update_attrs"); ok {
updateAttrs = attrs.(map[string]interface{})
defer scope.InstanceSet("gorm:update_attrs", updateAttrs)
}

if field, ok := column.(*Field); ok {
updateAttrs[field.DBName] = value
return field.Set(value)
} else if name, ok := column.(string); ok {

if field, ok := scope.Fields()[name]; ok {
updateAttrs[field.DBName] = value
return field.Set(value)
}

dbName := ToDBName(name)
if field, ok := scope.Fields()[dbName]; ok {
updateAttrs[field.DBName] = value
return field.Set(value)
}

if field, ok := scope.FieldByName(name); ok {
updateAttrs[field.DBName] = value
return field.Set(value)
}
}
Expand Down
42 changes: 17 additions & 25 deletions scope_private.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,37 +319,29 @@ func (scope *Scope) callCallbacks(funcs []*func(s *Scope)) *Scope {
return scope
}

func (scope *Scope) updatedAttrsWithValues(values map[string]interface{}, ignoreProtectedAttrs bool) (results map[string]interface{}, hasUpdate bool) {
if !scope.IndirectValue().CanAddr() {
func (scope *Scope) updatedAttrsWithValues(values map[string]interface{}) (results map[string]interface{}, hasUpdate bool) {
if scope.IndirectValue().Kind() != reflect.Struct {
return values, true
}

var hasExpr bool
results = map[string]interface{}{}
for key, value := range values {
if field, ok := scope.FieldByName(key); ok && field.Field.IsValid() {
if field, ok := scope.FieldByName(key); ok && scope.changeableField(field) {
if !reflect.DeepEqual(field.Field, reflect.ValueOf(value)) {
if _, ok := value.(*expr); ok {
hasExpr = true
} else if !equalAsString(field.Field.Interface(), value) {
hasUpdate = true
field.Set(value)
}
}
}
}

if hasExpr {
var updateMap = map[string]interface{}{}
for key, field := range scope.Fields() {
if field.IsNormal {
if v, ok := values[key]; ok {
updateMap[key] = v
if field.IsNormal {
if _, ok := value.(*expr); ok {
hasUpdate = true
results[field.DBName] = value
} else if !equalAsString(field.Field.Interface(), value) {
hasUpdate = true
field.Set(value)
results[field.DBName] = field.Field.Interface()
}
} else {
updateMap[key] = field.Field.Interface()
field.Set(value)
}
}
}
return updateMap, true
}
return
}
Expand All @@ -370,10 +362,10 @@ func (scope *Scope) rows() (*sql.Rows, error) {

func (scope *Scope) initialize() *Scope {
for _, clause := range scope.Search.whereConditions {
scope.updatedAttrsWithValues(convertInterfaceToMap(clause["query"]), false)
scope.updatedAttrsWithValues(convertInterfaceToMap(clause["query"]))
}
scope.updatedAttrsWithValues(convertInterfaceToMap(scope.Search.initAttrs), false)
scope.updatedAttrsWithValues(convertInterfaceToMap(scope.Search.assignAttrs), false)
scope.updatedAttrsWithValues(convertInterfaceToMap(scope.Search.initAttrs))
scope.updatedAttrsWithValues(convertInterfaceToMap(scope.Search.assignAttrs))
return scope
}

Expand Down
9 changes: 5 additions & 4 deletions update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,14 @@ func TestUpdate(t *testing.T) {
}

DB.First(&product4, product4.Id)
updatedAt4 := product4.UpdatedAt
DB.Model(&product4).Update("price", gorm.Expr("price + ? - ?", 100, 50))
var product5 Product
DB.First(&product5, product4.Id)
if product5.Price != product4.Price+100-50 {
t.Errorf("Update with expression")
}
if product5.UpdatedAt.Format(time.RFC3339Nano) == product4.UpdatedAt.Format(time.RFC3339Nano) {
if product4.UpdatedAt.Format(time.RFC3339Nano) == updatedAt4.Format(time.RFC3339Nano) {
t.Errorf("Update with expression should update UpdatedAt")
}
}
Expand Down Expand Up @@ -170,13 +171,15 @@ func TestUpdates(t *testing.T) {
t.Errorf("product2's code should be updated")
}

updatedAt4 := product4.UpdatedAt
DB.Model(&product4).Updates(map[string]interface{}{"price": gorm.Expr("price + ?", 100)})
var product5 Product
DB.First(&product5, product4.Id)
if product5.Price != product4.Price+100 {
t.Errorf("Updates with expression")
}
if product5.UpdatedAt.Format(time.RFC3339Nano) == product4.UpdatedAt.Format(time.RFC3339Nano) {
// product4's UpdatedAt will be reset when updating
if product4.UpdatedAt.Format(time.RFC3339Nano) == updatedAt4.Format(time.RFC3339Nano) {
t.Errorf("Updates with expression should update UpdatedAt")
}
}
Expand Down Expand Up @@ -421,8 +424,6 @@ func TestUpdateColumnsSkipsAssociations(t *testing.T) {
}

func TestUpdatesWithBlankValues(t *testing.T) {
t.Skip("not implemented")

product := Product{Code: "product1", Price: 10}
DB.Save(&product)

Expand Down
4 changes: 1 addition & 3 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,7 @@ func convertInterfaceToMap(values interface{}) map[string]interface{} {

switch value := values.(type) {
case map[string]interface{}:
for k, v := range value {
attrs[k] = v
}
return value
case []interface{}:
for _, v := range value {
for key, value := range convertInterfaceToMap(v) {
Expand Down

0 comments on commit 6bd0862

Please sign in to comment.