Browse Source

Refactor association Delete

Jinzhu 8 years ago
parent
commit
300b74f15f
2 changed files with 154 additions and 37 deletions
  1. 50 35
      association.go
  2. 104 2
      polymorphic_test.go

+ 50 - 35
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
+				}
+			}
 		}
 	}
 

+ 104 - 2
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")
+	}
 }