12 "github.com/jinzhu/inflection"
15 // DefaultTableNameHandler default table name handler
16 var DefaultTableNameHandler = func(db *DB, defaultTableName string) string {
17 return defaultTableName
20 var modelStructsMap sync.Map
22 // ModelStruct model definition
23 type ModelStruct struct {
24 PrimaryFields []*StructField
25 StructFields []*StructField
26 ModelType reflect.Type
28 defaultTableName string
32 // TableName returns model's table name
33 func (s *ModelStruct) TableName(db *DB) string {
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()
42 tableName := ToTableName(s.ModelType.Name())
43 if db == nil || !db.parent.singularTable {
44 tableName = inflection.Plural(tableName)
46 s.defaultTableName = tableName
50 return DefaultTableNameHandler(db, s.defaultTableName)
53 // StructField model field's struct definition
54 type StructField struct {
64 TagSettings map[string]string
65 Struct reflect.StructField
67 Relationship *Relationship
69 tagSettingsLock sync.RWMutex
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
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]
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)
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,
110 if structField.Relationship != nil {
111 relationship := *structField.Relationship
112 clone.Relationship = &relationship
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
125 // Relationship described the relationship between models
126 type Relationship struct {
128 PolymorphicType string
129 PolymorphicDBName string
130 PolymorphicValue string
131 ForeignFieldNames []string
132 ForeignDBNames []string
133 AssociationForeignFieldNames []string
134 AssociationForeignDBNames []string
135 JoinTableHandler JoinTableHandlerInterface
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) {
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 {
155 reflectType := reflect.ValueOf(scope.Value).Type()
156 for reflectType.Kind() == reflect.Slice || reflectType.Kind() == reflect.Ptr {
157 reflectType = reflectType.Elem()
160 // Scope value need to be a struct
161 if reflectType.Kind() != reflect.Struct {
165 // Get Cached model struct
166 if value, ok := modelStructsMap.Load(reflectType); ok && value != nil {
167 return value.(*ModelStruct)
170 modelStruct.ModelType = reflectType
173 for i := 0; i < reflectType.NumField(); i++ {
174 if fieldStruct := reflectType.Field(i); ast.IsExported(fieldStruct.Name) {
175 field := &StructField{
177 Name: fieldStruct.Name,
178 Names: []string{fieldStruct.Name},
179 Tag: fieldStruct.Tag,
180 TagSettings: parseTagSetting(fieldStruct.Tag),
184 if _, ok := field.TagSettingsGet("-"); ok {
185 field.IsIgnored = true
187 if _, ok := field.TagSettingsGet("PRIMARY_KEY"); ok {
188 field.IsPrimaryKey = true
189 modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
192 if _, ok := field.TagSettingsGet("DEFAULT"); ok {
193 field.HasDefaultValue = true
196 if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok && !field.IsPrimaryKey {
197 field.HasDefaultValue = true
200 indirectType := fieldStruct.Type
201 for indirectType.Kind() == reflect.Ptr {
202 indirectType = indirectType.Elem()
205 fieldValue := reflect.New(indirectType).Interface()
206 if _, isScanner := fieldValue.(sql.Scanner); isScanner {
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)
218 } else if _, isTime := fieldValue.(*time.Time); isTime {
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
230 if subField.IsPrimaryKey {
231 if _, ok := subField.TagSettingsGet("PRIMARY_KEY"); ok {
232 modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, subField)
234 subField.IsPrimaryKey = false
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
246 modelStruct.StructFields = append(modelStruct.StructFields, subField)
250 // build relationships
251 switch indirectType.Kind() {
253 defer func(field *StructField) {
255 relationship = &Relationship{}
256 toScope = scope.New(reflect.New(field.Struct.Type).Interface())
258 associationForeignKeys []string
259 elemType = field.Struct.Type
262 if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
263 foreignKeys = strings.Split(foreignKey, ",")
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, ",")
272 for elemType.Kind() == reflect.Slice || elemType.Kind() == reflect.Ptr {
273 elemType = elemType.Elem()
276 if elemType.Kind() == reflect.Struct {
277 if many2many, _ := field.TagSettingsGet("MANY2MANY"); many2many != "" {
278 relationship.Kind = "many_to_many"
280 { // Foreign Keys for Source
281 joinTableDBNames := []string{}
283 if foreignKey, _ := field.TagSettingsGet("JOINTABLE_FOREIGNKEY"); foreignKey != "" {
284 joinTableDBNames = strings.Split(foreignKey, ",")
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)
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)
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])
304 defaultJointableForeignKey := ToColumnName(reflectType.Name()) + "_" + foreignField.DBName
305 relationship.ForeignDBNames = append(relationship.ForeignDBNames, defaultJointableForeignKey)
311 { // Foreign Keys for Association (Destination)
312 associationJoinTableDBNames := []string{}
314 if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_JOINTABLE_FOREIGNKEY"); foreignKey != "" {
315 associationJoinTableDBNames = strings.Split(foreignKey, ",")
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)
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)
330 // setup join table foreign keys for association
331 if len(associationJoinTableDBNames) > idx {
332 relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationJoinTableDBNames[idx])
334 // join table foreign keys for association
335 joinTableDBName := ToColumnName(elemType.Name()) + "_" + field.DBName
336 relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
342 joinTableHandler := JoinTableHandler{}
343 joinTableHandler.Setup(relationship, ToTableName(many2many), reflectType, elemType)
344 relationship.JoinTableHandler = &joinTableHandler
345 field.Relationship = relationship
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"
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
363 relationship.PolymorphicValue = scope.TableName()
365 polymorphicType.IsForeignKey = true
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)
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)
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)
397 if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
398 associationForeignKeys = []string{scope.PrimaryKey()}
400 } else if len(foreignKeys) != len(associationForeignKeys) {
401 scope.Err(errors.New("invalid foreign keys, should have same length"))
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)
414 // association foreign keys
415 relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
416 relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
421 if len(relationship.ForeignFieldNames) != 0 {
422 field.Relationship = relationship
426 field.IsNormal = true
430 defer func(field *StructField) {
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
442 if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
443 tagForeignKeys = strings.Split(foreignKey, ",")
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, ",")
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
463 relationship.PolymorphicValue = scope.TableName()
465 polymorphicType.IsForeignKey = true
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)
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)
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)
501 if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
502 associationForeignKeys = []string{scope.PrimaryKey()}
504 } else if len(foreignKeys) != len(associationForeignKeys) {
505 scope.Err(errors.New("invalid foreign keys, should have same length"))
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)
518 // association foreign keys
519 relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
520 relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
526 if len(relationship.ForeignFieldNames) != 0 {
527 relationship.Kind = "has_one"
528 field.Relationship = relationship
530 var foreignKeys = tagForeignKeys
531 var associationForeignKeys = tagAssociationForeignKeys
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)
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)
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)
560 if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 {
561 associationForeignKeys = []string{toScope.PrimaryKey()}
563 } else if len(foreignKeys) != len(associationForeignKeys) {
564 scope.Err(errors.New("invalid foreign keys, should have same length"))
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
574 // association foreign keys
575 relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, associationField.Name)
576 relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationField.DBName)
578 // source foreign keys
579 relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
580 relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
585 if len(relationship.ForeignFieldNames) != 0 {
586 relationship.Kind = "belongs_to"
587 field.Relationship = relationship
592 field.IsNormal = true
597 // Even it is ignored, also possible to decode db value into the field
598 if value, ok := field.TagSettingsGet("COLUMN"); ok {
601 field.DBName = ToColumnName(fieldStruct.Name)
604 modelStruct.StructFields = append(modelStruct.StructFields, field)
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)
615 modelStructsMap.Store(reflectType, &modelStruct)
620 // GetStructFields get model's field structs
621 func (scope *Scope) GetStructFields() (fields []*StructField) {
622 return scope.GetModelStruct().StructFields
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]))
633 setting[k] = strings.Join(v[1:], ":")