forked from mirror/gorm
Implement guess relation
This commit is contained in:
parent
eea78f3f30
commit
a9c20291e4
schema
|
@ -25,15 +25,15 @@ type Relationships struct {
|
|||
}
|
||||
|
||||
type Relationship struct {
|
||||
Name string
|
||||
Type RelationshipType
|
||||
Field *Field
|
||||
Polymorphic *Polymorphic
|
||||
References []Reference
|
||||
Schema *Schema
|
||||
FieldSchema *Schema
|
||||
JoinTable *Schema
|
||||
ForeignKeys, AssociationForeignKeys []string
|
||||
Name string
|
||||
Type RelationshipType
|
||||
Field *Field
|
||||
Polymorphic *Polymorphic
|
||||
References []Reference
|
||||
Schema *Schema
|
||||
FieldSchema *Schema
|
||||
JoinTable *Schema
|
||||
ForeignKeys, PrimaryKeys []string
|
||||
}
|
||||
|
||||
type Polymorphic struct {
|
||||
|
@ -53,12 +53,11 @@ func (schema *Schema) parseRelation(field *Field) {
|
|||
var (
|
||||
fieldValue = reflect.New(field.FieldType).Interface()
|
||||
relation = &Relationship{
|
||||
Name: field.Name,
|
||||
Field: field,
|
||||
Schema: schema,
|
||||
Type: RelationshipType(strings.ToLower(strings.TrimSpace(field.TagSettings["REL"]))),
|
||||
ForeignKeys: toColumns(field.TagSettings["FOREIGNKEY"]),
|
||||
AssociationForeignKeys: toColumns(field.TagSettings["ASSOCIATION_FOREIGNKEY"]),
|
||||
Name: field.Name,
|
||||
Field: field,
|
||||
Schema: schema,
|
||||
ForeignKeys: toColumns(field.TagSettings["FOREIGNKEY"]),
|
||||
PrimaryKeys: toColumns(field.TagSettings["PRIMARYKEY"]),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -66,6 +65,8 @@ func (schema *Schema) parseRelation(field *Field) {
|
|||
return
|
||||
}
|
||||
|
||||
// Parse Polymorphic relations
|
||||
//
|
||||
// User has many Toys, its `Polymorphic` is `Owner`, Pet has one Toy, its `Polymorphic` is `Owner`
|
||||
// type User struct {
|
||||
// Toys []Toy `gorm:"polymorphic:Owner;"`
|
||||
|
@ -89,11 +90,11 @@ func (schema *Schema) parseRelation(field *Field) {
|
|||
}
|
||||
|
||||
if relation.Polymorphic.PolymorphicType == nil {
|
||||
schema.err = fmt.Errorf("invalid polymorphic type: %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"Type")
|
||||
schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"Type")
|
||||
}
|
||||
|
||||
if relation.Polymorphic.PolymorphicID == nil {
|
||||
schema.err = fmt.Errorf("invalid polymorphic type: %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"ID")
|
||||
schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"ID")
|
||||
}
|
||||
|
||||
if schema.err == nil {
|
||||
|
@ -105,7 +106,7 @@ func (schema *Schema) parseRelation(field *Field) {
|
|||
primaryKeyField := schema.PrioritizedPrimaryField
|
||||
if len(relation.ForeignKeys) > 0 {
|
||||
if primaryKeyField = schema.LookUpField(relation.ForeignKeys[0]); primaryKeyField == nil || len(relation.ForeignKeys) > 1 {
|
||||
schema.err = fmt.Errorf("invalid polymorphic foreign key: %+v for %v on field %v", relation.ForeignKeys, schema, field.Name)
|
||||
schema.err = fmt.Errorf("invalid polymorphic foreign keys %+v for %v on field %v", relation.ForeignKeys, schema, field.Name)
|
||||
}
|
||||
}
|
||||
relation.References = append(relation.References, Reference{
|
||||
|
@ -115,29 +116,108 @@ func (schema *Schema) parseRelation(field *Field) {
|
|||
})
|
||||
}
|
||||
|
||||
relation.Type = "has"
|
||||
} else {
|
||||
switch field.FieldType.Kind() {
|
||||
case reflect.Struct:
|
||||
schema.guessRelation(relation, field, true)
|
||||
case reflect.Slice:
|
||||
schema.guessRelation(relation, field, true)
|
||||
default:
|
||||
schema.err = fmt.Errorf("unsupported data type %v for %v on field %v", relation.FieldSchema, schema, field.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if relation.Type == "has" {
|
||||
switch field.FieldType.Kind() {
|
||||
case reflect.Struct:
|
||||
relation.Type = HasOne
|
||||
case reflect.Slice:
|
||||
relation.Type = HasMany
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (schema *Schema) guessRelation(relation *Relationship, field *Field, guessHas bool) {
|
||||
var (
|
||||
primaryFields, foreignFields []*Field
|
||||
primarySchema, foreignSchema = schema, relation.FieldSchema
|
||||
)
|
||||
|
||||
if !guessHas {
|
||||
primarySchema, foreignSchema = relation.FieldSchema, schema
|
||||
}
|
||||
|
||||
reguessOrErr := func(err string, args ...interface{}) {
|
||||
if guessHas {
|
||||
schema.guessRelation(relation, field, false)
|
||||
} else {
|
||||
schema.err = fmt.Errorf(err, args...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(relation.ForeignKeys) > 0 {
|
||||
for _, foreignKey := range relation.ForeignKeys {
|
||||
if f := foreignSchema.LookUpField(foreignKey); f != nil {
|
||||
foreignFields = append(foreignFields, f)
|
||||
} else {
|
||||
reguessOrErr("unsupported relations %v for %v on field %v with foreign keys %v", relation.FieldSchema, schema, field.Name, relation.ForeignKeys)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, primaryField := range primarySchema.PrimaryFields {
|
||||
if f := foreignSchema.LookUpField(field.Name + primaryField.Name); f != nil {
|
||||
foreignFields = append(foreignFields, f)
|
||||
primaryFields = append(primaryFields, primaryField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(foreignFields) == 0 {
|
||||
reguessOrErr("failed to guess %v's relations with %v's field %v", relation.FieldSchema, schema, field.Name)
|
||||
return
|
||||
} else if len(relation.PrimaryKeys) > 0 {
|
||||
for idx, primaryKey := range relation.PrimaryKeys {
|
||||
if f := primarySchema.LookUpField(primaryKey); f != nil {
|
||||
if len(primaryFields) < idx+1 {
|
||||
primaryFields = append(primaryFields, f)
|
||||
} else if f != primaryFields[idx] {
|
||||
reguessOrErr("unsupported relations %v for %v on field %v with primary keys %v", relation.FieldSchema, schema, field.Name, relation.PrimaryKeys)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
reguessOrErr("unsupported relations %v for %v on field %v with primary keys %v", relation.FieldSchema, schema, field.Name, relation.PrimaryKeys)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if len(primaryFields) == 0 {
|
||||
if len(foreignFields) == 1 {
|
||||
primaryFields = append(primaryFields, primarySchema.PrioritizedPrimaryField)
|
||||
} else if len(primarySchema.PrimaryFields) == len(foreignFields) {
|
||||
primaryFields = append(primaryFields, primarySchema.PrimaryFields...)
|
||||
} else {
|
||||
reguessOrErr("unsupported relations %v for %v on field %v", relation.FieldSchema, schema, field.Name)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch field.FieldType.Kind() {
|
||||
case reflect.Struct:
|
||||
schema.parseStructRelation(relation, field)
|
||||
case reflect.Slice:
|
||||
schema.parseSliceRelation(relation, field)
|
||||
default:
|
||||
schema.err = fmt.Errorf("unsupported data type: %v (in %v#%v ", field.FieldType.PkgPath(), schema, field.Name)
|
||||
// build references
|
||||
for idx, foreignField := range foreignFields {
|
||||
relation.References = append(relation.References, Reference{
|
||||
PriamryKey: primaryFields[idx],
|
||||
ForeignKey: foreignField,
|
||||
OwnPriamryKey: schema == primarySchema,
|
||||
})
|
||||
}
|
||||
|
||||
if guessHas {
|
||||
relation.Type = "has"
|
||||
} else {
|
||||
relation.Type = "belongs_to"
|
||||
}
|
||||
}
|
||||
|
||||
func (schema *Schema) parseStructRelation(relation *Relationship, field *Field) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (schema *Schema) parseSliceRelation(relation *Relationship, field *Field) error {
|
||||
func (schema *Schema) parseMany2ManyRelation(relation *Relationship, field *Field) error {
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue