multpile foreign keys

This commit is contained in:
Jinzhu 2015-07-30 14:26:48 +08:00
parent 82d726bbfd
commit a29230c86f
6 changed files with 127 additions and 86 deletions

View File

@ -35,9 +35,11 @@ func Create(scope *Scope) {
} }
} }
} else if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" { } else if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" {
if relationField := fields[relationship.ForeignDBName]; !scope.changeableField(relationField) { for _, dbName := range relationship.ForeignDBNames {
columns = append(columns, scope.Quote(relationField.DBName)) if relationField := fields[dbName]; !scope.changeableField(relationField) {
sqls = append(sqls, scope.AddToVars(relationField.Field.Interface())) columns = append(columns, scope.Quote(relationField.DBName))
sqls = append(sqls, scope.AddToVars(relationField.Field.Interface()))
}
} }
} }
} }

View File

@ -19,8 +19,13 @@ func SaveBeforeAssociations(scope *Scope) {
if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" { if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" {
value := field.Field value := field.Field
scope.Err(scope.NewDB().Save(value.Addr().Interface()).Error) scope.Err(scope.NewDB().Save(value.Addr().Interface()).Error)
if relationship.ForeignFieldName != "" { if len(relationship.ForeignFieldNames) != 0 {
scope.Err(scope.SetColumn(relationship.ForeignFieldName, scope.New(value.Addr().Interface()).PrimaryKeyValue())) for idx, fieldName := range relationship.ForeignFieldNames {
associationForeignName := relationship.AssociationForeignDBNames[idx]
if f, ok := scope.New(value.Addr().Interface()).FieldByName(associationForeignName); ok {
scope.Err(scope.SetColumn(fieldName, f.Field.Interface()))
}
}
} }
} }
} }
@ -44,8 +49,13 @@ func SaveAfterAssociations(scope *Scope) {
elem := value.Index(i).Addr().Interface() elem := value.Index(i).Addr().Interface()
newScope := newDB.NewScope(elem) newScope := newDB.NewScope(elem)
if relationship.JoinTableHandler == nil && relationship.ForeignFieldName != "" { if relationship.JoinTableHandler == nil && len(relationship.ForeignFieldNames) != 0 {
scope.Err(newScope.SetColumn(relationship.ForeignFieldName, scope.PrimaryKeyValue())) for idx, fieldName := range relationship.ForeignFieldNames {
associationForeignName := relationship.AssociationForeignDBNames[idx]
if f, ok := scope.New(value.Addr().Interface()).FieldByName(associationForeignName); ok {
scope.Err(scope.SetColumn(fieldName, f.Field.Interface()))
}
}
} }
if relationship.PolymorphicType != "" { if relationship.PolymorphicType != "" {
@ -61,8 +71,13 @@ func SaveAfterAssociations(scope *Scope) {
default: default:
elem := value.Addr().Interface() elem := value.Addr().Interface()
newScope := scope.New(elem) newScope := scope.New(elem)
if relationship.ForeignFieldName != "" { if len(relationship.ForeignFieldNames) != 0 {
scope.Err(newScope.SetColumn(relationship.ForeignFieldName, scope.PrimaryKeyValue())) for idx, fieldName := range relationship.ForeignFieldNames {
associationForeignName := relationship.AssociationForeignDBNames[idx]
if f, ok := scope.New(value.Addr().Interface()).FieldByName(associationForeignName); ok {
scope.Err(scope.SetColumn(fieldName, f.Field.Interface()))
}
}
} }
if relationship.PolymorphicType != "" { if relationship.PolymorphicType != "" {

View File

@ -55,9 +55,10 @@ func Update(scope *Scope) {
sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface()))) sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface())))
} }
} else if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" { } else if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" {
if relationField := fields[relationship.ForeignDBName]; !scope.changeableField(relationField) { for _, dbName := range relationship.ForeignDBNames {
if !relationField.IsBlank { if relationField := fields[dbName]; !scope.changeableField(relationField) && !relationField.IsBlank {
sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(relationField.DBName), scope.AddToVars(relationField.Field.Interface()))) sql := fmt.Sprintf("%v = %v", scope.Quote(relationField.DBName), scope.AddToVars(relationField.Field.Interface()))
sqls = append(sqls, sql)
} }
} }
} }

View File

@ -45,41 +45,18 @@ func (s *JoinTableHandler) Setup(relationship *Relationship, tableName string, s
s.TableName = tableName s.TableName = tableName
s.Source = JoinTableSource{ModelType: source} s.Source = JoinTableSource{ModelType: source}
sourceScope := &Scope{Value: reflect.New(source).Interface()} for idx, dbName := range relationship.ForeignFieldNames {
sourcePrimaryFields := sourceScope.GetModelStruct().PrimaryFields
for _, primaryField := range sourcePrimaryFields {
if relationship.ForeignDBName == "" {
relationship.ForeignFieldName = source.Name() + primaryField.Name
relationship.ForeignDBName = ToDBName(relationship.ForeignFieldName)
}
var dbName string
if len(sourcePrimaryFields) == 1 || primaryField.DBName == "id" {
dbName = relationship.ForeignDBName
} else {
dbName = ToDBName(source.Name() + primaryField.Name)
}
s.Source.ForeignKeys = append(s.Source.ForeignKeys, JoinTableForeignKey{ s.Source.ForeignKeys = append(s.Source.ForeignKeys, JoinTableForeignKey{
DBName: dbName, DBName: relationship.ForeignDBNames[idx],
AssociationDBName: primaryField.DBName, AssociationDBName: dbName,
}) })
} }
s.Destination = JoinTableSource{ModelType: destination} s.Destination = JoinTableSource{ModelType: destination}
destinationScope := &Scope{Value: reflect.New(destination).Interface()} for idx, dbName := range relationship.AssociationForeignFieldNames {
destinationPrimaryFields := destinationScope.GetModelStruct().PrimaryFields
for _, primaryField := range destinationPrimaryFields {
var dbName string
if len(sourcePrimaryFields) == 1 || primaryField.DBName == "id" {
dbName = relationship.AssociationForeignDBName
} else {
dbName = ToDBName(destinationScope.GetModelStruct().ModelType.Name() + primaryField.Name)
}
s.Destination.ForeignKeys = append(s.Destination.ForeignKeys, JoinTableForeignKey{ s.Destination.ForeignKeys = append(s.Destination.ForeignKeys, JoinTableForeignKey{
DBName: dbName, DBName: relationship.AssociationForeignDBNames[idx],
AssociationDBName: primaryField.DBName, AssociationDBName: dbName,
}) })
} }
} }

View File

@ -445,7 +445,7 @@ func (s *DB) Association(column string) *Association {
err = errors.New("primary key can't be nil") err = errors.New("primary key can't be nil")
} else { } else {
if field, ok := scope.FieldByName(column); ok { if field, ok := scope.FieldByName(column); ok {
if field.Relationship == nil || field.Relationship.ForeignFieldName == "" { if field.Relationship == nil || len(field.Relationship.ForeignFieldNames) == 0 {
err = fmt.Errorf("invalid association %v for %v", column, scope.IndirectValue().Type()) err = fmt.Errorf("invalid association %v for %v", column, scope.IndirectValue().Type())
} else { } else {
return &Association{Scope: scope, Column: column, PrimaryKey: primaryField.Field.Interface(), Field: field} return &Association{Scope: scope, Column: column, PrimaryKey: primaryField.Field.Interface(), Field: field}

View File

@ -61,14 +61,14 @@ func (structField *StructField) clone() *StructField {
} }
type Relationship struct { type Relationship struct {
Kind string Kind string
PolymorphicType string PolymorphicType string
PolymorphicDBName string PolymorphicDBName string
ForeignFieldName string ForeignFieldNames []string
ForeignDBName string ForeignDBNames []string
AssociationForeignFieldName string AssociationForeignFieldNames []string
AssociationForeignDBName string AssociationForeignDBNames []string
JoinTableHandler JoinTableHandlerInterface JoinTableHandler JoinTableHandlerInterface
} }
var pluralMapKeys = []*regexp.Regexp{regexp.MustCompile("ch$"), regexp.MustCompile("ss$"), regexp.MustCompile("sh$"), regexp.MustCompile("day$"), regexp.MustCompile("y$"), regexp.MustCompile("x$"), regexp.MustCompile("([^s])s?$")} var pluralMapKeys = []*regexp.Regexp{regexp.MustCompile("ch$"), regexp.MustCompile("ss$"), regexp.MustCompile("sh$"), regexp.MustCompile("day$"), regexp.MustCompile("y$"), regexp.MustCompile("x$"), regexp.MustCompile("([^s])s?$")}
@ -190,12 +190,11 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
var relationship = &Relationship{} var relationship = &Relationship{}
foreignKey := gormSettings["FOREIGNKEY"]
if polymorphic := gormSettings["POLYMORPHIC"]; polymorphic != "" { if polymorphic := gormSettings["POLYMORPHIC"]; polymorphic != "" {
if polymorphicField := getForeignField(polymorphic+"Id", toScope.GetStructFields()); polymorphicField != nil { if polymorphicField := getForeignField(polymorphic+"Id", toScope.GetStructFields()); polymorphicField != nil {
if polymorphicType := getForeignField(polymorphic+"Type", toScope.GetStructFields()); polymorphicType != nil { if polymorphicType := getForeignField(polymorphic+"Type", toScope.GetStructFields()); polymorphicType != nil {
relationship.ForeignFieldName = polymorphicField.Name relationship.ForeignFieldNames = []string{polymorphicField.Name}
relationship.ForeignDBName = polymorphicField.DBName relationship.ForeignDBNames = []string{polymorphicField.DBName}
relationship.PolymorphicType = polymorphicType.Name relationship.PolymorphicType = polymorphicType.Name
relationship.PolymorphicDBName = polymorphicType.DBName relationship.PolymorphicDBName = polymorphicType.DBName
polymorphicType.IsForeignKey = true polymorphicType.IsForeignKey = true
@ -204,6 +203,10 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
} }
} }
var foreignKeys []string
if foreignKey, ok := gormSettings["FOREIGNKEY"]; ok {
foreignKeys := append(foreignKeys, gormSettings["FOREIGNKEY"])
}
switch indirectType.Kind() { switch indirectType.Kind() {
case reflect.Slice: case reflect.Slice:
elemType := indirectType.Elem() elemType := indirectType.Elem()
@ -212,34 +215,63 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
} }
if elemType.Kind() == reflect.Struct { if elemType.Kind() == reflect.Struct {
if foreignKey == "" {
foreignKey = scopeType.Name() + "Id"
}
if many2many := gormSettings["MANY2MANY"]; many2many != "" { if many2many := gormSettings["MANY2MANY"]; many2many != "" {
relationship.Kind = "many_to_many" relationship.Kind = "many_to_many"
associationForeignKey := gormSettings["ASSOCIATIONFOREIGNKEY"]
if associationForeignKey == "" { // foreign keys
associationForeignKey = elemType.Name() + "Id" if len(foreignKeys) == 0 {
for _, field := range scope.PrimaryFields() {
foreignKeys = append(foreignKeys, field.DBName)
}
} }
relationship.ForeignFieldName = foreignKey for _, foreignKey := range foreignKeys {
relationship.ForeignDBName = ToDBName(foreignKey) if field, ok := scope.FieldByName(foreignKey); ok {
relationship.AssociationForeignFieldName = associationForeignKey relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, field.DBName)
relationship.AssociationForeignDBName = ToDBName(associationForeignKey) joinTableDBName := ToDBName(scopeType.Name()) + "_" + field.DBName
relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBName)
}
}
// association foreign keys
var associationForeignKeys []string
if foreignKey := gormSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
associationForeignKeys = []string{gormSettings["ASSOCIATIONFOREIGNKEY"]}
} else {
for _, field := range toScope.PrimaryFields() {
associationForeignKeys = append(associationForeignKeys, field.DBName)
}
}
for _, name := range associationForeignKeys {
if field, ok := toScope.FieldByName(name); ok {
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, name)
joinTableDBName := ToDBName(elemType.Name()) + "_" + field.DBName
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
}
}
joinTableHandler := JoinTableHandler{} joinTableHandler := JoinTableHandler{}
joinTableHandler.Setup(relationship, many2many, scopeType, elemType) joinTableHandler.Setup(relationship, many2many, scopeType, elemType)
relationship.JoinTableHandler = &joinTableHandler relationship.JoinTableHandler = &joinTableHandler
field.Relationship = relationship field.Relationship = relationship
} else { } else {
if len(foreignKeys) == 0 {
for _, field := range scope.PrimaryFields() {
foreignKeys = append(foreignKeys, scopeType.Name()+field.Name)
}
}
relationship.Kind = "has_many" relationship.Kind = "has_many"
if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil { for _, foreignKey := range foreignKeys {
relationship.ForeignFieldName = foreignField.Name if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil {
relationship.ForeignDBName = foreignField.DBName relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
foreignField.IsForeignKey = true relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
field.Relationship = relationship foreignField.IsForeignKey = true
} else if relationship.ForeignFieldName != "" { }
}
if len(relationship.ForeignFieldNames) != 0 {
field.Relationship = relationship field.Relationship = relationship
} }
} }
@ -258,28 +290,42 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
} }
continue continue
} else { } else {
belongsToForeignKey := foreignKey belongsToForeignKeys := foreignKeys
if belongsToForeignKey == "" { if len(belongsToForeignKeys) == 0 {
belongsToForeignKey = field.Name + "Id" for _, field := range toScope.PrimaryFields() {
belongsToForeignKeys = append(belongsToForeignKeys, field.Name+field.Name)
}
} }
if foreignField := getForeignField(belongsToForeignKey, fields); foreignField != nil { for _, foreignKey := range belongsToForeignKeys {
if foreignField := getForeignField(foreignKey, fields); foreignField != nil {
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
foreignField.IsForeignKey = true
}
}
if len(relationship.ForeignFieldNames) != 0 {
relationship.Kind = "belongs_to" relationship.Kind = "belongs_to"
relationship.ForeignFieldName = foreignField.Name
relationship.ForeignDBName = foreignField.DBName
foreignField.IsForeignKey = true
field.Relationship = relationship field.Relationship = relationship
} else { } else {
if foreignKey == "" { hasOneForeignKeys := foreignKeys
foreignKey = modelStruct.ModelType.Name() + "Id" if len(hasOneForeignKeys) == 0 {
for _, field := range toScope.PrimaryFields() {
hasOneForeignKeys = append(hasOneForeignKeys, modelStruct.ModelType.Name()+field.Name)
}
} }
relationship.Kind = "has_one"
if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil { for _, foreignKey := range hasOneForeignKeys {
relationship.ForeignFieldName = foreignField.Name if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil {
relationship.ForeignDBName = foreignField.DBName relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
foreignField.IsForeignKey = true relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
field.Relationship = relationship foreignField.IsForeignKey = true
} else if relationship.ForeignFieldName != "" { }
}
if len(relationship.ForeignFieldNames) != 0 {
relationship.Kind = "has_one"
field.Relationship = relationship field.Relationship = relationship
} }
} }