diff --git a/schema/field.go b/schema/field.go index bc47e543..35c1e44d 100644 --- a/schema/field.go +++ b/schema/field.go @@ -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 diff --git a/schema/model_test.go b/schema/model_test.go index 84c7b327..1f2b0948 100644 --- a/schema/model_test.go +++ b/schema/model_test.go @@ -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 { diff --git a/schema/schema.go b/schema/schema.go index d81da4b8..458256d1 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -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)...) + } } } } diff --git a/schema/schema_test.go b/schema/schema_test.go index 966f80e4..c0ad3c25 100644 --- a/schema/schema_test.go +++ b/schema/schema_test.go @@ -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 + }) + } } diff --git a/schema/utils.go b/schema/utils.go index 1481d428..29f2fefb 100644 --- a/schema/utils.go +++ b/schema/utils.go @@ -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) diff --git a/tests/embedded_struct_test.go b/tests/embedded_struct_test.go index fb0d6f23..c29078bd 100644 --- a/tests/embedded_struct_test.go +++ b/tests/embedded_struct_test.go @@ -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) } } diff --git a/utils/tests/utils.go b/utils/tests/utils.go index 0067d5c6..817e4b0b 100644 --- a/utils/tests/utils.go +++ b/utils/tests/utils.go @@ -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 }