diff --git a/README.md b/README.md index 58d5b879..e7f25893 100644 --- a/README.md +++ b/README.md @@ -360,7 +360,7 @@ db.Model(&user).Updates(User{Name: "hello", Age: 18}) ### Update Without Callbacks -By default, update will call BeforeUpdate, AfterUpdate callbacks, if you want to update w/o callbacks: +By default, update will call BeforeUpdate, AfterUpdate callbacks, if you want to update w/o callbacks and w/o saving associations: ```go db.Model(&user).UpdateColumn("name", "hello") diff --git a/callback_shared.go b/callback_shared.go index ae75c250..88158cfc 100644 --- a/callback_shared.go +++ b/callback_shared.go @@ -11,6 +11,9 @@ func CommitOrRollbackTransaction(scope *Scope) { } func SaveBeforeAssociations(scope *Scope) { + if !scope.shouldSaveAssociations() { + return + } for _, field := range scope.Fields() { if scope.changeableField(field) && !field.IsBlank && !field.IsIgnored { if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" { @@ -25,6 +28,9 @@ func SaveBeforeAssociations(scope *Scope) { } func SaveAfterAssociations(scope *Scope) { + if !scope.shouldSaveAssociations() { + return + } for _, field := range scope.Fields() { if scope.changeableField(field) && !field.IsBlank && !field.IsIgnored { if relationship := field.Relationship; relationship != nil && diff --git a/main.go b/main.go index e197a99c..4a029512 100644 --- a/main.go +++ b/main.go @@ -266,6 +266,7 @@ func (s *DB) UpdateColumn(attrs ...interface{}) *DB { func (s *DB) UpdateColumns(values interface{}) *DB { return s.clone().NewScope(s.Value). Set("gorm:update_column", true). + Set("gorm:save_associations", false). InstanceSet("gorm:update_interface", values). callCallbacks(s.parent.callback.updates).db } diff --git a/scope.go b/scope.go index fccc5b88..d8e39348 100644 --- a/scope.go +++ b/scope.go @@ -398,3 +398,11 @@ func (scope *Scope) changeableField(field *Field) bool { return !field.IsIgnored } + +func (scope *Scope) shouldSaveAssociations() bool { + saveAssociations, ok := scope.Get("gorm:save_associations") + if ok && !saveAssociations.(bool) { + return false + } + return true +} diff --git a/update_test.go b/update_test.go index 8a019087..9a0af806 100644 --- a/update_test.go +++ b/update_test.go @@ -382,3 +382,32 @@ func TestOmitWithUpdateColumn(t *testing.T) { t.Errorf("Should omit name column when update user") } } + +func TestUpdateColumnsSkipsAssociations(t *testing.T) { + user := getPreparedUser("update_columns_user", "special_role") + user.Age = 99 + address1 := "first street" + user.BillingAddress = Address{Address1: address1} + DB.Save(user) + + // Update a single field of the user and verify that the changed address is not stored. + newAge := int64(100) + user.BillingAddress.Address1 = "second street" + db := DB.Model(user).UpdateColumns(User{Age: newAge}) + if db.RowsAffected != 1 { + t.Errorf("Expected RowsAffected=1 but instead RowsAffected=%v", DB.RowsAffected) + } + + // Verify that Age now=`newAge`. + freshUser := &User{Id: user.Id} + DB.First(freshUser) + if freshUser.Age != newAge { + t.Errorf("Expected freshly queried user to have Age=%v but instead found Age=%v", newAge, freshUser.Age) + } + + // Verify that user's BillingAddress.Address1 is not changed and is still "first street". + DB.First(&freshUser.BillingAddress, freshUser.BillingAddressID) + if freshUser.BillingAddress.Address1 != address1 { + t.Errorf("Expected user's BillingAddress.Address1=%s to remain unchanged after UpdateColumns invocation, but BillingAddress.Address1=%s", address1, freshUser.BillingAddress.Address1) + } +}