Merge branch 'master' of https://github.com/jinzhu/gorm
This commit is contained in:
		
						commit
						387c00d2b5
					
				@ -1064,10 +1064,10 @@ func (u *User) AfterCreate() (err error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
As you know, save/delete operations in gorm are running in a transaction,
 | 
					Save/delete operations in gorm are running in a transaction.  
 | 
				
			||||||
This is means if changes made in the transaction is not visiable unless it is commited,
 | 
					Changes made in that transaction are not visible unless it is commited.
 | 
				
			||||||
So if you want to use those changes in your callbacks, you need to run SQL in same transaction.
 | 
					So if you want to use those changes in your callbacks, you need to run your SQL in the same transaction.
 | 
				
			||||||
Fortunately, gorm support pass transaction to callbacks as you needed, you could do it like this:
 | 
					For this Gorm supports passing transactions to callbacks like this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```go
 | 
					```go
 | 
				
			||||||
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
 | 
					func (u *User) AfterCreate(tx *gorm.DB) (err error) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								main.go
									
									
									
									
									
								
							@ -392,6 +392,10 @@ func (s *DB) DropTable(values ...interface{}) *DB {
 | 
				
			|||||||
func (s *DB) DropTableIfExists(values ...interface{}) *DB {
 | 
					func (s *DB) DropTableIfExists(values ...interface{}) *DB {
 | 
				
			||||||
	db := s.clone()
 | 
						db := s.clone()
 | 
				
			||||||
	for _, value := range values {
 | 
						for _, value := range values {
 | 
				
			||||||
 | 
							if tableName, ok := value.(string); ok {
 | 
				
			||||||
 | 
								db = db.Table(tableName)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		db = db.NewScope(value).dropTableIfExists().db
 | 
							db = db.NewScope(value).dropTableIfExists().db
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return db
 | 
						return db
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								preload.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								preload.go
									
									
									
									
									
								
							@ -9,15 +9,20 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getRealValue(value reflect.Value, columns []string) (results []interface{}) {
 | 
					func getRealValue(value reflect.Value, columns []string) (results []interface{}) {
 | 
				
			||||||
 | 
						// If value is a nil pointer, Indirect returns a zero Value!
 | 
				
			||||||
 | 
						// Therefor we need to check for a zero value,
 | 
				
			||||||
 | 
						// as FieldByName could panic
 | 
				
			||||||
 | 
						if pointedValue := reflect.Indirect(value); pointedValue.IsValid() {
 | 
				
			||||||
		for _, column := range columns {
 | 
							for _, column := range columns {
 | 
				
			||||||
		if reflect.Indirect(value).FieldByName(column).IsValid() {
 | 
								if pointedValue.FieldByName(column).IsValid() {
 | 
				
			||||||
			result := reflect.Indirect(value).FieldByName(column).Interface()
 | 
									result := pointedValue.FieldByName(column).Interface()
 | 
				
			||||||
				if r, ok := result.(driver.Valuer); ok {
 | 
									if r, ok := result.(driver.Valuer); ok {
 | 
				
			||||||
					result, _ = r.Value()
 | 
										result, _ = r.Value()
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				results = append(results, result)
 | 
									results = append(results, result)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -290,13 +295,14 @@ func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		object := scope.IndirectValue()
 | 
							if object := scope.IndirectValue(); object.IsValid() {
 | 
				
			||||||
			source := getRealValue(object, associationForeignStructFieldNames)
 | 
								source := getRealValue(object, associationForeignStructFieldNames)
 | 
				
			||||||
			field := object.FieldByName(field.Name)
 | 
								field := object.FieldByName(field.Name)
 | 
				
			||||||
			for _, link := range linkHash[toString(source)] {
 | 
								for _, link := range linkHash[toString(source)] {
 | 
				
			||||||
				field.Set(reflect.Append(field, link))
 | 
									field.Set(reflect.Append(field, link))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (scope *Scope) getColumnAsArray(columns []string) (results [][]interface{}) {
 | 
					func (scope *Scope) getColumnAsArray(columns []string) (results [][]interface{}) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										253
									
								
								preload_test.go
									
									
									
									
									
								
							
							
						
						
									
										253
									
								
								preload_test.go
									
									
									
									
									
								
							@ -1,10 +1,13 @@
 | 
				
			|||||||
package gorm_test
 | 
					package gorm_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/jinzhu/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getPreloadUser(name string) *User {
 | 
					func getPreloadUser(name string) *User {
 | 
				
			||||||
@ -129,6 +132,10 @@ func TestNestedPreload1(t *testing.T) {
 | 
				
			|||||||
	if !reflect.DeepEqual(got, want) {
 | 
						if !reflect.DeepEqual(got, want) {
 | 
				
			||||||
		t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want))
 | 
							t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.Preload("Level2").Preload("Level2.Level1").Find(&got, "name = ?", "not_found").Error; err != gorm.RecordNotFound {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestNestedPreload2(t *testing.T) {
 | 
					func TestNestedPreload2(t *testing.T) {
 | 
				
			||||||
@ -625,7 +632,7 @@ func TestManyToManyPreloadWithMultiPrimaryKeys(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	DB.DropTableIfExists(&Level2{})
 | 
						DB.DropTableIfExists(&Level2{})
 | 
				
			||||||
	DB.DropTableIfExists(&Level1{})
 | 
						DB.DropTableIfExists(&Level1{})
 | 
				
			||||||
	DB.Table("levels").DropTableIfExists("levels")
 | 
						DB.DropTableIfExists("levels")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := DB.AutoMigrate(&Level2{}, &Level1{}).Error; err != nil {
 | 
						if err := DB.AutoMigrate(&Level2{}, &Level1{}).Error; err != nil {
 | 
				
			||||||
		panic(err)
 | 
							panic(err)
 | 
				
			||||||
@ -698,11 +705,11 @@ func TestManyToManyPreloadWithMultiPrimaryKeys(t *testing.T) {
 | 
				
			|||||||
func TestManyToManyPreloadForPointer(t *testing.T) {
 | 
					func TestManyToManyPreloadForPointer(t *testing.T) {
 | 
				
			||||||
	type (
 | 
						type (
 | 
				
			||||||
		Level1 struct {
 | 
							Level1 struct {
 | 
				
			||||||
			ID    uint `gorm:"primary_key;"`
 | 
								ID    uint
 | 
				
			||||||
			Value string
 | 
								Value string
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		Level2 struct {
 | 
							Level2 struct {
 | 
				
			||||||
			ID      uint `gorm:"primary_key;"`
 | 
								ID      uint
 | 
				
			||||||
			Value   string
 | 
								Value   string
 | 
				
			||||||
			Level1s []*Level1 `gorm:"many2many:levels;"`
 | 
								Level1s []*Level1 `gorm:"many2many:levels;"`
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -710,7 +717,7 @@ func TestManyToManyPreloadForPointer(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	DB.DropTableIfExists(&Level2{})
 | 
						DB.DropTableIfExists(&Level2{})
 | 
				
			||||||
	DB.DropTableIfExists(&Level1{})
 | 
						DB.DropTableIfExists(&Level1{})
 | 
				
			||||||
	DB.Table("levels").DropTableIfExists("levels")
 | 
						DB.DropTableIfExists("levels")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := DB.AutoMigrate(&Level2{}, &Level1{}).Error; err != nil {
 | 
						if err := DB.AutoMigrate(&Level2{}, &Level1{}).Error; err != nil {
 | 
				
			||||||
		panic(err)
 | 
							panic(err)
 | 
				
			||||||
@ -764,6 +771,9 @@ func TestManyToManyPreloadForPointer(t *testing.T) {
 | 
				
			|||||||
		panic(err)
 | 
							panic(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var got5 Level2
 | 
				
			||||||
 | 
						DB.Preload("Level1s").First(&got5, "value = ?", "bogus")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var ruLevel1 Level1
 | 
						var ruLevel1 Level1
 | 
				
			||||||
	var zhLevel1 Level1
 | 
						var zhLevel1 Level1
 | 
				
			||||||
	DB.First(&ruLevel1, "value = ?", "ru")
 | 
						DB.First(&ruLevel1, "value = ?", "ru")
 | 
				
			||||||
@ -776,20 +786,245 @@ func TestManyToManyPreloadForPointer(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestNilPointerSlice(t *testing.T) {
 | 
					func TestManyToManyPreloadForNestedPointer(t *testing.T) {
 | 
				
			||||||
	type (
 | 
						type (
 | 
				
			||||||
		Level3 struct {
 | 
							Level1 struct {
 | 
				
			||||||
			ID    uint `gorm:"primary_key;"`
 | 
								ID    uint
 | 
				
			||||||
			Value string
 | 
								Value string
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		Level2 struct {
 | 
							Level2 struct {
 | 
				
			||||||
			ID       uint `gorm:"primary_key;"`
 | 
								ID      uint
 | 
				
			||||||
 | 
								Value   string
 | 
				
			||||||
 | 
								Level1s []*Level1 `gorm:"many2many:levels;"`
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Level3 struct {
 | 
				
			||||||
 | 
								ID       uint
 | 
				
			||||||
 | 
								Value    string
 | 
				
			||||||
 | 
								Level2ID sql.NullInt64
 | 
				
			||||||
 | 
								Level2   *Level2
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DB.DropTableIfExists(&Level3{})
 | 
				
			||||||
 | 
						DB.DropTableIfExists(&Level2{})
 | 
				
			||||||
 | 
						DB.DropTableIfExists(&Level1{})
 | 
				
			||||||
 | 
						DB.DropTableIfExists("levels")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						want := Level3{
 | 
				
			||||||
 | 
							Value: "Bob",
 | 
				
			||||||
 | 
							Level2: &Level2{
 | 
				
			||||||
 | 
								Value: "Foo",
 | 
				
			||||||
 | 
								Level1s: []*Level1{
 | 
				
			||||||
 | 
									{Value: "ru"},
 | 
				
			||||||
 | 
									{Value: "en"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := DB.Save(&want).Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						want2 := Level3{
 | 
				
			||||||
 | 
							Value: "Tom",
 | 
				
			||||||
 | 
							Level2: &Level2{
 | 
				
			||||||
 | 
								Value: "Bar",
 | 
				
			||||||
 | 
								Level1s: []*Level1{
 | 
				
			||||||
 | 
									{Value: "zh"},
 | 
				
			||||||
 | 
									{Value: "de"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := DB.Save(&want2).Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var got Level3
 | 
				
			||||||
 | 
						if err := DB.Preload("Level2.Level1s").Find(&got, "value = ?", "Bob").Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(got, want) {
 | 
				
			||||||
 | 
							t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var got2 Level3
 | 
				
			||||||
 | 
						if err := DB.Preload("Level2.Level1s").Find(&got2, "value = ?", "Tom").Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(got2, want2) {
 | 
				
			||||||
 | 
							t.Errorf("got %s; want %s", toJSONString(got2), toJSONString(want2))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var got3 []Level3
 | 
				
			||||||
 | 
						if err := DB.Preload("Level2.Level1s").Find(&got3, "value IN (?)", []string{"Bob", "Tom"}).Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(got3, []Level3{got, got2}) {
 | 
				
			||||||
 | 
							t.Errorf("got %s; want %s", toJSONString(got3), toJSONString([]Level3{got, got2}))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var got4 []Level3
 | 
				
			||||||
 | 
						if err := DB.Preload("Level2.Level1s", "value IN (?)", []string{"zh", "ru"}).Find(&got4, "value IN (?)", []string{"Bob", "Tom"}).Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var got5 Level3
 | 
				
			||||||
 | 
						DB.Preload("Level2.Level1s").Find(&got5, "value = ?", "bogus")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var ruLevel1 Level1
 | 
				
			||||||
 | 
						var zhLevel1 Level1
 | 
				
			||||||
 | 
						DB.First(&ruLevel1, "value = ?", "ru")
 | 
				
			||||||
 | 
						DB.First(&zhLevel1, "value = ?", "zh")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						got.Level2.Level1s = []*Level1{&ruLevel1}
 | 
				
			||||||
 | 
						got2.Level2.Level1s = []*Level1{&zhLevel1}
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(got4, []Level3{got, got2}) {
 | 
				
			||||||
 | 
							t.Errorf("got %s; want %s", toJSONString(got4), toJSONString([]Level3{got, got2}))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNestedManyToManyPreload(t *testing.T) {
 | 
				
			||||||
 | 
						type (
 | 
				
			||||||
 | 
							Level1 struct {
 | 
				
			||||||
 | 
								ID    uint
 | 
				
			||||||
 | 
								Value string
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Level2 struct {
 | 
				
			||||||
 | 
								ID      uint
 | 
				
			||||||
 | 
								Value   string
 | 
				
			||||||
 | 
								Level1s []*Level1 `gorm:"many2many:level1_level2;"`
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Level3 struct {
 | 
				
			||||||
 | 
								ID      uint
 | 
				
			||||||
 | 
								Value   string
 | 
				
			||||||
 | 
								Level2s []Level2 `gorm:"many2many:level2_level3;"`
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DB.DropTableIfExists(&Level1{})
 | 
				
			||||||
 | 
						DB.DropTableIfExists(&Level2{})
 | 
				
			||||||
 | 
						DB.DropTableIfExists(&Level3{})
 | 
				
			||||||
 | 
						DB.DropTableIfExists("level1_level2")
 | 
				
			||||||
 | 
						DB.DropTableIfExists("level2_level3")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						want := Level3{
 | 
				
			||||||
 | 
							Value: "Level3",
 | 
				
			||||||
 | 
							Level2s: []Level2{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Value: "Bob",
 | 
				
			||||||
 | 
									Level1s: []*Level1{
 | 
				
			||||||
 | 
										{Value: "ru"},
 | 
				
			||||||
 | 
										{Value: "en"},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									Value: "Tom",
 | 
				
			||||||
 | 
									Level1s: []*Level1{
 | 
				
			||||||
 | 
										{Value: "zh"},
 | 
				
			||||||
 | 
										{Value: "de"},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.Save(&want).Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var got Level3
 | 
				
			||||||
 | 
						if err := DB.Preload("Level2s").Preload("Level2s.Level1s").Find(&got, "value = ?", "Level3").Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(got, want) {
 | 
				
			||||||
 | 
							t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.Preload("Level2s.Level1s").Find(&got, "value = ?", "not_found").Error; err != gorm.RecordNotFound {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNestedManyToManyPreload2(t *testing.T) {
 | 
				
			||||||
 | 
						type (
 | 
				
			||||||
 | 
							Level1 struct {
 | 
				
			||||||
 | 
								ID    uint
 | 
				
			||||||
 | 
								Value string
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Level2 struct {
 | 
				
			||||||
 | 
								ID      uint
 | 
				
			||||||
 | 
								Value   string
 | 
				
			||||||
 | 
								Level1s []*Level1 `gorm:"many2many:level1_level2;"`
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Level3 struct {
 | 
				
			||||||
 | 
								ID       uint
 | 
				
			||||||
 | 
								Value    string
 | 
				
			||||||
 | 
								Level2ID sql.NullInt64
 | 
				
			||||||
 | 
								Level2   *Level2
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DB.DropTableIfExists(&Level1{})
 | 
				
			||||||
 | 
						DB.DropTableIfExists(&Level2{})
 | 
				
			||||||
 | 
						DB.DropTableIfExists(&Level3{})
 | 
				
			||||||
 | 
						DB.DropTableIfExists("level1_level2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						want := Level3{
 | 
				
			||||||
 | 
							Value: "Level3",
 | 
				
			||||||
 | 
							Level2: &Level2{
 | 
				
			||||||
 | 
								Value: "Bob",
 | 
				
			||||||
 | 
								Level1s: []*Level1{
 | 
				
			||||||
 | 
									{Value: "ru"},
 | 
				
			||||||
 | 
									{Value: "en"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.Save(&want).Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var got Level3
 | 
				
			||||||
 | 
						if err := DB.Preload("Level2.Level1s").Find(&got, "value = ?", "Level3").Error; err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(got, want) {
 | 
				
			||||||
 | 
							t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.Preload("Level2.Level1s").Find(&got, "value = ?", "not_found").Error; err != gorm.RecordNotFound {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNilPointerSlice(t *testing.T) {
 | 
				
			||||||
 | 
						type (
 | 
				
			||||||
 | 
							Level3 struct {
 | 
				
			||||||
 | 
								ID    uint
 | 
				
			||||||
 | 
								Value string
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Level2 struct {
 | 
				
			||||||
 | 
								ID       uint
 | 
				
			||||||
			Value    string
 | 
								Value    string
 | 
				
			||||||
			Level3ID uint
 | 
								Level3ID uint
 | 
				
			||||||
			Level3   *Level3
 | 
								Level3   *Level3
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		Level1 struct {
 | 
							Level1 struct {
 | 
				
			||||||
			ID       uint `gorm:"primary_key;"`
 | 
								ID       uint
 | 
				
			||||||
			Value    string
 | 
								Value    string
 | 
				
			||||||
			Level2ID uint
 | 
								Level2ID uint
 | 
				
			||||||
			Level2   *Level2
 | 
								Level2   *Level2
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user