Refactor association Delete

This commit is contained in:
Jinzhu 2015-12-26 16:06:53 +08:00
parent e65f94b287
commit 300b74f15f
2 changed files with 154 additions and 37 deletions

View File

@ -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
}
}
}
}

View File

@ -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")
}
}