OSDN Git Service

feat: init cross_tx keepers (#146)
[bytom/vapor.git] / vendor / github.com / jinzhu / gorm / model_struct.go
1 package gorm
2
3 import (
4         "database/sql"
5         "errors"
6         "go/ast"
7         "reflect"
8         "strings"
9         "sync"
10         "time"
11
12         "github.com/jinzhu/inflection"
13 )
14
15 // DefaultTableNameHandler default table name handler
16 var DefaultTableNameHandler = func(db *DB, defaultTableName string) string {
17         return defaultTableName
18 }
19
20 var modelStructsMap sync.Map
21
22 // ModelStruct model definition
23 type ModelStruct struct {
24         PrimaryFields    []*StructField
25         StructFields     []*StructField
26         ModelType        reflect.Type
27
28         defaultTableName string
29         l sync.Mutex
30 }
31
32 // TableName returns model's table name
33 func (s *ModelStruct) TableName(db *DB) string {
34         s.l.Lock()
35         defer s.l.Unlock()
36
37         if s.defaultTableName == "" && db != nil && s.ModelType != nil {
38                 // Set default table name
39                 if tabler, ok := reflect.New(s.ModelType).Interface().(tabler); ok {
40                         s.defaultTableName = tabler.TableName()
41                 } else {
42                         tableName := ToTableName(s.ModelType.Name())
43                         if db == nil || !db.parent.singularTable {
44                                 tableName = inflection.Plural(tableName)
45                         }
46                         s.defaultTableName = tableName
47                 }
48         }
49
50         return DefaultTableNameHandler(db, s.defaultTableName)
51 }
52
53 // StructField model field's struct definition
54 type StructField struct {
55         DBName          string
56         Name            string
57         Names           []string
58         IsPrimaryKey    bool
59         IsNormal        bool
60         IsIgnored       bool
61         IsScanner       bool
62         HasDefaultValue bool
63         Tag             reflect.StructTag
64         TagSettings     map[string]string
65         Struct          reflect.StructField
66         IsForeignKey    bool
67         Relationship    *Relationship
68
69         tagSettingsLock sync.RWMutex
70 }
71
72 // TagSettingsSet Sets a tag in the tag settings map
73 func (s *StructField) TagSettingsSet(key, val string) {
74         s.tagSettingsLock.Lock()
75         defer s.tagSettingsLock.Unlock()
76         s.TagSettings[key] = val
77 }
78
79 // TagSettingsGet returns a tag from the tag settings
80 func (s *StructField) TagSettingsGet(key string) (string, bool) {
81         s.tagSettingsLock.RLock()
82         defer s.tagSettingsLock.RUnlock()
83         val, ok := s.TagSettings[key]
84         return val, ok
85 }
86
87 // TagSettingsDelete deletes a tag
88 func (s *StructField) TagSettingsDelete(key string) {
89         s.tagSettingsLock.Lock()
90         defer s.tagSettingsLock.Unlock()
91         delete(s.TagSettings, key)
92 }
93
94 func (structField *StructField) clone() *StructField {
95         clone := &StructField{
96                 DBName:          structField.DBName,
97                 Name:            structField.Name,
98                 Names:           structField.Names,
99                 IsPrimaryKey:    structField.IsPrimaryKey,
100                 IsNormal:        structField.IsNormal,
101                 IsIgnored:       structField.IsIgnored,
102                 IsScanner:       structField.IsScanner,
103                 HasDefaultValue: structField.HasDefaultValue,
104                 Tag:             structField.Tag,
105                 TagSettings:     map[string]string{},
106                 Struct:          structField.Struct,
107                 IsForeignKey:    structField.IsForeignKey,
108         }
109
110         if structField.Relationship != nil {
111                 relationship := *structField.Relationship
112                 clone.Relationship = &relationship
113         }
114
115         // copy the struct field tagSettings, they should be read-locked while they are copied
116         structField.tagSettingsLock.Lock()
117         defer structField.tagSettingsLock.Unlock()
118         for key, value := range structField.TagSettings {
119                 clone.TagSettings[key] = value
120         }
121
122         return clone
123 }
124
125 // Relationship described the relationship between models
126 type Relationship struct {
127         Kind                         string
128         PolymorphicType              string
129         PolymorphicDBName            string
130         PolymorphicValue             string
131         ForeignFieldNames            []string
132         ForeignDBNames               []string
133         AssociationForeignFieldNames []string
134         AssociationForeignDBNames    []string
135         JoinTableHandler             JoinTableHandlerInterface
136 }
137
138 func getForeignField(column string, fields []*StructField) *StructField {
139         for _, field := range fields {
140                 if field.Name == column || field.DBName == column || field.DBName == ToColumnName(column) {
141                         return field
142                 }
143         }
144         return nil
145 }
146
147 // GetModelStruct get value's model struct, relationships based on struct and tag definition
148 func (scope *Scope) GetModelStruct() *ModelStruct {
149         var modelStruct ModelStruct
150         // Scope value can't be nil
151         if scope.Value == nil {
152                 return &modelStruct
153         }
154
155         reflectType := reflect.ValueOf(scope.Value).Type()
156         for reflectType.Kind() == reflect.Slice || reflectType.Kind() == reflect.Ptr {
157                 reflectType = reflectType.Elem()
158         }
159
160         // Scope value need to be a struct
161         if reflectType.Kind() != reflect.Struct {
162                 return &modelStruct
163         }
164
165         // Get Cached model struct
166         if value, ok := modelStructsMap.Load(reflectType); ok && value != nil {
167                 return value.(*ModelStruct)
168         }
169
170         modelStruct.ModelType = reflectType
171
172         // Get all fields
173         for i := 0; i < reflectType.NumField(); i++ {
174                 if fieldStruct := reflectType.Field(i); ast.IsExported(fieldStruct.Name) {
175                         field := &StructField{
176                                 Struct:      fieldStruct,
177                                 Name:        fieldStruct.Name,
178                                 Names:       []string{fieldStruct.Name},
179                                 Tag:         fieldStruct.Tag,
180                                 TagSettings: parseTagSetting(fieldStruct.Tag),
181                         }
182
183                         // is ignored field
184                         if _, ok := field.TagSettingsGet("-"); ok {
185                                 field.IsIgnored = true
186                         } else {
187                                 if _, ok := field.TagSettingsGet("PRIMARY_KEY"); ok {
188                                         field.IsPrimaryKey = true
189                                         modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
190                                 }
191
192                                 if _, ok := field.TagSettingsGet("DEFAULT"); ok {
193                                         field.HasDefaultValue = true
194                                 }
195
196                                 if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok && !field.IsPrimaryKey {
197                                         field.HasDefaultValue = true
198                                 }
199
200                                 indirectType := fieldStruct.Type
201                                 for indirectType.Kind() == reflect.Ptr {
202                                         indirectType = indirectType.Elem()
203                                 }
204
205                                 fieldValue := reflect.New(indirectType).Interface()
206                                 if _, isScanner := fieldValue.(sql.Scanner); isScanner {
207                                         // is scanner
208                                         field.IsScanner, field.IsNormal = true, true
209                                         if indirectType.Kind() == reflect.Struct {
210                                                 for i := 0; i < indirectType.NumField(); i++ {
211                                                         for key, value := range parseTagSetting(indirectType.Field(i).Tag) {
212                                                                 if _, ok := field.TagSettingsGet(key); !ok {
213                                                                         field.TagSettingsSet(key, value)
214                                                                 }
215                                                         }
216                                                 }
217                                         }
218                                 } else if _, isTime := fieldValue.(*time.Time); isTime {
219                                         // is time
220                                         field.IsNormal = true
221                                 } else if _, ok := field.TagSettingsGet("EMBEDDED"); ok || fieldStruct.Anonymous {
222                                         // is embedded struct
223                                         for _, subField := range scope.New(fieldValue).GetModelStruct().StructFields {
224                                                 subField = subField.clone()
225                                                 subField.Names = append([]string{fieldStruct.Name}, subField.Names...)
226                                                 if prefix, ok := field.TagSettingsGet("EMBEDDED_PREFIX"); ok {
227                                                         subField.DBName = prefix + subField.DBName
228                                                 }
229
230                                                 if subField.IsPrimaryKey {
231                                                         if _, ok := subField.TagSettingsGet("PRIMARY_KEY"); ok {
232                                                                 modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, subField)
233                                                         } else {
234                                                                 subField.IsPrimaryKey = false
235                                                         }
236                                                 }
237
238                                                 if subField.Relationship != nil && subField.Relationship.JoinTableHandler != nil {
239                                                         if joinTableHandler, ok := subField.Relationship.JoinTableHandler.(*JoinTableHandler); ok {
240                                                                 newJoinTableHandler := &JoinTableHandler{}
241                                                                 newJoinTableHandler.Setup(subField.Relationship, joinTableHandler.TableName, reflectType, joinTableHandler.Destination.ModelType)
242                                                                 subField.Relationship.JoinTableHandler = newJoinTableHandler
243                                                         }
244                                                 }
245
246                                                 modelStruct.StructFields = append(modelStruct.StructFields, subField)
247                                         }
248                                         continue
249                                 } else {
250                                         // build relationships
251                                         switch indirectType.Kind() {
252                                         case reflect.Slice:
253                                                 defer func(field *StructField) {
254                                                         var (
255                                                                 relationship           = &Relationship{}
256                                                                 toScope                = scope.New(reflect.New(field.Struct.Type).Interface())
257                                                                 foreignKeys            []string
258                                                                 associationForeignKeys []string
259                                                                 elemType               = field.Struct.Type
260                                                         )
261
262                                                         if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
263                                                                 foreignKeys = strings.Split(foreignKey, ",")
264                                                         }
265
266                                                         if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
267                                                                 associationForeignKeys = strings.Split(foreignKey, ",")
268                                                         } else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
269                                                                 associationForeignKeys = strings.Split(foreignKey, ",")
270                                                         }
271
272                                                         for elemType.Kind() == reflect.Slice || elemType.Kind() == reflect.Ptr {
273                                                                 elemType = elemType.Elem()
274                                                         }
275
276                                                         if elemType.Kind() == reflect.Struct {
277                                                                 if many2many, _ := field.TagSettingsGet("MANY2MANY"); many2many != "" {
278                                                                         relationship.Kind = "many_to_many"
279
280                                                                         { // Foreign Keys for Source
281                                                                                 joinTableDBNames := []string{}
282
283                                                                                 if foreignKey, _ := field.TagSettingsGet("JOINTABLE_FOREIGNKEY"); foreignKey != "" {
284                                                                                         joinTableDBNames = strings.Split(foreignKey, ",")
285                                                                                 }
286
287                                                                                 // if no foreign keys defined with tag
288                                                                                 if len(foreignKeys) == 0 {
289                                                                                         for _, field := range modelStruct.PrimaryFields {
290                                                                                                 foreignKeys = append(foreignKeys, field.DBName)
291                                                                                         }
292                                                                                 }
293
294                                                                                 for idx, foreignKey := range foreignKeys {
295                                                                                         if foreignField := getForeignField(foreignKey, modelStruct.StructFields); foreignField != nil {
296                                                                                                 // source foreign keys (db names)
297                                                                                                 relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.DBName)
298
299                                                                                                 // setup join table foreign keys for source
300                                                                                                 if len(joinTableDBNames) > idx {
301                                                                                                         // if defined join table's foreign key
302                                                                                                         relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBNames[idx])
303                                                                                                 } else {
304                                                                                                         defaultJointableForeignKey := ToColumnName(reflectType.Name()) + "_" + foreignField.DBName
305                                                                                                         relationship.ForeignDBNames = append(relationship.ForeignDBNames, defaultJointableForeignKey)
306                                                                                                 }
307                                                                                         }
308                                                                                 }
309                                                                         }
310
311                                                                         { // Foreign Keys for Association (Destination)
312                                                                                 associationJoinTableDBNames := []string{}
313
314                                                                                 if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_JOINTABLE_FOREIGNKEY"); foreignKey != "" {
315                                                                                         associationJoinTableDBNames = strings.Split(foreignKey, ",")
316                                                                                 }
317
318                                                                                 // if no association foreign keys defined with tag
319                                                                                 if len(associationForeignKeys) == 0 {
320                                                                                         for _, field := range toScope.PrimaryFields() {
321                                                                                                 associationForeignKeys = append(associationForeignKeys, field.DBName)
322                                                                                         }
323                                                                                 }
324
325                                                                                 for idx, name := range associationForeignKeys {
326                                                                                         if field, ok := toScope.FieldByName(name); ok {
327                                                                                                 // association foreign keys (db names)
328                                                                                                 relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, field.DBName)
329
330                                                                                                 // setup join table foreign keys for association
331                                                                                                 if len(associationJoinTableDBNames) > idx {
332                                                                                                         relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationJoinTableDBNames[idx])
333                                                                                                 } else {
334                                                                                                         // join table foreign keys for association
335                                                                                                         joinTableDBName := ToColumnName(elemType.Name()) + "_" + field.DBName
336                                                                                                         relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
337                                                                                                 }
338                                                                                         }
339                                                                                 }
340                                                                         }
341
342                                                                         joinTableHandler := JoinTableHandler{}
343                                                                         joinTableHandler.Setup(relationship, ToTableName(many2many), reflectType, elemType)
344                                                                         relationship.JoinTableHandler = &joinTableHandler
345                                                                         field.Relationship = relationship
346                                                                 } else {
347                                                                         // User has many comments, associationType is User, comment use UserID as foreign key
348                                                                         var associationType = reflectType.Name()
349                                                                         var toFields = toScope.GetStructFields()
350                                                                         relationship.Kind = "has_many"
351
352                                                                         if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
353                                                                                 // Dog has many toys, tag polymorphic is Owner, then associationType is Owner
354                                                                                 // Toy use OwnerID, OwnerType ('dogs') as foreign key
355                                                                                 if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
356                                                                                         associationType = polymorphic
357                                                                                         relationship.PolymorphicType = polymorphicType.Name
358                                                                                         relationship.PolymorphicDBName = polymorphicType.DBName
359                                                                                         // if Dog has multiple set of toys set name of the set (instead of default 'dogs')
360                                                                                         if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
361                                                                                                 relationship.PolymorphicValue = value
362                                                                                         } else {
363                                                                                                 relationship.PolymorphicValue = scope.TableName()
364                                                                                         }
365                                                                                         polymorphicType.IsForeignKey = true
366                                                                                 }
367                                                                         }
368
369                                                                         // if no foreign keys defined with tag
370                                                                         if len(foreignKeys) == 0 {
371                                                                                 // if no association foreign keys defined with tag
372                                                                                 if len(associationForeignKeys) == 0 {
373                                                                                         for _, field := range modelStruct.PrimaryFields {
374                                                                                                 foreignKeys = append(foreignKeys, associationType+field.Name)
375                                                                                                 associationForeignKeys = append(associationForeignKeys, field.Name)
376                                                                                         }
377                                                                                 } else {
378                                                                                         // generate foreign keys from defined association foreign keys
379                                                                                         for _, scopeFieldName := range associationForeignKeys {
380                                                                                                 if foreignField := getForeignField(scopeFieldName, modelStruct.StructFields); foreignField != nil {
381                                                                                                         foreignKeys = append(foreignKeys, associationType+foreignField.Name)
382                                                                                                         associationForeignKeys = append(associationForeignKeys, foreignField.Name)
383                                                                                                 }
384                                                                                         }
385                                                                                 }
386                                                                         } else {
387                                                                                 // generate association foreign keys from foreign keys
388                                                                                 if len(associationForeignKeys) == 0 {
389                                                                                         for _, foreignKey := range foreignKeys {
390                                                                                                 if strings.HasPrefix(foreignKey, associationType) {
391                                                                                                         associationForeignKey := strings.TrimPrefix(foreignKey, associationType)
392                                                                                                         if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil {
393                                                                                                                 associationForeignKeys = append(associationForeignKeys, associationForeignKey)
394                                                                                                         }
395                                                                                                 }
396                                                                                         }
397                                                                                         if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
398                                                                                                 associationForeignKeys = []string{scope.PrimaryKey()}
399                                                                                         }
400                                                                                 } else if len(foreignKeys) != len(associationForeignKeys) {
401                                                                                         scope.Err(errors.New("invalid foreign keys, should have same length"))
402                                                                                         return
403                                                                                 }
404                                                                         }
405
406                                                                         for idx, foreignKey := range foreignKeys {
407                                                                                 if foreignField := getForeignField(foreignKey, toFields); foreignField != nil {
408                                                                                         if associationField := getForeignField(associationForeignKeys[idx], modelStruct.StructFields); associationField != nil {
409                                                                                                 // source foreign keys
410                                                                                                 foreignField.IsForeignKey = true
411                                                                                                 relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, associationField.Name)
412                                                                                                 relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationField.DBName)
413
414                                                                                                 // association foreign keys
415                                                                                                 relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
416                                                                                                 relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
417                                                                                         }
418                                                                                 }
419                                                                         }
420
421                                                                         if len(relationship.ForeignFieldNames) != 0 {
422                                                                                 field.Relationship = relationship
423                                                                         }
424                                                                 }
425                                                         } else {
426                                                                 field.IsNormal = true
427                                                         }
428                                                 }(field)
429                                         case reflect.Struct:
430                                                 defer func(field *StructField) {
431                                                         var (
432                                                                 // user has one profile, associationType is User, profile use UserID as foreign key
433                                                                 // user belongs to profile, associationType is Profile, user use ProfileID as foreign key
434                                                                 associationType           = reflectType.Name()
435                                                                 relationship              = &Relationship{}
436                                                                 toScope                   = scope.New(reflect.New(field.Struct.Type).Interface())
437                                                                 toFields                  = toScope.GetStructFields()
438                                                                 tagForeignKeys            []string
439                                                                 tagAssociationForeignKeys []string
440                                                         )
441
442                                                         if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
443                                                                 tagForeignKeys = strings.Split(foreignKey, ",")
444                                                         }
445
446                                                         if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
447                                                                 tagAssociationForeignKeys = strings.Split(foreignKey, ",")
448                                                         } else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
449                                                                 tagAssociationForeignKeys = strings.Split(foreignKey, ",")
450                                                         }
451
452                                                         if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
453                                                                 // Cat has one toy, tag polymorphic is Owner, then associationType is Owner
454                                                                 // Toy use OwnerID, OwnerType ('cats') as foreign key
455                                                                 if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
456                                                                         associationType = polymorphic
457                                                                         relationship.PolymorphicType = polymorphicType.Name
458                                                                         relationship.PolymorphicDBName = polymorphicType.DBName
459                                                                         // if Cat has several different types of toys set name for each (instead of default 'cats')
460                                                                         if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
461                                                                                 relationship.PolymorphicValue = value
462                                                                         } else {
463                                                                                 relationship.PolymorphicValue = scope.TableName()
464                                                                         }
465                                                                         polymorphicType.IsForeignKey = true
466                                                                 }
467                                                         }
468
469                                                         // Has One
470                                                         {
471                                                                 var foreignKeys = tagForeignKeys
472                                                                 var associationForeignKeys = tagAssociationForeignKeys
473                                                                 // if no foreign keys defined with tag
474                                                                 if len(foreignKeys) == 0 {
475                                                                         // if no association foreign keys defined with tag
476                                                                         if len(associationForeignKeys) == 0 {
477                                                                                 for _, primaryField := range modelStruct.PrimaryFields {
478                                                                                         foreignKeys = append(foreignKeys, associationType+primaryField.Name)
479                                                                                         associationForeignKeys = append(associationForeignKeys, primaryField.Name)
480                                                                                 }
481                                                                         } else {
482                                                                                 // generate foreign keys form association foreign keys
483                                                                                 for _, associationForeignKey := range tagAssociationForeignKeys {
484                                                                                         if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil {
485                                                                                                 foreignKeys = append(foreignKeys, associationType+foreignField.Name)
486                                                                                                 associationForeignKeys = append(associationForeignKeys, foreignField.Name)
487                                                                                         }
488                                                                                 }
489                                                                         }
490                                                                 } else {
491                                                                         // generate association foreign keys from foreign keys
492                                                                         if len(associationForeignKeys) == 0 {
493                                                                                 for _, foreignKey := range foreignKeys {
494                                                                                         if strings.HasPrefix(foreignKey, associationType) {
495                                                                                                 associationForeignKey := strings.TrimPrefix(foreignKey, associationType)
496                                                                                                 if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil {
497                                                                                                         associationForeignKeys = append(associationForeignKeys, associationForeignKey)
498                                                                                                 }
499                                                                                         }
500                                                                                 }
501                                                                                 if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
502                                                                                         associationForeignKeys = []string{scope.PrimaryKey()}
503                                                                                 }
504                                                                         } else if len(foreignKeys) != len(associationForeignKeys) {
505                                                                                 scope.Err(errors.New("invalid foreign keys, should have same length"))
506                                                                                 return
507                                                                         }
508                                                                 }
509
510                                                                 for idx, foreignKey := range foreignKeys {
511                                                                         if foreignField := getForeignField(foreignKey, toFields); foreignField != nil {
512                                                                                 if scopeField := getForeignField(associationForeignKeys[idx], modelStruct.StructFields); scopeField != nil {
513                                                                                         foreignField.IsForeignKey = true
514                                                                                         // source foreign keys
515                                                                                         relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, scopeField.Name)
516                                                                                         relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, scopeField.DBName)
517
518                                                                                         // association foreign keys
519                                                                                         relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
520                                                                                         relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
521                                                                                 }
522                                                                         }
523                                                                 }
524                                                         }
525
526                                                         if len(relationship.ForeignFieldNames) != 0 {
527                                                                 relationship.Kind = "has_one"
528                                                                 field.Relationship = relationship
529                                                         } else {
530                                                                 var foreignKeys = tagForeignKeys
531                                                                 var associationForeignKeys = tagAssociationForeignKeys
532
533                                                                 if len(foreignKeys) == 0 {
534                                                                         // generate foreign keys & association foreign keys
535                                                                         if len(associationForeignKeys) == 0 {
536                                                                                 for _, primaryField := range toScope.PrimaryFields() {
537                                                                                         foreignKeys = append(foreignKeys, field.Name+primaryField.Name)
538                                                                                         associationForeignKeys = append(associationForeignKeys, primaryField.Name)
539                                                                                 }
540                                                                         } else {
541                                                                                 // generate foreign keys with association foreign keys
542                                                                                 for _, associationForeignKey := range associationForeignKeys {
543                                                                                         if foreignField := getForeignField(associationForeignKey, toFields); foreignField != nil {
544                                                                                                 foreignKeys = append(foreignKeys, field.Name+foreignField.Name)
545                                                                                                 associationForeignKeys = append(associationForeignKeys, foreignField.Name)
546                                                                                         }
547                                                                                 }
548                                                                         }
549                                                                 } else {
550                                                                         // generate foreign keys & association foreign keys
551                                                                         if len(associationForeignKeys) == 0 {
552                                                                                 for _, foreignKey := range foreignKeys {
553                                                                                         if strings.HasPrefix(foreignKey, field.Name) {
554                                                                                                 associationForeignKey := strings.TrimPrefix(foreignKey, field.Name)
555                                                                                                 if foreignField := getForeignField(associationForeignKey, toFields); foreignField != nil {
556                                                                                                         associationForeignKeys = append(associationForeignKeys, associationForeignKey)
557                                                                                                 }
558                                                                                         }
559                                                                                 }
560                                                                                 if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
561                                                                                         associationForeignKeys = []string{toScope.PrimaryKey()}
562                                                                                 }
563                                                                         } else if len(foreignKeys) != len(associationForeignKeys) {
564                                                                                 scope.Err(errors.New("invalid foreign keys, should have same length"))
565                                                                                 return
566                                                                         }
567                                                                 }
568
569                                                                 for idx, foreignKey := range foreignKeys {
570                                                                         if foreignField := getForeignField(foreignKey, modelStruct.StructFields); foreignField != nil {
571                                                                                 if associationField := getForeignField(associationForeignKeys[idx], toFields); associationField != nil {
572                                                                                         foreignField.IsForeignKey = true
573
574                                                                                         // association foreign keys
575                                                                                         relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, associationField.Name)
576                                                                                         relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationField.DBName)
577
578                                                                                         // source foreign keys
579                                                                                         relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
580                                                                                         relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
581                                                                                 }
582                                                                         }
583                                                                 }
584
585                                                                 if len(relationship.ForeignFieldNames) != 0 {
586                                                                         relationship.Kind = "belongs_to"
587                                                                         field.Relationship = relationship
588                                                                 }
589                                                         }
590                                                 }(field)
591                                         default:
592                                                 field.IsNormal = true
593                                         }
594                                 }
595                         }
596
597                         // Even it is ignored, also possible to decode db value into the field
598                         if value, ok := field.TagSettingsGet("COLUMN"); ok {
599                                 field.DBName = value
600                         } else {
601                                 field.DBName = ToColumnName(fieldStruct.Name)
602                         }
603
604                         modelStruct.StructFields = append(modelStruct.StructFields, field)
605                 }
606         }
607
608         if len(modelStruct.PrimaryFields) == 0 {
609                 if field := getForeignField("id", modelStruct.StructFields); field != nil {
610                         field.IsPrimaryKey = true
611                         modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
612                 }
613         }
614
615         modelStructsMap.Store(reflectType, &modelStruct)
616
617         return &modelStruct
618 }
619
620 // GetStructFields get model's field structs
621 func (scope *Scope) GetStructFields() (fields []*StructField) {
622         return scope.GetModelStruct().StructFields
623 }
624
625 func parseTagSetting(tags reflect.StructTag) map[string]string {
626         setting := map[string]string{}
627         for _, str := range []string{tags.Get("sql"), tags.Get("gorm")} {
628                 tags := strings.Split(str, ";")
629                 for _, value := range tags {
630                         v := strings.Split(value, ":")
631                         k := strings.TrimSpace(strings.ToUpper(v[0]))
632                         if len(v) >= 2 {
633                                 setting[k] = strings.Join(v[1:], ":")
634                         } else {
635                                 setting[k] = k
636                         }
637                 }
638         }
639         return setting
640 }