OSDN Git Service

Merge pull request #201 from Bytom/v0.1
[bytom/vapor.git] / vendor / github.com / jinzhu / gorm / model_struct.go
diff --git a/vendor/github.com/jinzhu/gorm/model_struct.go b/vendor/github.com/jinzhu/gorm/model_struct.go
new file mode 100755 (executable)
index 0000000..8c27e20
--- /dev/null
@@ -0,0 +1,640 @@
+package gorm
+
+import (
+       "database/sql"
+       "errors"
+       "go/ast"
+       "reflect"
+       "strings"
+       "sync"
+       "time"
+
+       "github.com/jinzhu/inflection"
+)
+
+// DefaultTableNameHandler default table name handler
+var DefaultTableNameHandler = func(db *DB, defaultTableName string) string {
+       return defaultTableName
+}
+
+var modelStructsMap sync.Map
+
+// ModelStruct model definition
+type ModelStruct struct {
+       PrimaryFields    []*StructField
+       StructFields     []*StructField
+       ModelType        reflect.Type
+
+       defaultTableName string
+       l sync.Mutex
+}
+
+// TableName returns model's table name
+func (s *ModelStruct) TableName(db *DB) string {
+       s.l.Lock()
+       defer s.l.Unlock()
+
+       if s.defaultTableName == "" && db != nil && s.ModelType != nil {
+               // Set default table name
+               if tabler, ok := reflect.New(s.ModelType).Interface().(tabler); ok {
+                       s.defaultTableName = tabler.TableName()
+               } else {
+                       tableName := ToTableName(s.ModelType.Name())
+                       if db == nil || !db.parent.singularTable {
+                               tableName = inflection.Plural(tableName)
+                       }
+                       s.defaultTableName = tableName
+               }
+       }
+
+       return DefaultTableNameHandler(db, s.defaultTableName)
+}
+
+// StructField model field's struct definition
+type StructField struct {
+       DBName          string
+       Name            string
+       Names           []string
+       IsPrimaryKey    bool
+       IsNormal        bool
+       IsIgnored       bool
+       IsScanner       bool
+       HasDefaultValue bool
+       Tag             reflect.StructTag
+       TagSettings     map[string]string
+       Struct          reflect.StructField
+       IsForeignKey    bool
+       Relationship    *Relationship
+
+       tagSettingsLock sync.RWMutex
+}
+
+// TagSettingsSet Sets a tag in the tag settings map
+func (s *StructField) TagSettingsSet(key, val string) {
+       s.tagSettingsLock.Lock()
+       defer s.tagSettingsLock.Unlock()
+       s.TagSettings[key] = val
+}
+
+// TagSettingsGet returns a tag from the tag settings
+func (s *StructField) TagSettingsGet(key string) (string, bool) {
+       s.tagSettingsLock.RLock()
+       defer s.tagSettingsLock.RUnlock()
+       val, ok := s.TagSettings[key]
+       return val, ok
+}
+
+// TagSettingsDelete deletes a tag
+func (s *StructField) TagSettingsDelete(key string) {
+       s.tagSettingsLock.Lock()
+       defer s.tagSettingsLock.Unlock()
+       delete(s.TagSettings, key)
+}
+
+func (structField *StructField) clone() *StructField {
+       clone := &StructField{
+               DBName:          structField.DBName,
+               Name:            structField.Name,
+               Names:           structField.Names,
+               IsPrimaryKey:    structField.IsPrimaryKey,
+               IsNormal:        structField.IsNormal,
+               IsIgnored:       structField.IsIgnored,
+               IsScanner:       structField.IsScanner,
+               HasDefaultValue: structField.HasDefaultValue,
+               Tag:             structField.Tag,
+               TagSettings:     map[string]string{},
+               Struct:          structField.Struct,
+               IsForeignKey:    structField.IsForeignKey,
+       }
+
+       if structField.Relationship != nil {
+               relationship := *structField.Relationship
+               clone.Relationship = &relationship
+       }
+
+       // copy the struct field tagSettings, they should be read-locked while they are copied
+       structField.tagSettingsLock.Lock()
+       defer structField.tagSettingsLock.Unlock()
+       for key, value := range structField.TagSettings {
+               clone.TagSettings[key] = value
+       }
+
+       return clone
+}
+
+// Relationship described the relationship between models
+type Relationship struct {
+       Kind                         string
+       PolymorphicType              string
+       PolymorphicDBName            string
+       PolymorphicValue             string
+       ForeignFieldNames            []string
+       ForeignDBNames               []string
+       AssociationForeignFieldNames []string
+       AssociationForeignDBNames    []string
+       JoinTableHandler             JoinTableHandlerInterface
+}
+
+func getForeignField(column string, fields []*StructField) *StructField {
+       for _, field := range fields {
+               if field.Name == column || field.DBName == column || field.DBName == ToColumnName(column) {
+                       return field
+               }
+       }
+       return nil
+}
+
+// GetModelStruct get value's model struct, relationships based on struct and tag definition
+func (scope *Scope) GetModelStruct() *ModelStruct {
+       var modelStruct ModelStruct
+       // Scope value can't be nil
+       if scope.Value == nil {
+               return &modelStruct
+       }
+
+       reflectType := reflect.ValueOf(scope.Value).Type()
+       for reflectType.Kind() == reflect.Slice || reflectType.Kind() == reflect.Ptr {
+               reflectType = reflectType.Elem()
+       }
+
+       // Scope value need to be a struct
+       if reflectType.Kind() != reflect.Struct {
+               return &modelStruct
+       }
+
+       // Get Cached model struct
+       if value, ok := modelStructsMap.Load(reflectType); ok && value != nil {
+               return value.(*ModelStruct)
+       }
+
+       modelStruct.ModelType = reflectType
+
+       // Get all fields
+       for i := 0; i < reflectType.NumField(); i++ {
+               if fieldStruct := reflectType.Field(i); ast.IsExported(fieldStruct.Name) {
+                       field := &StructField{
+                               Struct:      fieldStruct,
+                               Name:        fieldStruct.Name,
+                               Names:       []string{fieldStruct.Name},
+                               Tag:         fieldStruct.Tag,
+                               TagSettings: parseTagSetting(fieldStruct.Tag),
+                       }
+
+                       // is ignored field
+                       if _, ok := field.TagSettingsGet("-"); ok {
+                               field.IsIgnored = true
+                       } else {
+                               if _, ok := field.TagSettingsGet("PRIMARY_KEY"); ok {
+                                       field.IsPrimaryKey = true
+                                       modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
+                               }
+
+                               if _, ok := field.TagSettingsGet("DEFAULT"); ok {
+                                       field.HasDefaultValue = true
+                               }
+
+                               if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok && !field.IsPrimaryKey {
+                                       field.HasDefaultValue = true
+                               }
+
+                               indirectType := fieldStruct.Type
+                               for indirectType.Kind() == reflect.Ptr {
+                                       indirectType = indirectType.Elem()
+                               }
+
+                               fieldValue := reflect.New(indirectType).Interface()
+                               if _, isScanner := fieldValue.(sql.Scanner); isScanner {
+                                       // is scanner
+                                       field.IsScanner, field.IsNormal = true, true
+                                       if indirectType.Kind() == reflect.Struct {
+                                               for i := 0; i < indirectType.NumField(); i++ {
+                                                       for key, value := range parseTagSetting(indirectType.Field(i).Tag) {
+                                                               if _, ok := field.TagSettingsGet(key); !ok {
+                                                                       field.TagSettingsSet(key, value)
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               } else if _, isTime := fieldValue.(*time.Time); isTime {
+                                       // is time
+                                       field.IsNormal = true
+                               } else if _, ok := field.TagSettingsGet("EMBEDDED"); ok || fieldStruct.Anonymous {
+                                       // is embedded struct
+                                       for _, subField := range scope.New(fieldValue).GetModelStruct().StructFields {
+                                               subField = subField.clone()
+                                               subField.Names = append([]string{fieldStruct.Name}, subField.Names...)
+                                               if prefix, ok := field.TagSettingsGet("EMBEDDED_PREFIX"); ok {
+                                                       subField.DBName = prefix + subField.DBName
+                                               }
+
+                                               if subField.IsPrimaryKey {
+                                                       if _, ok := subField.TagSettingsGet("PRIMARY_KEY"); ok {
+                                                               modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, subField)
+                                                       } else {
+                                                               subField.IsPrimaryKey = false
+                                                       }
+                                               }
+
+                                               if subField.Relationship != nil && subField.Relationship.JoinTableHandler != nil {
+                                                       if joinTableHandler, ok := subField.Relationship.JoinTableHandler.(*JoinTableHandler); ok {
+                                                               newJoinTableHandler := &JoinTableHandler{}
+                                                               newJoinTableHandler.Setup(subField.Relationship, joinTableHandler.TableName, reflectType, joinTableHandler.Destination.ModelType)
+                                                               subField.Relationship.JoinTableHandler = newJoinTableHandler
+                                                       }
+                                               }
+
+                                               modelStruct.StructFields = append(modelStruct.StructFields, subField)
+                                       }
+                                       continue
+                               } else {
+                                       // build relationships
+                                       switch indirectType.Kind() {
+                                       case reflect.Slice:
+                                               defer func(field *StructField) {
+                                                       var (
+                                                               relationship           = &Relationship{}
+                                                               toScope                = scope.New(reflect.New(field.Struct.Type).Interface())
+                                                               foreignKeys            []string
+                                                               associationForeignKeys []string
+                                                               elemType               = field.Struct.Type
+                                                       )
+
+                                                       if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
+                                                               foreignKeys = strings.Split(foreignKey, ",")
+                                                       }
+
+                                                       if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
+                                                               associationForeignKeys = strings.Split(foreignKey, ",")
+                                                       } else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
+                                                               associationForeignKeys = strings.Split(foreignKey, ",")
+                                                       }
+
+                                                       for elemType.Kind() == reflect.Slice || elemType.Kind() == reflect.Ptr {
+                                                               elemType = elemType.Elem()
+                                                       }
+
+                                                       if elemType.Kind() == reflect.Struct {
+                                                               if many2many, _ := field.TagSettingsGet("MANY2MANY"); many2many != "" {
+                                                                       relationship.Kind = "many_to_many"
+
+                                                                       { // Foreign Keys for Source
+                                                                               joinTableDBNames := []string{}
+
+                                                                               if foreignKey, _ := field.TagSettingsGet("JOINTABLE_FOREIGNKEY"); foreignKey != "" {
+                                                                                       joinTableDBNames = strings.Split(foreignKey, ",")
+                                                                               }
+
+                                                                               // if no foreign keys defined with tag
+                                                                               if len(foreignKeys) == 0 {
+                                                                                       for _, field := range modelStruct.PrimaryFields {
+                                                                                               foreignKeys = append(foreignKeys, field.DBName)
+                                                                                       }
+                                                                               }
+
+                                                                               for idx, foreignKey := range foreignKeys {
+                                                                                       if foreignField := getForeignField(foreignKey, modelStruct.StructFields); foreignField != nil {
+                                                                                               // source foreign keys (db names)
+                                                                                               relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.DBName)
+
+                                                                                               // setup join table foreign keys for source
+                                                                                               if len(joinTableDBNames) > idx {
+                                                                                                       // if defined join table's foreign key
+                                                                                                       relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBNames[idx])
+                                                                                               } else {
+                                                                                                       defaultJointableForeignKey := ToColumnName(reflectType.Name()) + "_" + foreignField.DBName
+                                                                                                       relationship.ForeignDBNames = append(relationship.ForeignDBNames, defaultJointableForeignKey)
+                                                                                               }
+                                                                                       }
+                                                                               }
+                                                                       }
+
+                                                                       { // Foreign Keys for Association (Destination)
+                                                                               associationJoinTableDBNames := []string{}
+
+                                                                               if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_JOINTABLE_FOREIGNKEY"); foreignKey != "" {
+                                                                                       associationJoinTableDBNames = strings.Split(foreignKey, ",")
+                                                                               }
+
+                                                                               // if no association foreign keys defined with tag
+                                                                               if len(associationForeignKeys) == 0 {
+                                                                                       for _, field := range toScope.PrimaryFields() {
+                                                                                               associationForeignKeys = append(associationForeignKeys, field.DBName)
+                                                                                       }
+                                                                               }
+
+                                                                               for idx, name := range associationForeignKeys {
+                                                                                       if field, ok := toScope.FieldByName(name); ok {
+                                                                                               // association foreign keys (db names)
+                                                                                               relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, field.DBName)
+
+                                                                                               // setup join table foreign keys for association
+                                                                                               if len(associationJoinTableDBNames) > idx {
+                                                                                                       relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationJoinTableDBNames[idx])
+                                                                                               } else {
+                                                                                                       // join table foreign keys for association
+                                                                                                       joinTableDBName := ToColumnName(elemType.Name()) + "_" + field.DBName
+                                                                                                       relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
+                                                                                               }
+                                                                                       }
+                                                                               }
+                                                                       }
+
+                                                                       joinTableHandler := JoinTableHandler{}
+                                                                       joinTableHandler.Setup(relationship, ToTableName(many2many), reflectType, elemType)
+                                                                       relationship.JoinTableHandler = &joinTableHandler
+                                                                       field.Relationship = relationship
+                                                               } else {
+                                                                       // User has many comments, associationType is User, comment use UserID as foreign key
+                                                                       var associationType = reflectType.Name()
+                                                                       var toFields = toScope.GetStructFields()
+                                                                       relationship.Kind = "has_many"
+
+                                                                       if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
+                                                                               // Dog has many toys, tag polymorphic is Owner, then associationType is Owner
+                                                                               // Toy use OwnerID, OwnerType ('dogs') as foreign key
+                                                                               if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
+                                                                                       associationType = polymorphic
+                                                                                       relationship.PolymorphicType = polymorphicType.Name
+                                                                                       relationship.PolymorphicDBName = polymorphicType.DBName
+                                                                                       // if Dog has multiple set of toys set name of the set (instead of default 'dogs')
+                                                                                       if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
+                                                                                               relationship.PolymorphicValue = value
+                                                                                       } else {
+                                                                                               relationship.PolymorphicValue = scope.TableName()
+                                                                                       }
+                                                                                       polymorphicType.IsForeignKey = true
+                                                                               }
+                                                                       }
+
+                                                                       // if no foreign keys defined with tag
+                                                                       if len(foreignKeys) == 0 {
+                                                                               // if no association foreign keys defined with tag
+                                                                               if len(associationForeignKeys) == 0 {
+                                                                                       for _, field := range modelStruct.PrimaryFields {
+                                                                                               foreignKeys = append(foreignKeys, associationType+field.Name)
+                                                                                               associationForeignKeys = append(associationForeignKeys, field.Name)
+                                                                                       }
+                                                                               } else {
+                                                                                       // generate foreign keys from defined association foreign keys
+                                                                                       for _, scopeFieldName := range associationForeignKeys {
+                                                                                               if foreignField := getForeignField(scopeFieldName, modelStruct.StructFields); foreignField != nil {
+                                                                                                       foreignKeys = append(foreignKeys, associationType+foreignField.Name)
+                                                                                                       associationForeignKeys = append(associationForeignKeys, foreignField.Name)
+                                                                                               }
+                                                                                       }
+                                                                               }
+                                                                       } else {
+                                                                               // generate association foreign keys from foreign keys
+                                                                               if len(associationForeignKeys) == 0 {
+                                                                                       for _, foreignKey := range foreignKeys {
+                                                                                               if strings.HasPrefix(foreignKey, associationType) {
+                                                                                                       associationForeignKey := strings.TrimPrefix(foreignKey, associationType)
+                                                                                                       if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil {
+                                                                                                               associationForeignKeys = append(associationForeignKeys, associationForeignKey)
+                                                                                                       }
+                                                                                               }
+                                                                                       }
+                                                                                       if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
+                                                                                               associationForeignKeys = []string{scope.PrimaryKey()}
+                                                                                       }
+                                                                               } else if len(foreignKeys) != len(associationForeignKeys) {
+                                                                                       scope.Err(errors.New("invalid foreign keys, should have same length"))
+                                                                                       return
+                                                                               }
+                                                                       }
+
+                                                                       for idx, foreignKey := range foreignKeys {
+                                                                               if foreignField := getForeignField(foreignKey, toFields); foreignField != nil {
+                                                                                       if associationField := getForeignField(associationForeignKeys[idx], modelStruct.StructFields); associationField != nil {
+                                                                                               // source foreign keys
+                                                                                               foreignField.IsForeignKey = true
+                                                                                               relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, associationField.Name)
+                                                                                               relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationField.DBName)
+
+                                                                                               // association foreign keys
+                                                                                               relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
+                                                                                               relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
+                                                                                       }
+                                                                               }
+                                                                       }
+
+                                                                       if len(relationship.ForeignFieldNames) != 0 {
+                                                                               field.Relationship = relationship
+                                                                       }
+                                                               }
+                                                       } else {
+                                                               field.IsNormal = true
+                                                       }
+                                               }(field)
+                                       case reflect.Struct:
+                                               defer func(field *StructField) {
+                                                       var (
+                                                               // user has one profile, associationType is User, profile use UserID as foreign key
+                                                               // user belongs to profile, associationType is Profile, user use ProfileID as foreign key
+                                                               associationType           = reflectType.Name()
+                                                               relationship              = &Relationship{}
+                                                               toScope                   = scope.New(reflect.New(field.Struct.Type).Interface())
+                                                               toFields                  = toScope.GetStructFields()
+                                                               tagForeignKeys            []string
+                                                               tagAssociationForeignKeys []string
+                                                       )
+
+                                                       if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
+                                                               tagForeignKeys = strings.Split(foreignKey, ",")
+                                                       }
+
+                                                       if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
+                                                               tagAssociationForeignKeys = strings.Split(foreignKey, ",")
+                                                       } else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
+                                                               tagAssociationForeignKeys = strings.Split(foreignKey, ",")
+                                                       }
+
+                                                       if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
+                                                               // Cat has one toy, tag polymorphic is Owner, then associationType is Owner
+                                                               // Toy use OwnerID, OwnerType ('cats') as foreign key
+                                                               if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
+                                                                       associationType = polymorphic
+                                                                       relationship.PolymorphicType = polymorphicType.Name
+                                                                       relationship.PolymorphicDBName = polymorphicType.DBName
+                                                                       // if Cat has several different types of toys set name for each (instead of default 'cats')
+                                                                       if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
+                                                                               relationship.PolymorphicValue = value
+                                                                       } else {
+                                                                               relationship.PolymorphicValue = scope.TableName()
+                                                                       }
+                                                                       polymorphicType.IsForeignKey = true
+                                                               }
+                                                       }
+
+                                                       // Has One
+                                                       {
+                                                               var foreignKeys = tagForeignKeys
+                                                               var associationForeignKeys = tagAssociationForeignKeys
+                                                               // if no foreign keys defined with tag
+                                                               if len(foreignKeys) == 0 {
+                                                                       // if no association foreign keys defined with tag
+                                                                       if len(associationForeignKeys) == 0 {
+                                                                               for _, primaryField := range modelStruct.PrimaryFields {
+                                                                                       foreignKeys = append(foreignKeys, associationType+primaryField.Name)
+                                                                                       associationForeignKeys = append(associationForeignKeys, primaryField.Name)
+                                                                               }
+                                                                       } else {
+                                                                               // generate foreign keys form association foreign keys
+                                                                               for _, associationForeignKey := range tagAssociationForeignKeys {
+                                                                                       if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil {
+                                                                                               foreignKeys = append(foreignKeys, associationType+foreignField.Name)
+                                                                                               associationForeignKeys = append(associationForeignKeys, foreignField.Name)
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               } else {
+                                                                       // generate association foreign keys from foreign keys
+                                                                       if len(associationForeignKeys) == 0 {
+                                                                               for _, foreignKey := range foreignKeys {
+                                                                                       if strings.HasPrefix(foreignKey, associationType) {
+                                                                                               associationForeignKey := strings.TrimPrefix(foreignKey, associationType)
+                                                                                               if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil {
+                                                                                                       associationForeignKeys = append(associationForeignKeys, associationForeignKey)
+                                                                                               }
+                                                                                       }
+                                                                               }
+                                                                               if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
+                                                                                       associationForeignKeys = []string{scope.PrimaryKey()}
+                                                                               }
+                                                                       } else if len(foreignKeys) != len(associationForeignKeys) {
+                                                                               scope.Err(errors.New("invalid foreign keys, should have same length"))
+                                                                               return
+                                                                       }
+                                                               }
+
+                                                               for idx, foreignKey := range foreignKeys {
+                                                                       if foreignField := getForeignField(foreignKey, toFields); foreignField != nil {
+                                                                               if scopeField := getForeignField(associationForeignKeys[idx], modelStruct.StructFields); scopeField != nil {
+                                                                                       foreignField.IsForeignKey = true
+                                                                                       // source foreign keys
+                                                                                       relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, scopeField.Name)
+                                                                                       relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, scopeField.DBName)
+
+                                                                                       // association foreign keys
+                                                                                       relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
+                                                                                       relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+
+                                                       if len(relationship.ForeignFieldNames) != 0 {
+                                                               relationship.Kind = "has_one"
+                                                               field.Relationship = relationship
+                                                       } else {
+                                                               var foreignKeys = tagForeignKeys
+                                                               var associationForeignKeys = tagAssociationForeignKeys
+
+                                                               if len(foreignKeys) == 0 {
+                                                                       // generate foreign keys & association foreign keys
+                                                                       if len(associationForeignKeys) == 0 {
+                                                                               for _, primaryField := range toScope.PrimaryFields() {
+                                                                                       foreignKeys = append(foreignKeys, field.Name+primaryField.Name)
+                                                                                       associationForeignKeys = append(associationForeignKeys, primaryField.Name)
+                                                                               }
+                                                                       } else {
+                                                                               // generate foreign keys with association foreign keys
+                                                                               for _, associationForeignKey := range associationForeignKeys {
+                                                                                       if foreignField := getForeignField(associationForeignKey, toFields); foreignField != nil {
+                                                                                               foreignKeys = append(foreignKeys, field.Name+foreignField.Name)
+                                                                                               associationForeignKeys = append(associationForeignKeys, foreignField.Name)
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               } else {
+                                                                       // generate foreign keys & association foreign keys
+                                                                       if len(associationForeignKeys) == 0 {
+                                                                               for _, foreignKey := range foreignKeys {
+                                                                                       if strings.HasPrefix(foreignKey, field.Name) {
+                                                                                               associationForeignKey := strings.TrimPrefix(foreignKey, field.Name)
+                                                                                               if foreignField := getForeignField(associationForeignKey, toFields); foreignField != nil {
+                                                                                                       associationForeignKeys = append(associationForeignKeys, associationForeignKey)
+                                                                                               }
+                                                                                       }
+                                                                               }
+                                                                               if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
+                                                                                       associationForeignKeys = []string{toScope.PrimaryKey()}
+                                                                               }
+                                                                       } else if len(foreignKeys) != len(associationForeignKeys) {
+                                                                               scope.Err(errors.New("invalid foreign keys, should have same length"))
+                                                                               return
+                                                                       }
+                                                               }
+
+                                                               for idx, foreignKey := range foreignKeys {
+                                                                       if foreignField := getForeignField(foreignKey, modelStruct.StructFields); foreignField != nil {
+                                                                               if associationField := getForeignField(associationForeignKeys[idx], toFields); associationField != nil {
+                                                                                       foreignField.IsForeignKey = true
+
+                                                                                       // association foreign keys
+                                                                                       relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, associationField.Name)
+                                                                                       relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationField.DBName)
+
+                                                                                       // source foreign keys
+                                                                                       relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
+                                                                                       relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
+                                                                               }
+                                                                       }
+                                                               }
+
+                                                               if len(relationship.ForeignFieldNames) != 0 {
+                                                                       relationship.Kind = "belongs_to"
+                                                                       field.Relationship = relationship
+                                                               }
+                                                       }
+                                               }(field)
+                                       default:
+                                               field.IsNormal = true
+                                       }
+                               }
+                       }
+
+                       // Even it is ignored, also possible to decode db value into the field
+                       if value, ok := field.TagSettingsGet("COLUMN"); ok {
+                               field.DBName = value
+                       } else {
+                               field.DBName = ToColumnName(fieldStruct.Name)
+                       }
+
+                       modelStruct.StructFields = append(modelStruct.StructFields, field)
+               }
+       }
+
+       if len(modelStruct.PrimaryFields) == 0 {
+               if field := getForeignField("id", modelStruct.StructFields); field != nil {
+                       field.IsPrimaryKey = true
+                       modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
+               }
+       }
+
+       modelStructsMap.Store(reflectType, &modelStruct)
+
+       return &modelStruct
+}
+
+// GetStructFields get model's field structs
+func (scope *Scope) GetStructFields() (fields []*StructField) {
+       return scope.GetModelStruct().StructFields
+}
+
+func parseTagSetting(tags reflect.StructTag) map[string]string {
+       setting := map[string]string{}
+       for _, str := range []string{tags.Get("sql"), tags.Get("gorm")} {
+               tags := strings.Split(str, ";")
+               for _, value := range tags {
+                       v := strings.Split(value, ":")
+                       k := strings.TrimSpace(strings.ToUpper(v[0]))
+                       if len(v) >= 2 {
+                               setting[k] = strings.Join(v[1:], ":")
+                       } else {
+                               setting[k] = k
+                       }
+               }
+       }
+       return setting
+}