mirror of https://github.com/go-gorm/gorm.git
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…
Reference in New Issue