From 135d9f8b0308c4bb24286d907f8d799705a24672 Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Mon, 25 May 2020 11:49:02 +0800 Subject: [PATCH] Test HasMany Association for Slice --- association.go | 17 +++-- callbacks.go | 3 + callbacks/associations.go | 4 +- tests/associations_test.go | 152 ++++++++++++++++++++++++++++++++++++- 4 files changed, 165 insertions(+), 11 deletions(-) diff --git a/association.go b/association.go index 9405d962..e3aee8f2 100644 --- a/association.go +++ b/association.go @@ -185,6 +185,7 @@ func (association *Association) Delete(values ...interface{}) error { primaryFields, foreignFields []*schema.Field foreignKeys []string updateAttrs = map[string]interface{}{} + conds []clause.Expression ) for _, ref := range rel.References { @@ -193,6 +194,8 @@ func (association *Association) Delete(values ...interface{}) error { foreignFields = append(foreignFields, ref.ForeignKey) foreignKeys = append(foreignKeys, ref.ForeignKey.DBName) updateAttrs[ref.ForeignKey.DBName] = nil + } else { + conds = append(conds, clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue}) } } @@ -205,12 +208,11 @@ func (association *Association) Delete(values ...interface{}) error { ) column, values := schema.ToQueryValues(foreignKeys, queryValues) + conds = append(conds, clause.IN{Column: column, Values: values}) relColumn, relValues := schema.ToQueryValues(rel.FieldSchema.PrimaryFieldDBNames, relQueryValues) + conds = append(conds, clause.IN{Column: relColumn, Values: relValues}) - tx.Session(&Session{}).Model(modelValue).Clauses( - clause.IN{Column: column, Values: values}, - clause.IN{Column: relColumn, Values: relValues}, - ).UpdateColumns(updateAttrs) + tx.Session(&Session{}).Model(modelValue).Clauses(conds...).UpdateColumns(updateAttrs) case schema.BelongsTo: var ( modelValue = reflect.New(rel.Schema.ModelType).Interface() @@ -219,12 +221,11 @@ func (association *Association) Delete(values ...interface{}) error { ) column, values := schema.ToQueryValues(rel.Schema.PrimaryFieldDBNames, queryValues) + conds = append(conds, clause.IN{Column: column, Values: values}) relColumn, relValues := schema.ToQueryValues(foreignKeys, relQueryValues) + conds = append(conds, clause.IN{Column: relColumn, Values: relValues}) - tx.Session(&Session{}).Model(modelValue).Clauses( - clause.IN{Column: column, Values: values}, - clause.IN{Column: relColumn, Values: relValues}, - ).UpdateColumns(updateAttrs) + tx.Session(&Session{}).Model(modelValue).Clauses(conds...).UpdateColumns(updateAttrs) case schema.Many2Many: modelValue := reflect.New(rel.JoinTable.ModelType).Interface() conds := rel.ToQueryConditions(reflectValue) diff --git a/callbacks.go b/callbacks.go index 629b90aa..d05947d9 100644 --- a/callbacks.go +++ b/callbacks.go @@ -87,6 +87,9 @@ func (p *processor) Execute(db *DB) { if stmt.Dest != nil { stmt.ReflectValue = reflect.Indirect(reflect.ValueOf(stmt.Dest)) + for stmt.ReflectValue.Kind() == reflect.Ptr { + stmt.ReflectValue = stmt.ReflectValue.Elem() + } if !stmt.ReflectValue.IsValid() { db.AddError(fmt.Errorf("invalid value")) } diff --git a/callbacks/associations.go b/callbacks/associations.go index 2342f110..d9ecafc7 100644 --- a/callbacks/associations.go +++ b/callbacks/associations.go @@ -125,7 +125,7 @@ func SaveAfterAssociations(db *gorm.DB) { if _, isZero := rel.FieldSchema.PrioritizedPrimaryField.ValueOf(rv); isZero { elems = reflect.Append(elems, rv) } else { - db.Session(&gorm.Session{}).Save(rv.Interface()) + db.Session(&gorm.Session{}).Save(rv.Addr().Interface()) } } } @@ -192,7 +192,7 @@ func SaveAfterAssociations(db *gorm.DB) { elems = reflect.Append(elems, elem.Addr()) } } else { - db.Session(&gorm.Session{}).Save(elem.Interface()) + db.Session(&gorm.Session{}).Save(elem.Addr().Interface()) } } } diff --git a/tests/associations_test.go b/tests/associations_test.go index 08733005..dd9f7efb 100644 --- a/tests/associations_test.go +++ b/tests/associations_test.go @@ -606,7 +606,7 @@ func TestHasManyAssociationForSlice(t *testing.T) { AssertAssociationCount(t, users, "Pets", 4, "after delete") - if err := DB.Debug().Model(&users).Association("Pets").Delete(users[0].Pets[0], users[1].Pets[1]); err != nil { + if err := DB.Model(&users).Association("Pets").Delete(users[0].Pets[0], users[1].Pets[1]); err != nil { t.Errorf("no error should happend when deleting pet, but got %v", err) } @@ -616,3 +616,153 @@ func TestHasManyAssociationForSlice(t *testing.T) { DB.Model(&users).Association("Pets").Clear() AssertAssociationCount(t, users, "Pets", 0, "After Clear") } + +func TestPolymorphicHasManyAssociation(t *testing.T) { + var user = *GetUser("hasmany", Config{Toys: 2}) + + if err := DB.Create(&user).Error; err != nil { + t.Fatalf("errors happened when create: %v", err) + } + + CheckUser(t, user, user) + + // Find + var user2 User + DB.Find(&user2, "id = ?", user.ID) + DB.Model(&user2).Association("Toys").Find(&user2.Toys) + CheckUser(t, user2, user) + + // Count + AssertAssociationCount(t, user, "Toys", 2, "") + + // Append + var toy = Toy{Name: "toy-has-many-append"} + + if err := DB.Model(&user2).Association("Toys").Append(&toy); err != nil { + t.Fatalf("Error happened when append account, got %v", err) + } + return + + if toy.ID == 0 { + t.Fatalf("Toy's ID should be created") + } + + user.Toys = append(user.Toys, toy) + CheckUser(t, user2, user) + + AssertAssociationCount(t, user, "Toys", 3, "AfterAppend") + + var toys = []Toy{{Name: "toy-has-many-append-1-1"}, {Name: "toy-has-many-append-1-1"}} + + if err := DB.Model(&user2).Association("Toys").Append(&toys); err != nil { + t.Fatalf("Error happened when append toy, got %v", err) + } + + for _, toy := range toys { + var toy = toy + if toy.ID == 0 { + t.Fatalf("Toy's ID should be created") + } + + user.Toys = append(user.Toys, toy) + } + + CheckUser(t, user2, user) + + AssertAssociationCount(t, user, "Toys", 5, "AfterAppendSlice") + + // Replace + var toy2 = Toy{Name: "toy-has-many-replace"} + + if err := DB.Model(&user2).Association("Toys").Replace(&toy2); err != nil { + t.Fatalf("Error happened when append toy, got %v", err) + } + + if toy2.ID == 0 { + t.Fatalf("toy2's ID should be created") + } + + user.Toys = []Toy{toy2} + CheckUser(t, user2, user) + + AssertAssociationCount(t, user2, "Toys", 1, "AfterReplace") + + // Delete + if err := DB.Model(&user2).Association("Toys").Delete(&Toy{}); err != nil { + t.Fatalf("Error happened when delete toy, got %v", err) + } + AssertAssociationCount(t, user2, "Toys", 1, "after delete non-existing data") + + if err := DB.Model(&user2).Association("Toys").Delete(&toy2); err != nil { + t.Fatalf("Error happened when delete Toys, got %v", err) + } + AssertAssociationCount(t, user2, "Toys", 0, "after delete") + + // Prepare Data for Clear + if err := DB.Model(&user2).Association("Toys").Append(&toy); err != nil { + t.Fatalf("Error happened when append Toys, got %v", err) + } + + AssertAssociationCount(t, user2, "Toys", 1, "after prepare data") + + // Clear + if err := DB.Model(&user2).Association("Toys").Clear(); err != nil { + t.Errorf("Error happened when clear Toys, got %v", err) + } + + AssertAssociationCount(t, user2, "Toys", 0, "after clear") +} + +func TestPolymorphicHasManyAssociationForSlice(t *testing.T) { + var users = []User{ + *GetUser("slice-hasmany-1", Config{Toys: 2}), + *GetUser("slice-hasmany-2", Config{Toys: 0}), + *GetUser("slice-hasmany-3", Config{Toys: 4}), + } + + DB.Create(&users) + + // Count + AssertAssociationCount(t, users, "Toys", 6, "") + + // Find + var toys []Toy + if DB.Model(&users).Association("Toys").Find(&toys); len(toys) != 6 { + t.Errorf("toys count should be %v, but got %v", 6, len(toys)) + } + + // Append + DB.Model(&users).Association("Toys").Append( + &Toy{Name: "toy-slice-append-1"}, + []Toy{{Name: "toy-slice-append-2-1"}, {Name: "toy-slice-append-2-2"}}, + &Toy{Name: "toy-slice-append-3"}, + ) + + AssertAssociationCount(t, users, "Toys", 10, "After Append") + + // Replace -> same as append + DB.Model(&users).Association("Toys").Replace( + []*Toy{{Name: "toy-slice-replace-1-1"}, {Name: "toy-slice-replace-1-2"}}, + []*Toy{{Name: "toy-slice-replace-2-1"}, {Name: "toy-slice-replace-2-2"}}, + &Toy{Name: "toy-slice-replace-3"}, + ) + + AssertAssociationCount(t, users, "Toys", 5, "After Append") + + // Delete + if err := DB.Model(&users).Association("Toys").Delete(&users[2].Toys); err != nil { + t.Errorf("no error should happend when deleting toy, but got %v", err) + } + + AssertAssociationCount(t, users, "Toys", 4, "after delete") + + if err := DB.Model(&users).Association("Toys").Delete(users[0].Toys[0], users[1].Toys[1]); err != nil { + t.Errorf("no error should happend when deleting toy, but got %v", err) + } + + AssertAssociationCount(t, users, "Toys", 2, "after delete") + + // Clear + DB.Model(&users).Association("Toys").Clear() + AssertAssociationCount(t, users, "Toys", 0, "After Clear") +}