Add more tests for parse schema relations
This commit is contained in:
		
							parent
							
								
									a4a0895a85
								
							
						
					
					
						commit
						3cbd233758
					
				| @ -55,6 +55,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | |||||||
| 		Updatable:   true, | 		Updatable:   true, | ||||||
| 		Tag:         fieldStruct.Tag, | 		Tag:         fieldStruct.Tag, | ||||||
| 		TagSettings: ParseTagSetting(fieldStruct.Tag), | 		TagSettings: ParseTagSetting(fieldStruct.Tag), | ||||||
|  | 		Schema:      schema, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for field.FieldType.Kind() == reflect.Ptr { | 	for field.FieldType.Kind() == reflect.Ptr { | ||||||
| @ -183,6 +184,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | |||||||
| 			schema.err = err | 			schema.err = err | ||||||
| 		} | 		} | ||||||
| 		for _, ef := range field.EmbeddedSchema.Fields { | 		for _, ef := range field.EmbeddedSchema.Fields { | ||||||
|  | 			ef.Schema = schema | ||||||
| 			ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...) | 			ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...) | ||||||
| 
 | 
 | ||||||
| 			if prefix, ok := field.TagSettings["EMBEDDEDPREFIX"]; ok { | 			if prefix, ok := field.TagSettings["EMBEDDEDPREFIX"]; ok { | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ import ( | |||||||
| // Namer namer interface
 | // Namer namer interface
 | ||||||
| type Namer interface { | type Namer interface { | ||||||
| 	TableName(table string) string | 	TableName(table string) string | ||||||
| 	ColumnName(column string) string | 	ColumnName(table, column string) string | ||||||
| 	JoinTableName(table string) string | 	JoinTableName(table string) string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -30,13 +30,13 @@ func (ns NamingStrategy) TableName(str string) string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ColumnName convert string to column name
 | // ColumnName convert string to column name
 | ||||||
| func (ns NamingStrategy) ColumnName(str string) string { | func (ns NamingStrategy) ColumnName(table, str string) string { | ||||||
| 	return toDBName(str) | 	return toDBName(str) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // JoinTableName convert string to join table name
 | // JoinTableName convert string to join table name
 | ||||||
| func (ns NamingStrategy) JoinTableName(str string) string { | func (ns NamingStrategy) JoinTableName(str string) string { | ||||||
| 	return ns.TablePrefix + toDBName(str) | 	return ns.TablePrefix + inflection.Plural(toDBName(str)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/jinzhu/inflection" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // RelationshipType relationship type
 | // RelationshipType relationship type
 | ||||||
| @ -43,10 +45,10 @@ type Polymorphic struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Reference struct { | type Reference struct { | ||||||
| 	PriamryKey    *Field | 	PrimaryKey    *Field | ||||||
| 	PriamryValue  string | 	PrimaryValue  string | ||||||
| 	ForeignKey    *Field | 	ForeignKey    *Field | ||||||
| 	OwnPriamryKey bool | 	OwnPrimaryKey bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (schema *Schema) parseRelation(field *Field) { | func (schema *Schema) parseRelation(field *Field) { | ||||||
| @ -136,7 +138,7 @@ func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Fi | |||||||
| 
 | 
 | ||||||
| 	if schema.err == nil { | 	if schema.err == nil { | ||||||
| 		relation.References = append(relation.References, Reference{ | 		relation.References = append(relation.References, Reference{ | ||||||
| 			PriamryValue: relation.Polymorphic.Value, | 			PrimaryValue: relation.Polymorphic.Value, | ||||||
| 			ForeignKey:   relation.Polymorphic.PolymorphicType, | 			ForeignKey:   relation.Polymorphic.PolymorphicType, | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| @ -147,9 +149,9 @@ func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Fi | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		relation.References = append(relation.References, Reference{ | 		relation.References = append(relation.References, Reference{ | ||||||
| 			PriamryKey:    primaryKeyField, | 			PrimaryKey:    primaryKeyField, | ||||||
| 			ForeignKey:    relation.Polymorphic.PolymorphicType, | 			ForeignKey:    relation.Polymorphic.PolymorphicID, | ||||||
| 			OwnPriamryKey: true, | 			OwnPrimaryKey: true, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -163,17 +165,20 @@ func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Fiel | |||||||
| 		err             error | 		err             error | ||||||
| 		joinTableFields []reflect.StructField | 		joinTableFields []reflect.StructField | ||||||
| 		fieldsMap       = map[string]*Field{} | 		fieldsMap       = map[string]*Field{} | ||||||
|  | 		ownFieldsMap    = map[string]bool{} // fix self join many2many
 | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	for _, s := range []*Schema{schema, relation.Schema} { | 	for _, s := range []*Schema{schema, relation.FieldSchema} { | ||||||
| 		for _, primaryField := range s.PrimaryFields { | 		for _, primaryField := range s.PrimaryFields { | ||||||
| 			fieldName := s.Name + primaryField.Name | 			fieldName := s.Name + primaryField.Name | ||||||
| 			if _, ok := fieldsMap[fieldName]; ok { | 			if _, ok := fieldsMap[fieldName]; ok { | ||||||
| 				if field.Name != s.Name { | 				if field.Name != s.Name { | ||||||
| 					fieldName = field.Name + primaryField.Name | 					fieldName = inflection.Singular(field.Name) + primaryField.Name | ||||||
| 				} else { | 				} else { | ||||||
| 					fieldName = s.Name + primaryField.Name + "Reference" | 					fieldName = s.Name + primaryField.Name + "Reference" | ||||||
| 				} | 				} | ||||||
|  | 			} else { | ||||||
|  | 				ownFieldsMap[fieldName] = true | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			fieldsMap[fieldName] = primaryField | 			fieldsMap[fieldName] = primaryField | ||||||
| @ -195,9 +200,9 @@ func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Fiel | |||||||
| 	// build references
 | 	// build references
 | ||||||
| 	for _, f := range relation.JoinTable.Fields { | 	for _, f := range relation.JoinTable.Fields { | ||||||
| 		relation.References = append(relation.References, Reference{ | 		relation.References = append(relation.References, Reference{ | ||||||
| 			PriamryKey:    fieldsMap[f.Name], | 			PrimaryKey:    fieldsMap[f.Name], | ||||||
| 			ForeignKey:    f, | 			ForeignKey:    f, | ||||||
| 			OwnPriamryKey: schema == fieldsMap[f.Name].Schema, | 			OwnPrimaryKey: schema == fieldsMap[f.Name].Schema && ownFieldsMap[f.Name], | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| @ -275,9 +280,9 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, guessH | |||||||
| 	// build references
 | 	// build references
 | ||||||
| 	for idx, foreignField := range foreignFields { | 	for idx, foreignField := range foreignFields { | ||||||
| 		relation.References = append(relation.References, Reference{ | 		relation.References = append(relation.References, Reference{ | ||||||
| 			PriamryKey:    primaryFields[idx], | 			PrimaryKey:    primaryFields[idx], | ||||||
| 			ForeignKey:    foreignField, | 			ForeignKey:    foreignField, | ||||||
| 			OwnPriamryKey: schema == primarySchema, | 			OwnPrimaryKey: schema == primarySchema && guessHas, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -25,6 +25,9 @@ type Schema struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (schema Schema) String() string { | func (schema Schema) String() string { | ||||||
|  | 	if schema.ModelType.Name() == "" { | ||||||
|  | 		return fmt.Sprintf("%v(%v)", schema.Name, schema.Table) | ||||||
|  | 	} | ||||||
| 	return fmt.Sprintf("%v.%v", schema.ModelType.PkgPath(), schema.ModelType.Name()) | 	return fmt.Sprintf("%v.%v", schema.ModelType.PkgPath(), schema.ModelType.Name()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -86,7 +89,7 @@ func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error) | |||||||
| 
 | 
 | ||||||
| 	for _, field := range schema.Fields { | 	for _, field := range schema.Fields { | ||||||
| 		if field.DBName == "" { | 		if field.DBName == "" { | ||||||
| 			field.DBName = namer.ColumnName(field.Name) | 			field.DBName = namer.ColumnName(schema.Table, field.Name) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if field.DBName != "" { | 		if field.DBName != "" { | ||||||
|  | |||||||
| @ -1,7 +1,9 @@ | |||||||
| package schema_test | package schema_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"reflect" | 	"reflect" | ||||||
|  | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jinzhu/gorm/schema" | 	"github.com/jinzhu/gorm/schema" | ||||||
| @ -90,14 +92,25 @@ func checkSchemaField(t *testing.T, s *schema.Schema, f *schema.Field, fc func(* | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Relation struct { | type Relation struct { | ||||||
| 	Name            string | 	Name        string | ||||||
| 	Type            schema.RelationshipType | 	Type        schema.RelationshipType | ||||||
| 	Polymorphic     schema.Polymorphic | 	Schema      string | ||||||
| 	Schema          string | 	FieldSchema string | ||||||
| 	FieldSchema     string | 	Polymorphic Polymorphic | ||||||
| 	JoinTable       string | 	JoinTable   JoinTable | ||||||
| 	JoinTableFields []schema.Field | 	References  []Reference | ||||||
| 	References      []Reference | } | ||||||
|  | 
 | ||||||
|  | type Polymorphic struct { | ||||||
|  | 	ID    string | ||||||
|  | 	Type  string | ||||||
|  | 	Value string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type JoinTable struct { | ||||||
|  | 	Name   string | ||||||
|  | 	Table  string | ||||||
|  | 	Fields []schema.Field | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Reference struct { | type Reference struct { | ||||||
| @ -105,17 +118,82 @@ type Reference struct { | |||||||
| 	PrimarySchema string | 	PrimarySchema string | ||||||
| 	ForeignKey    string | 	ForeignKey    string | ||||||
| 	ForeignSchema string | 	ForeignSchema string | ||||||
| 	OwnPriamryKey bool | 	PrimaryValue  string | ||||||
|  | 	OwnPrimaryKey bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkSchemaRelation(t *testing.T, s *schema.Schema, relation Relation) { | func checkSchemaRelation(t *testing.T, s *schema.Schema, relation Relation) { | ||||||
| 	if r, ok := s.Relationships.Relations[relation.Name]; ok { | 	if r, ok := s.Relationships.Relations[relation.Name]; ok { | ||||||
| 		if r.Name != relation.Name { | 		if r.Name != relation.Name { | ||||||
| 			t.Errorf("schema %v relation name expects %v, but got %v", s, relation.Name, r.Name) | 			t.Errorf("schema %v relation name expects %v, but got %v", s, r.Name, relation.Name) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if r.Type != relation.Type { | 		if r.Type != relation.Type { | ||||||
| 			t.Errorf("schema %v relation name expects %v, but got %v", s, relation.Type, r.Type) | 			t.Errorf("schema %v relation name expects %v, but got %v", s, r.Type, relation.Type) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if r.Schema.Name != relation.Schema { | ||||||
|  | 			t.Errorf("schema %v relation's schema expects %v, but got %v", s, relation.Schema, r.Schema.Name) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if r.FieldSchema.Name != relation.FieldSchema { | ||||||
|  | 			t.Errorf("schema %v relation's schema expects %v, but got %v", s, relation.Schema, r.Schema.Name) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if r.Polymorphic != nil { | ||||||
|  | 			if r.Polymorphic.PolymorphicID.Name != relation.Polymorphic.ID { | ||||||
|  | 				t.Errorf("schema %v relation's polymorphic id field expects %v, but got %v", s, relation.Polymorphic.ID, r.Polymorphic.PolymorphicID.Name) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if r.Polymorphic.PolymorphicType.Name != relation.Polymorphic.Type { | ||||||
|  | 				t.Errorf("schema %v relation's polymorphic type field expects %v, but got %v", s, relation.Polymorphic.Type, r.Polymorphic.PolymorphicType.Name) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if r.Polymorphic.Value != relation.Polymorphic.Value { | ||||||
|  | 				t.Errorf("schema %v relation's polymorphic value expects %v, but got %v", s, relation.Polymorphic.Value, r.Polymorphic.Value) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if r.JoinTable != nil { | ||||||
|  | 			if r.JoinTable.Name != relation.JoinTable.Name { | ||||||
|  | 				t.Errorf("schema %v relation's join table name expects %v, but got %v", s, relation.JoinTable.Name, r.JoinTable.Name) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if r.JoinTable.Table != relation.JoinTable.Table { | ||||||
|  | 				t.Errorf("schema %v relation's join table tablename expects %v, but got %v", s, relation.JoinTable.Table, r.JoinTable.Table) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			for _, f := range relation.JoinTable.Fields { | ||||||
|  | 				checkSchemaField(t, r.JoinTable, &f, nil) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if len(relation.References) != len(r.References) { | ||||||
|  | 			t.Errorf("schema %v relation's reference's count doesn't match, expects %v, but got %v", s, len(relation.References), len(r.References)) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for _, ref := range relation.References { | ||||||
|  | 			var found bool | ||||||
|  | 			for _, rf := range r.References { | ||||||
|  | 				if (rf.PrimaryKey == nil || (rf.PrimaryKey.Name == ref.PrimaryKey && rf.PrimaryKey.Schema.Name == ref.PrimarySchema)) && (rf.PrimaryValue == ref.PrimaryValue) && (rf.ForeignKey.Name == ref.ForeignKey && rf.ForeignKey.Schema.Name == ref.ForeignSchema) && (rf.OwnPrimaryKey == ref.OwnPrimaryKey) { | ||||||
|  | 					found = true | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if !found { | ||||||
|  | 				var refs []string | ||||||
|  | 				for _, rf := range r.References { | ||||||
|  | 					var primaryKey, primaryKeySchema string | ||||||
|  | 					if rf.PrimaryKey != nil { | ||||||
|  | 						primaryKey, primaryKeySchema = rf.PrimaryKey.Name, rf.PrimaryKey.Schema.Name | ||||||
|  | 					} | ||||||
|  | 					refs = append(refs, fmt.Sprintf( | ||||||
|  | 						"{PrimaryKey: %v PrimaryKeySchame: %v ForeignKey: %v ForeignKeySchema: %v PrimaryValue: %v OwnPrimaryKey: %v}", | ||||||
|  | 						primaryKey, primaryKeySchema, rf.ForeignKey.Name, rf.ForeignKey.Schema.Name, rf.PrimaryValue, rf.OwnPrimaryKey, | ||||||
|  | 					)) | ||||||
|  | 				} | ||||||
|  | 				t.Errorf("schema %v relation %v failed to found reference %+v, has %v", s, relation.Name, ref, strings.Join(refs, ", ")) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		t.Errorf("schema %v failed to find relations by name %v", s, relation.Name) | 		t.Errorf("schema %v failed to find relations by name %v", s, relation.Name) | ||||||
|  | |||||||
| @ -41,8 +41,61 @@ func TestParseSchema(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	// check relations
 | 	// check relations
 | ||||||
| 	relations := []Relation{ | 	relations := []Relation{ | ||||||
| 		{Name: "Pets", Type: schema.HasMany, Schema: "User", FieldSchema: "Pet", References: []Reference{{"ID", "User", "UserID", "Pet", true}}}, | 		{ | ||||||
|  | 			Name: "Account", Type: schema.HasOne, Schema: "User", FieldSchema: "Account", | ||||||
|  | 			References: []Reference{{"ID", "User", "UserID", "Account", "", true}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "Pets", Type: schema.HasMany, Schema: "User", FieldSchema: "Pet", | ||||||
|  | 			References: []Reference{{"ID", "User", "UserID", "Pet", "", true}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "Toys", Type: schema.HasMany, Schema: "User", FieldSchema: "Toy", | ||||||
|  | 			Polymorphic: Polymorphic{ID: "OwnerID", Type: "OwnerType", Value: "users"}, | ||||||
|  | 			References:  []Reference{{"ID", "User", "OwnerID", "Toy", "", true}, {"", "", "OwnerType", "Toy", "users", false}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "Company", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Company", | ||||||
|  | 			References: []Reference{{"ID", "Company", "CompanyID", "User", "", false}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "Manager", Type: schema.BelongsTo, Schema: "User", FieldSchema: "User", | ||||||
|  | 			References: []Reference{{"ID", "User", "ManagerID", "User", "", false}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "Team", Type: schema.HasMany, Schema: "User", FieldSchema: "User", | ||||||
|  | 			References: []Reference{{"ID", "User", "ManagerID", "User", "", true}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "Languages", Type: schema.Many2Many, Schema: "User", FieldSchema: "Language", | ||||||
|  | 			JoinTable: JoinTable{Name: "UserSpeak", Table: "user_speaks", Fields: []schema.Field{ | ||||||
|  | 				{ | ||||||
|  | 					Name: "UserID", DBName: "user_id", BindNames: []string{"UserID"}, DataType: schema.Uint, | ||||||
|  | 					Tag: `gorm:"primarykey"`, Creatable: true, Updatable: true, PrimaryKey: true, | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					Name: "LanguageCode", DBName: "language_code", BindNames: []string{"LanguageCode"}, DataType: schema.String, | ||||||
|  | 					Tag: `gorm:"primarykey"`, Creatable: true, Updatable: true, PrimaryKey: true, | ||||||
|  | 				}, | ||||||
|  | 			}}, | ||||||
|  | 			References: []Reference{{"ID", "User", "UserID", "UserSpeak", "", true}, {"Code", "Language", "LanguageCode", "UserSpeak", "", false}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "Friends", Type: schema.Many2Many, Schema: "User", FieldSchema: "User", | ||||||
|  | 			JoinTable: JoinTable{Name: "user_friends", Table: "user_friends", Fields: []schema.Field{ | ||||||
|  | 				{ | ||||||
|  | 					Name: "UserID", DBName: "user_id", BindNames: []string{"UserID"}, DataType: schema.Uint, | ||||||
|  | 					Tag: `gorm:"primarykey"`, Creatable: true, Updatable: true, PrimaryKey: true, | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					Name: "FriendID", DBName: "friend_id", BindNames: []string{"FriendID"}, DataType: schema.Uint, | ||||||
|  | 					Tag: `gorm:"primarykey"`, Creatable: true, Updatable: true, PrimaryKey: true, | ||||||
|  | 				}, | ||||||
|  | 			}}, | ||||||
|  | 			References: []Reference{{"ID", "User", "UserID", "user_friends", "", true}, {"ID", "User", "FriendID", "user_friends", "", false}}, | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	for _, relation := range relations { | 	for _, relation := range relations { | ||||||
| 		checkSchemaRelation(t, user, relation) | 		checkSchemaRelation(t, user, relation) | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -24,8 +24,8 @@ type User struct { | |||||||
| 	ManagerID uint | 	ManagerID uint | ||||||
| 	Manager   *User | 	Manager   *User | ||||||
| 	Team      []User     `gorm:"foreignkey:ManagerID"` | 	Team      []User     `gorm:"foreignkey:ManagerID"` | ||||||
|  | 	Languages []Language `gorm:"many2many:UserSpeak"` | ||||||
| 	Friends   []*User    `gorm:"many2many:user_friends"` | 	Friends   []*User    `gorm:"many2many:user_friends"` | ||||||
| 	Languages []Language `gorm:"many2many:user_speaks"` |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Account struct { | type Account struct { | ||||||
| @ -53,6 +53,6 @@ type Company struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Language struct { | type Language struct { | ||||||
| 	Code string `gorm:primarykey` | 	Code string `gorm:"primarykey"` | ||||||
| 	Name string | 	Name string | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Jinzhu
						Jinzhu