Fix create duplicated value when updating nested has many relationship, close #4796
This commit is contained in:
		
							parent
							
								
									45e804dd3f
								
							
						
					
					
						commit
						27e2753c9d
					
				| @ -7,6 +7,7 @@ import ( | |||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| 	"gorm.io/gorm/clause" | 	"gorm.io/gorm/clause" | ||||||
| 	"gorm.io/gorm/schema" | 	"gorm.io/gorm/schema" | ||||||
|  | 	"gorm.io/gorm/utils" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func SaveBeforeAssociations(create bool) func(db *gorm.DB) { | func SaveBeforeAssociations(create bool) func(db *gorm.DB) { | ||||||
| @ -182,6 +183,7 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) { | |||||||
| 					fieldType = reflect.PtrTo(fieldType) | 					fieldType = reflect.PtrTo(fieldType) | ||||||
| 				} | 				} | ||||||
| 				elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10) | 				elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10) | ||||||
|  | 				identityMap := map[string]bool{} | ||||||
| 				appendToElems := func(v reflect.Value) { | 				appendToElems := func(v reflect.Value) { | ||||||
| 					if _, zero := rel.Field.ValueOf(v); !zero { | 					if _, zero := rel.Field.ValueOf(v); !zero { | ||||||
| 						f := reflect.Indirect(rel.Field.ReflectValueOf(v)) | 						f := reflect.Indirect(rel.Field.ReflectValueOf(v)) | ||||||
| @ -197,10 +199,21 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) { | |||||||
| 								} | 								} | ||||||
| 							} | 							} | ||||||
| 
 | 
 | ||||||
| 							if isPtr { | 							relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields)) | ||||||
| 								elems = reflect.Append(elems, elem) | 							for _, pf := range rel.FieldSchema.PrimaryFields { | ||||||
| 							} else { | 								if pfv, ok := pf.ValueOf(elem); !ok { | ||||||
| 								elems = reflect.Append(elems, elem.Addr()) | 									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()) | ||||||
|  | 								} | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
|  | |||||||
| @ -178,19 +178,21 @@ func TestForeignKeyConstraintsBelongsTo(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestFullSaveAssociations(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. | 	err := DB. | ||||||
| 		Session(&gorm.Session{FullSaveAssociations: true}). | 		Session(&gorm.Session{FullSaveAssociations: true}). | ||||||
| 		Create(&Coupon{ | 		Create(coupon).Error | ||||||
| 			ID: "full-save-association-coupon1", |  | ||||||
| 			AppliesToProduct: []*CouponProduct{ |  | ||||||
| 				{ |  | ||||||
| 					CouponId:  "full-save-association-coupon1", |  | ||||||
| 					ProductId: "full-save-association-product1", |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 			AmountOff:  10, |  | ||||||
| 			PercentOff: 0.0, |  | ||||||
| 		}).Error |  | ||||||
| 
 | 
 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Failed, got error: %v", err) | 		t.Errorf("Failed, got error: %v", err) | ||||||
| @ -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 { | 	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") | 		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) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -427,7 +427,7 @@ func TestCompositePrimaryKeysAssociations(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	DB.Migrator().DropTable(&Label{}, &Book{}) | 	DB.Migrator().DropTable(&Label{}, &Book{}) | ||||||
| 	if err := DB.AutoMigrate(&Label{}, &Book{}); err != nil { | 	if err := DB.AutoMigrate(&Label{}, &Book{}); err != nil { | ||||||
| 		t.Fatalf("failed to migrate") | 		t.Fatalf("failed to migrate, got %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	book := Book{ | 	book := Book{ | ||||||
|  | |||||||
| @ -87,7 +87,7 @@ func OpenTestConnection() (db *gorm.DB, err error) { | |||||||
| 
 | 
 | ||||||
| func RunMigrations() { | func RunMigrations() { | ||||||
| 	var err error | 	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.Seed(time.Now().UnixNano()) | ||||||
| 	rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] }) | 	rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] }) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -72,3 +72,10 @@ type CouponProduct struct { | |||||||
| 	CouponId  string `gorm:"primarykey; size:255"` | 	CouponId  string `gorm:"primarykey; size:255"` | ||||||
| 	ProductId string `gorm:"primarykey; size:255"` | 	ProductId string `gorm:"primarykey; size:255"` | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | type Order struct { | ||||||
|  | 	gorm.Model | ||||||
|  | 	Num      string | ||||||
|  | 	Coupon   *Coupon | ||||||
|  | 	CouponID string | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Jinzhu
						Jinzhu