diff --git a/association.go b/association.go index a8a67be7..10be46c6 100644 --- a/association.go +++ b/association.go @@ -169,37 +169,30 @@ func (association *Association) Delete(values ...interface{}) *Association { return association } + var deletingResourcePrimaryFieldNames, deletingResourcePrimaryDBNames []string + for _, field := range scope.New(reflect.New(field.Type()).Interface()).Fields() { + if field.IsPrimaryKey { + deletingResourcePrimaryFieldNames = append(deletingResourcePrimaryFieldNames, field.Name) + deletingResourcePrimaryDBNames = append(deletingResourcePrimaryDBNames, field.DBName) + } + } + + deletingPrimaryKeys := association.getPrimaryKeys(deletingResourcePrimaryFieldNames, values...) + if relationship.Kind == "many_to_many" { - // many to many - // current value's foreign keys + // source value's foreign keys for idx, foreignKey := range relationship.ForeignDBNames { if field, ok := scope.FieldByName(relationship.ForeignFieldNames[idx]); ok { newDB = newDB.Where(fmt.Sprintf("%v = ?", scope.Quote(foreignKey)), field.Field.Interface()) } } - // deleting value's foreign keys - primaryKeys := association.getPrimaryKeys(relationship.AssociationForeignFieldNames, values...) - sql := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.AssociationForeignDBNames), toQueryMarks(primaryKeys)) - newDB = newDB.Where(sql, toQueryValues(primaryKeys)...) + // association value's foreign keys + deletingPrimaryKeys := association.getPrimaryKeys(relationship.AssociationForeignFieldNames, values...) + sql := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.AssociationForeignDBNames), toQueryMarks(deletingPrimaryKeys)) + newDB = newDB.Where(sql, toQueryValues(deletingPrimaryKeys)...) - if err := relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB, relationship); err == nil { - leftValues := reflect.Zero(association.Field.Field.Type()) - for i := 0; i < association.Field.Field.Len(); i++ { - reflectValue := association.Field.Field.Index(i) - primaryKey := association.getPrimaryKeys(relationship.ForeignFieldNames, reflectValue.Interface())[0] - var included = false - for _, pk := range primaryKeys { - if equalAsString(primaryKey, pk) { - included = true - } - } - if !included { - leftValues = reflect.Append(leftValues, reflectValue) - } - } - association.Field.Set(leftValues) - } + association.setErr(relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB, relationship)) } else { var foreignKeyMap = map[string]interface{}{} for _, foreignKey := range relationship.ForeignDBNames { @@ -225,23 +218,45 @@ func (association *Association) Delete(values ...interface{}) *Association { ) // only include those deleting relations - var primaryFieldNames, primaryFieldDBNames []string - for _, field := range scope.New(reflect.New(field.Type()).Interface()).Fields() { - if field.IsPrimaryKey { - primaryFieldNames = append(primaryFieldNames, field.Name) - primaryFieldDBNames = append(primaryFieldDBNames, field.DBName) - } - } - - relationsPrimaryKeys := association.getPrimaryKeys(primaryFieldNames, values...) newDB = newDB.Where( - fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, primaryFieldDBNames), toQueryMarks(relationsPrimaryKeys)), - toQueryValues(relationsPrimaryKeys)..., + fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, deletingResourcePrimaryDBNames), toQueryMarks(deletingPrimaryKeys)), + toQueryValues(deletingPrimaryKeys)..., ) // set matched relation's foreign key to be null fieldValue := reflect.New(association.Field.Field.Type()).Interface() - newDB.Model(fieldValue).UpdateColumn(foreignKeyMap) + association.setErr(newDB.Model(fieldValue).UpdateColumn(foreignKeyMap).Error) + } + } + + // Remove deleted records from field + if association.Error == nil { + if association.Field.Field.Kind() == reflect.Slice { + leftValues := reflect.Zero(association.Field.Field.Type()) + + for i := 0; i < association.Field.Field.Len(); i++ { + reflectValue := association.Field.Field.Index(i) + primaryKey := association.getPrimaryKeys(deletingResourcePrimaryFieldNames, reflectValue.Interface())[0] + var included = false + for _, pk := range deletingPrimaryKeys { + if equalAsString(primaryKey, pk) { + included = true + } + } + if !included { + leftValues = reflect.Append(leftValues, reflectValue) + } + } + + association.Field.Set(leftValues) + } else if association.Field.Field.Kind() == reflect.Struct { + for _, pk := range deletingPrimaryKeys { + primaryKey := association.getPrimaryKeys(deletingResourcePrimaryFieldNames, association.Field.Field)[0] + if equalAsString(primaryKey, pk) { + association.Field.Set(reflect.Zero(association.Field.Field.Type())) + break + } + } } } diff --git a/polymorphic_test.go b/polymorphic_test.go index f926fdd4..f3d7584d 100644 --- a/polymorphic_test.go +++ b/polymorphic_test.go @@ -1,7 +1,6 @@ package gorm_test import ( - "fmt" "reflect" "sort" "testing" @@ -103,7 +102,6 @@ func TestPolymorphic(t *testing.T) { var dogToys2 []Toy DB.Model(&dog).Association("Toys").Find(&dogToys2) - fmt.Println(dogToys2) if !compareToys(dogToys2, []string{"dog toy 1", "dog toy 2", "dog toy 3"}) { t.Errorf("Dog's toys should be updated with Append") } @@ -111,7 +109,111 @@ func TestPolymorphic(t *testing.T) { if DB.Model(&dog).Association("Toys").Count() != 3 { t.Errorf("Should return three polymorphic has many associations") } + // Replace + DB.Model(&cat).Association("Toy").Replace(&Toy{ + Name: "cat toy 3", + }) + + var catToy3 Toy + DB.Model(&cat).Association("Toy").Find(&catToy3) + if catToy3.Name != "cat toy 3" { + t.Errorf("Should update has one polymorphic association with Replace") + } + + if DB.Model(&cat).Association("Toy").Count() != 1 { + t.Errorf("Cat's toys count should be 1 after Replace") + } + + if DB.Model(&dog).Association("Toys").Count() != 3 { + t.Errorf("Should return three polymorphic has many associations") + } + + DB.Model(&dog).Association("Toys").Replace(&Toy{ + Name: "dog toy 4", + }, []Toy{ + {Name: "dog toy 5"}, {Name: "dog toy 6"}, {Name: "dog toy 7"}, + }) + + var dogToys3 []Toy + DB.Model(&dog).Association("Toys").Find(&dogToys3) + if !compareToys(dogToys3, []string{"dog toy 4", "dog toy 5", "dog toy 6", "dog toy 7"}) { + t.Errorf("Dog's toys should be updated with Replace") + } + + if DB.Model(&dog).Association("Toys").Count() != 4 { + t.Errorf("Should return three polymorphic has many associations") + } + // Delete + DB.Model(&cat).Association("Toy").Delete(&catToy2) + + var catToy4 Toy + DB.Model(&cat).Association("Toy").Find(&catToy4) + if catToy4.Name != "cat toy 3" { + t.Errorf("Should not update has one polymorphic association when Delete a unrelated Toy") + } + + if DB.Model(&cat).Association("Toy").Count() != 1 { + t.Errorf("Cat's toys count should be 1") + } + + if DB.Model(&dog).Association("Toys").Count() != 4 { + t.Errorf("Dog's toys count should be 4") + } + + DB.Model(&cat).Association("Toy").Delete(&catToy3) + + if !DB.Model(&cat).Related(&Toy{}, "Toy").RecordNotFound() { + t.Errorf("Toy should be deleted with Delete") + } + + if DB.Model(&cat).Association("Toy").Count() != 0 { + t.Errorf("Cat's toys count should be 0 after Delete") + } + + if DB.Model(&dog).Association("Toys").Count() != 4 { + t.Errorf("Dog's toys count should not be changed when delete cat's toy") + } + + DB.Model(&dog).Association("Toys").Delete(&dogToys2) + + if DB.Model(&dog).Association("Toys").Count() != 4 { + t.Errorf("Dog's toys count should not be changed when delete unrelated toys") + } + + DB.Model(&dog).Association("Toys").Delete(&dogToys3) + + if DB.Model(&dog).Association("Toys").Count() != 0 { + t.Errorf("Dog's toys count should be deleted with Delete") + } + // Clear + DB.Model(&cat).Association("Toy").Append(&Toy{ + Name: "cat toy 2", + }) + + if DB.Model(&cat).Association("Toy").Count() != 1 { + t.Errorf("Cat's toys should be added with Append") + } + + DB.Model(&cat).Association("Toy").Clear() + + if DB.Model(&cat).Association("Toy").Count() != 0 { + t.Errorf("Cat's toys should be cleared with Clear") + } + + DB.Model(&dog).Association("Toys").Append(&Toy{ + Name: "dog toy 8", + }) + + if DB.Model(&dog).Association("Toys").Count() != 1 { + t.Errorf("Dog's toys should be added with Append") + } + + DB.Model(&dog).Association("Toys").Clear() + + if DB.Model(&dog).Association("Toys").Count() != 0 { + t.Errorf("Dog's toys should be cleared with Clear") + } }