Skip to content

Commit

Permalink
Fix create duplicated value when updating nested has many relationship,
Browse files Browse the repository at this point in the history
  • Loading branch information
jinzhu committed Nov 29, 2021
1 parent 45e804d commit 27e2753
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 17 deletions.
21 changes: 17 additions & 4 deletions callbacks/associations.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils"
)

func SaveBeforeAssociations(create bool) func(db *gorm.DB) {
Expand Down Expand Up @@ -182,6 +183,7 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) {
fieldType = reflect.PtrTo(fieldType)
}
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
identityMap := map[string]bool{}
appendToElems := func(v reflect.Value) {
if _, zero := rel.Field.ValueOf(v); !zero {
f := reflect.Indirect(rel.Field.ReflectValueOf(v))
Expand All @@ -197,10 +199,21 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) {
}
}

if isPtr {
elems = reflect.Append(elems, elem)
} else {
elems = reflect.Append(elems, elem.Addr())
relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))
for _, pf := range rel.FieldSchema.PrimaryFields {
if pfv, ok := pf.ValueOf(elem); !ok {
relPrimaryValues = append(relPrimaryValues, pfv)
}
}

cacheKey := utils.ToStringKey(relPrimaryValues)
if len(relPrimaryValues) == 0 || (len(relPrimaryValues) == len(rel.FieldSchema.PrimaryFields) && !identityMap[cacheKey]) {
identityMap[cacheKey] = true
if isPtr {
elems = reflect.Append(elems, elem)
} else {
elems = reflect.Append(elems, elem.Addr())
}
}
}
}
Expand Down
29 changes: 18 additions & 11 deletions tests/associations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,19 +178,21 @@ func TestForeignKeyConstraintsBelongsTo(t *testing.T) {
}

func TestFullSaveAssociations(t *testing.T) {
coupon := &Coupon{
ID: "full-save-association-coupon1",
AppliesToProduct: []*CouponProduct{
{
CouponId: "full-save-association-coupon1",
ProductId: "full-save-association-product1",
},
},
AmountOff: 10,
PercentOff: 0.0,
}

err := DB.
Session(&gorm.Session{FullSaveAssociations: true}).
Create(&Coupon{
ID: "full-save-association-coupon1",
AppliesToProduct: []*CouponProduct{
{
CouponId: "full-save-association-coupon1",
ProductId: "full-save-association-product1",
},
},
AmountOff: 10,
PercentOff: 0.0,
}).Error
Create(coupon).Error

if err != nil {
t.Errorf("Failed, got error: %v", err)
Expand All @@ -203,4 +205,9 @@ func TestFullSaveAssociations(t *testing.T) {
if DB.First(&CouponProduct{}, "coupon_id = ? AND product_id = ?", "full-save-association-coupon1", "full-save-association-product1").Error != nil {
t.Errorf("Failed to query saved association")
}

orders := []Order{{Num: "order1", Coupon: coupon}, {Num: "order2", Coupon: coupon}}
if err := DB.Create(&orders).Error; err != nil {
t.Errorf("failed to create orders, got %v", err)
}
}
2 changes: 1 addition & 1 deletion tests/multi_primary_keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ func TestCompositePrimaryKeysAssociations(t *testing.T) {

DB.Migrator().DropTable(&Label{}, &Book{})
if err := DB.AutoMigrate(&Label{}, &Book{}); err != nil {
t.Fatalf("failed to migrate")
t.Fatalf("failed to migrate, got %v", err)
}

book := Book{
Expand Down
2 changes: 1 addition & 1 deletion tests/tests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func OpenTestConnection() (db *gorm.DB, err error) {

func RunMigrations() {
var err error
allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Coupon{}, &CouponProduct{}}
allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Coupon{}, &CouponProduct{}, &Order{}}
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] })

Expand Down
7 changes: 7 additions & 0 deletions utils/tests/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,10 @@ type CouponProduct struct {
CouponId string `gorm:"primarykey; size:255"`
ProductId string `gorm:"primarykey; size:255"`
}

type Order struct {
gorm.Model
Num string
Coupon *Coupon
CouponID string
}

0 comments on commit 27e2753

Please sign in to comment.