package gorm_test import ( "encoding/json" "reflect" "testing" ) func getPreloadUser(name string) *User { return getPreparedUser(name, "Preload") } func checkUserHasPreloadData(user User, t *testing.T) { u := getPreloadUser(user.Name) if user.BillingAddress.Address1 != u.BillingAddress.Address1 { t.Error("Failed to preload user's BillingAddress") } if user.ShippingAddress.Address1 != u.ShippingAddress.Address1 { t.Error("Failed to preload user's ShippingAddress") } if user.CreditCard.Number != u.CreditCard.Number { t.Error("Failed to preload user's CreditCard") } if user.Company.Name != u.Company.Name { t.Error("Failed to preload user's Company") } if len(user.Emails) != len(u.Emails) { t.Error("Failed to preload user's Emails") } else { var found int for _, e1 := range u.Emails { for _, e2 := range user.Emails { if e1.Email == e2.Email { found++ break } } } if found != len(u.Emails) { t.Error("Failed to preload user's email details") } } } func TestPreload(t *testing.T) { user1 := getPreloadUser("user1") DB.Save(user1) preloadDB := DB.Where("role = ?", "Preload").Preload("BillingAddress").Preload("ShippingAddress"). Preload("CreditCard").Preload("Emails").Preload("Company") var user User preloadDB.Find(&user) checkUserHasPreloadData(user, t) user2 := getPreloadUser("user2") DB.Save(user2) user3 := getPreloadUser("user3") DB.Save(user3) var users []User preloadDB.Find(&users) for _, user := range users { checkUserHasPreloadData(user, t) } var users2 []*User preloadDB.Find(&users2) for _, user := range users2 { checkUserHasPreloadData(*user, t) } var users3 []*User preloadDB.Preload("Emails", "email = ?", user3.Emails[0].Email).Find(&users3) for _, user := range users3 { if user.Name == user3.Name { if len(user.Emails) != 1 { t.Errorf("should only preload one emails for user3 when with condition") } } else if len(user.Emails) != 0 { t.Errorf("should not preload any emails for other users when with condition") } } } func TestNestedPreload1(t *testing.T) { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1 Level1 Level3ID uint } Level3 struct { ID uint Name string Level2 Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := Level3{Level2: Level2{Level1: Level1{Value: "value"}}} if err := DB.Create(&want).Error; err != nil { panic(err) } var got Level3 if err := DB.Preload("Level2").Preload("Level2.Level1").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } func TestNestedPreload2(t *testing.T) { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1s []*Level1 Level3ID uint } Level3 struct { ID uint Name string Level2s []Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := Level3{ Level2s: []Level2{ { Level1s: []*Level1{ &Level1{Value: "value1"}, &Level1{Value: "value2"}, }, }, { Level1s: []*Level1{ &Level1{Value: "value3"}, }, }, }, } if err := DB.Create(&want).Error; err != nil { panic(err) } var got Level3 if err := DB.Preload("Level2s.Level1s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } func TestNestedPreload3(t *testing.T) { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1 Level1 Level3ID uint } Level3 struct { Name string ID uint Level2s []Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := Level3{ Level2s: []Level2{ {Level1: Level1{Value: "value1"}}, {Level1: Level1{Value: "value2"}}, }, } if err := DB.Create(&want).Error; err != nil { panic(err) } var got Level3 if err := DB.Preload("Level2s.Level1").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } func TestNestedPreload4(t *testing.T) { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1s []Level1 Level3ID uint } Level3 struct { ID uint Name string Level2 Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := Level3{ Level2: Level2{ Level1s: []Level1{ Level1{Value: "value1"}, Level1{Value: "value2"}, }, }, } if err := DB.Create(&want).Error; err != nil { panic(err) } var got Level3 if err := DB.Preload("Level2.Level1s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } // Slice: []Level3 func TestNestedPreload5(t *testing.T) { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1 Level1 Level3ID uint } Level3 struct { ID uint Name string Level2 Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := make([]Level3, 2) want[0] = Level3{Level2: Level2{Level1: Level1{Value: "value"}}} if err := DB.Create(&want[0]).Error; err != nil { panic(err) } want[1] = Level3{Level2: Level2{Level1: Level1{Value: "value2"}}} if err := DB.Create(&want[1]).Error; err != nil { panic(err) } var got []Level3 if err := DB.Preload("Level2").Preload("Level2.Level1").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } func TestNestedPreload6(t *testing.T) { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1s []Level1 Level3ID uint } Level3 struct { ID uint Name string Level2s []Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := make([]Level3, 2) want[0] = Level3{ Level2s: []Level2{ { Level1s: []Level1{ {Value: "value1"}, {Value: "value2"}, }, }, { Level1s: []Level1{ {Value: "value3"}, }, }, }, } if err := DB.Create(&want[0]).Error; err != nil { panic(err) } want[1] = Level3{ Level2s: []Level2{ { Level1s: []Level1{ {Value: "value3"}, {Value: "value4"}, }, }, { Level1s: []Level1{ {Value: "value5"}, }, }, }, } if err := DB.Create(&want[1]).Error; err != nil { panic(err) } var got []Level3 if err := DB.Preload("Level2s.Level1s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } func TestNestedPreload7(t *testing.T) { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1 Level1 Level3ID uint } Level3 struct { ID uint Name string Level2s []Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := make([]Level3, 2) want[0] = Level3{ Level2s: []Level2{ {Level1: Level1{Value: "value1"}}, {Level1: Level1{Value: "value2"}}, }, } if err := DB.Create(&want[0]).Error; err != nil { panic(err) } want[1] = Level3{ Level2s: []Level2{ {Level1: Level1{Value: "value3"}}, {Level1: Level1{Value: "value4"}}, }, } if err := DB.Create(&want[1]).Error; err != nil { panic(err) } var got []Level3 if err := DB.Preload("Level2s.Level1").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } func TestNestedPreload8(t *testing.T) { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1s []Level1 Level3ID uint } Level3 struct { ID uint Name string Level2 Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := make([]Level3, 2) want[0] = Level3{ Level2: Level2{ Level1s: []Level1{ Level1{Value: "value1"}, Level1{Value: "value2"}, }, }, } if err := DB.Create(&want[0]).Error; err != nil { panic(err) } want[1] = Level3{ Level2: Level2{ Level1s: []Level1{ Level1{Value: "value3"}, Level1{Value: "value4"}, }, }, } if err := DB.Create(&want[1]).Error; err != nil { panic(err) } var got []Level3 if err := DB.Preload("Level2.Level1s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } func TestNestedPreload9(t *testing.T) { type ( Level0 struct { ID uint Value string Level1ID uint } Level1 struct { ID uint Value string Level2ID uint Level2_1ID uint Level0s []Level0 } Level2 struct { ID uint Level1s []Level1 Level3ID uint } Level2_1 struct { ID uint Level1s []Level1 Level3ID uint } Level3 struct { ID uint Name string Level2 Level2 Level2_1 Level2_1 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level2_1{}) DB.DropTableIfExists(&Level1{}) DB.DropTableIfExists(&Level0{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}, &Level2_1{}, &Level0{}).Error; err != nil { panic(err) } want := make([]Level3, 2) want[0] = Level3{ Level2: Level2{ Level1s: []Level1{ Level1{Value: "value1"}, Level1{Value: "value2"}, }, }, Level2_1: Level2_1{ Level1s: []Level1{ Level1{ Value: "value1-1", Level0s: []Level0{{Value: "Level0-1"}}, }, Level1{ Value: "value2-2", Level0s: []Level0{{Value: "Level0-2"}}, }, }, }, } if err := DB.Create(&want[0]).Error; err != nil { panic(err) } want[1] = Level3{ Level2: Level2{ Level1s: []Level1{ Level1{Value: "value3"}, Level1{Value: "value4"}, }, }, Level2_1: Level2_1{ Level1s: []Level1{ Level1{Value: "value3-3"}, Level1{Value: "value4-4"}, }, }, } if err := DB.Create(&want[1]).Error; err != nil { panic(err) } var got []Level3 if err := DB.Preload("Level2").Preload("Level2.Level1s").Preload("Level2_1").Preload("Level2_1.Level1s").Preload("Level2_1.Level1s.Level0s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } func TestManyToManyPreload(t *testing.T) { type ( Level1 struct { ID uint `gorm:"primary_key;"` Value string } Level2 struct { ID uint `gorm:"primary_key;"` Value string Level1s []Level1 `gorm:"many2many:levels;"` } ) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level2{}, &Level1{}).Error; err != nil { panic(err) } want := Level2{Value: "Bob", Level1s: []Level1{ Level1{Value: "ru"}, Level1{Value: "en"}, }} if err := DB.Save(&want).Error; err != nil { panic(err) } var got Level2 if err := DB.Preload("Level1s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } func toJSONString(v interface{}) []byte { r, _ := json.MarshalIndent(v, "", " ") return r }