Skip to content

Commit

Permalink
Allow name of column to be customized to support self-referencing man…
Browse files Browse the repository at this point in the history
…y2many fields.
  • Loading branch information
nathan-osman authored and jinzhu committed Feb 10, 2018
1 parent c035922 commit 8e7d807
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 2 deletions.
22 changes: 22 additions & 0 deletions customize_column_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,25 @@ func TestBelongsToWithPartialCustomizedColumn(t *testing.T) {
t.Errorf("should preload discount from coupon")
}
}

type SelfReferencingUser struct {
gorm.Model
Friends []*SelfReferencingUser `gorm:"many2many:UserFriends;AssociationForeignKey:ID=friend_id"`
}

func TestSelfReferencingMany2ManyColumn(t *testing.T) {
DB.DropTable(&SelfReferencingUser{}, "UserFriends")
DB.AutoMigrate(&SelfReferencingUser{})

friend := SelfReferencingUser{}
if err := DB.Create(&friend).Error; err != nil {
t.Errorf("no error should happen, but got %v", err)
}

user := SelfReferencingUser{
Friends: []*SelfReferencingUser{&friend},
}
if err := DB.Create(&user).Error; err != nil {
t.Errorf("no error should happen, but got %v", err)
}
}
19 changes: 18 additions & 1 deletion join_table_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,24 @@ func (s JoinTableHandler) getSearchMap(db *DB, sources ...interface{}) map[strin
// Add create relationship in join table for source and destination
func (s JoinTableHandler) Add(handler JoinTableHandlerInterface, db *DB, source interface{}, destination interface{}) error {
scope := db.NewScope("")
searchMap := s.getSearchMap(db, source, destination)
searchMap := map[string]interface{}{}

// getSearchMap() cannot be used here since the source and destination
// model types may be identical

sourceScope := db.NewScope(source)
for _, foreignKey := range s.Source.ForeignKeys {
if field, ok := sourceScope.FieldByName(foreignKey.AssociationDBName); ok {
searchMap[foreignKey.DBName] = field.Field.Interface()
}
}

destinationScope := db.NewScope(destination)
for _, foreignKey := range s.Destination.ForeignKeys {
if field, ok := destinationScope.FieldByName(foreignKey.AssociationDBName); ok {
searchMap[foreignKey.DBName] = field.Field.Interface()
}
}

var assignColumns, binVars, conditions []string
var values []interface{}
Expand Down
15 changes: 14 additions & 1 deletion model_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,24 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
}

for _, name := range associationForeignKeys {

// In order to allow self-referencing many2many tables, the name
// may be followed by "=" to allow renaming the column
parts := strings.Split(name, "=")
name = parts[0]

if field, ok := toScope.FieldByName(name); ok {
// association foreign keys (db names)
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, field.DBName)

// If a new name was provided for the field, use it
name = field.DBName
if len(parts) > 1 {
name = parts[1]
}

// join table foreign keys for association
joinTableDBName := ToDBName(elemType.Name()) + "_" + field.DBName
joinTableDBName := ToDBName(elemType.Name()) + "_" + name
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
}
}
Expand Down

0 comments on commit 8e7d807

Please sign in to comment.