Fix nested embedded struct, close #3278

This commit is contained in:
Jinzhu 2020-08-18 11:21:40 +08:00
parent 9fcc337bd1
commit dc48e04896
7 changed files with 46 additions and 30 deletions

View File

@ -301,14 +301,12 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
field.Updatable = false
field.Readable = false
cacheStore := schema.cacheStore
if _, embedded := schema.cacheStore.Load("embedded_cache_store"); !embedded {
cacheStore = &sync.Map{}
cacheStore.Store("embedded_cache_store", true)
}
cacheStore := &sync.Map{}
cacheStore.Store(embeddedCacheKey, true)
if field.EmbeddedSchema, err = Parse(fieldValue.Interface(), cacheStore, schema.namer); err != nil {
schema.err = err
}
for _, ef := range field.EmbeddedSchema.Fields {
ef.Schema = schema
ef.OwnerSchema = field.EmbeddedSchema

View File

@ -41,7 +41,7 @@ type AdvancedDataTypeUser struct {
}
type BaseModel struct {
ID uint `gorm:"primarykey"`
ID uint
CreatedAt time.Time
CreatedBy *int
Created *VersionUser `gorm:"foreignKey:CreatedBy"`
@ -51,8 +51,7 @@ type BaseModel struct {
type VersionModel struct {
BaseModel
Version int
CompanyID int
Version int
}
type VersionUser struct {

View File

@ -212,29 +212,30 @@ func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error)
}
if _, loaded := cacheStore.LoadOrStore(modelType, schema); !loaded {
// parse relations for unidentified fields
for _, field := range schema.Fields {
if field.DataType == "" && field.Creatable {
if schema.parseRelation(field); schema.err != nil {
return schema, schema.err
if _, embedded := schema.cacheStore.Load(embeddedCacheKey); !embedded {
for _, field := range schema.Fields {
if field.DataType == "" && field.Creatable {
if schema.parseRelation(field); schema.err != nil {
return schema, schema.err
}
}
}
fieldValue := reflect.New(field.IndirectFieldType)
if fc, ok := fieldValue.Interface().(CreateClausesInterface); ok {
field.Schema.CreateClauses = append(field.Schema.CreateClauses, fc.CreateClauses(field)...)
}
fieldValue := reflect.New(field.IndirectFieldType)
if fc, ok := fieldValue.Interface().(CreateClausesInterface); ok {
field.Schema.CreateClauses = append(field.Schema.CreateClauses, fc.CreateClauses(field)...)
}
if fc, ok := fieldValue.Interface().(QueryClausesInterface); ok {
field.Schema.QueryClauses = append(field.Schema.QueryClauses, fc.QueryClauses(field)...)
}
if fc, ok := fieldValue.Interface().(QueryClausesInterface); ok {
field.Schema.QueryClauses = append(field.Schema.QueryClauses, fc.QueryClauses(field)...)
}
if fc, ok := fieldValue.Interface().(UpdateClausesInterface); ok {
field.Schema.UpdateClauses = append(field.Schema.UpdateClauses, fc.UpdateClauses(field)...)
}
if fc, ok := fieldValue.Interface().(UpdateClausesInterface); ok {
field.Schema.UpdateClauses = append(field.Schema.UpdateClauses, fc.UpdateClauses(field)...)
}
if fc, ok := fieldValue.Interface().(DeleteClausesInterface); ok {
field.Schema.DeleteClauses = append(field.Schema.DeleteClauses, fc.DeleteClauses(field)...)
if fc, ok := fieldValue.Interface().(DeleteClausesInterface); ok {
field.Schema.DeleteClauses = append(field.Schema.DeleteClauses, fc.DeleteClauses(field)...)
}
}
}
}

View File

@ -162,7 +162,23 @@ func TestCustomizeTableName(t *testing.T) {
}
func TestNestedModel(t *testing.T) {
if _, err := schema.Parse(&VersionUser{}, &sync.Map{}, schema.NamingStrategy{}); err != nil {
versionUser, err := schema.Parse(&VersionUser{}, &sync.Map{}, schema.NamingStrategy{})
if err != nil {
t.Fatalf("failed to parse nested user, got error %v", err)
}
fields := []schema.Field{
{Name: "ID", DBName: "id", BindNames: []string{"VersionModel", "BaseModel", "ID"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true},
{Name: "CreatedBy", DBName: "created_by", BindNames: []string{"VersionModel", "BaseModel", "CreatedBy"}, DataType: schema.Int, Size: 64},
{Name: "Version", DBName: "version", BindNames: []string{"VersionModel", "Version"}, DataType: schema.Int, Size: 64},
}
for _, f := range fields {
checkSchemaField(t, versionUser, &f, func(f *schema.Field) {
f.Creatable = true
f.Updatable = true
f.Readable = true
})
}
}

View File

@ -9,6 +9,8 @@ import (
"gorm.io/gorm/utils"
)
var embeddedCacheKey = "embedded_cache_store"
func ParseTagSetting(str string, sep string) map[string]string {
settings := map[string]string{}
names := strings.Split(str, sep)

View File

@ -160,9 +160,9 @@ func TestEmbeddedRelations(t *testing.T) {
Advanced bool
}
DB.Debug().Migrator().DropTable(&AdvancedUser{})
DB.Migrator().DropTable(&AdvancedUser{})
if err := DB.Debug().AutoMigrate(&AdvancedUser{}); err != nil {
if err := DB.AutoMigrate(&AdvancedUser{}); err != nil {
t.Errorf("Failed to auto migrate advanced user, got error %v", err)
}
}

View File

@ -76,7 +76,7 @@ func AssertEqual(t *testing.T, got, expect interface{}) {
}
} else {
name := reflect.ValueOf(got).Type().Elem().Name()
t.Errorf("%v expects length: %v, got %v", name, reflect.ValueOf(expect).Len(), reflect.ValueOf(got).Len())
t.Errorf("%v expects length: %v, got %v (expects: %+v, got %+v)", name, reflect.ValueOf(expect).Len(), reflect.ValueOf(got).Len(), expect, got)
}
return
}