Gorm: Tag `foreignkey` doesn't affect db.Modle.Related

Created on 7 Mar 2018  路  9Comments  路  Source: go-gorm/gorm

As the belongs_to Foreign Key document shows, my example code below should be work. But it always notice * invalid association * error.

What's wrong here? Thanks for help!

```golang
package main

import(
"log"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)

type ApplicationGroup struct{
gorm.Model
Name string
}

type Application struct{
gorm.Model
Name string

GroupID int
Group   ApplicationGroup `gorm:"foreignkey:GroupID"`

}

func main() {
db, err := gorm.Open("mysql", "root:root@tcp(localhost:3306)/dbname?charset=utf8")
if err != nil {
log.Fatal(err)
}

app := Application{GroupID: 123}
var group ApplicationGroup
q := db.Model(&app).Related(&group)
log.Println(q.Error) //invalid association []

}
`

Most helpful comment

I ran into this too, I have to specify it manually. @jinzhu shouldn't it use the gorm:"foreignkey" tag value?

All 9 comments

maybe you can try this
q := db.Model(&app).Related(&group, "GroupID")

Yes, this works.

But I think it should get GroupID from the gorm tags, instead of manually specified.

I ran into this too, I have to specify it manually. @jinzhu shouldn't it use the gorm:"foreignkey" tag value?

same problem in version v1.9.14 @jinzhu

as per the source code:

// Related get related associations
func (s *DB) Related(value interface{}, foreignKeys ...string) *DB {
    return s.NewScope(s.Value).related(value, foreignKeys...).db
}
func (scope *Scope) related(value interface{}, foreignKeys ...string) *Scope {
    toScope := scope.db.NewScope(value)
    tx := scope.db.Set("gorm:association:source", scope.Value)

    for _, foreignKey := range append(foreignKeys, toScope.typeName()+"Id", scope.typeName()+"Id") {
        fromField, _ := scope.FieldByName(foreignKey)
        toField, _ := toScope.FieldByName(foreignKey)

        if fromField != nil {
            if relationship := fromField.Relationship; relationship != nil {
                if relationship.Kind == "many_to_many" {
                    joinTableHandler := relationship.JoinTableHandler
                    scope.Err(joinTableHandler.JoinWith(joinTableHandler, tx, scope.Value).Find(value).Error)
                } else if relationship.Kind == "belongs_to" {
                    for idx, foreignKey := range relationship.ForeignDBNames {
                        if field, ok := scope.FieldByName(foreignKey); ok {
                            tx = tx.Where(fmt.Sprintf("%v = ?", scope.Quote(relationship.AssociationForeignDBNames[idx])), field.Field.Interface())
                        }
                    }
                    scope.Err(tx.Find(value).Error)
                } else if relationship.Kind == "has_many" || relationship.Kind == "has_one" {
                    for idx, foreignKey := range relationship.ForeignDBNames {
                        if field, ok := scope.FieldByName(relationship.AssociationForeignDBNames[idx]); ok {
                            tx = tx.Where(fmt.Sprintf("%v = ?", scope.Quote(foreignKey)), field.Field.Interface())
                        }
                    }

                    if relationship.PolymorphicType != "" {
                        tx = tx.Where(fmt.Sprintf("%v = ?", scope.Quote(relationship.PolymorphicDBName)), relationship.PolymorphicValue)
                    }
                    scope.Err(tx.Find(value).Error)
                }
            } else {
                sql := fmt.Sprintf("%v = ?", scope.Quote(toScope.PrimaryKey()))
                scope.Err(tx.Where(sql, fromField.Field.Interface()).Find(value).Error)
            }
            return scope
        } else if toField != nil {
            sql := fmt.Sprintf("%v = ?", scope.Quote(toField.DBName))
            scope.Err(tx.Where(sql, scope.PrimaryKeyValue()).Find(value).Error)
            return scope
        }
    }

    scope.Err(fmt.Errorf("invalid association %v", foreignKeys))
    return scope
}

the code tries to figure out the first possible field name that containing the foreign key in the following order:
the second argument when calling the function Related()
the type name of the owner or owned type, appended by Id

we can see it definitely ignores the tags foreignkey and association_foreignkey

@jinzhu

the official documentation is basically wrong:

To define a belongs to relationship, the foreign key must exists, default foreign key uses owner鈥檚 type name plus its primary key.

For the above example, to define a model that belongs to User, the foreign key should be UserID.

GORM provides a way to customize the foreign key, for example:

type User struct {
  gorm.Model
  Name string
}

type Profile struct {
  gorm.Model
  Name      string
  User      User `gorm:"foreignkey:UserRefer"` // use UserRefer as foreign key
  UserRefer uint
}

in fact, foreignkey has no use here!

Yes, there is an issue, when I design the Related method, I thought it as a method to query associations with user-specified foreign key, the auto guessing foreign key is just sugar.

It should be used like db.Related(&user, "User"), db.Model(&app).Related(&group, "Group")

But I think I am not going to fix this one, as we dropped the Related support in v2, refer:

https://github.com/go-gorm/gorm/wiki/GORM-V2-Release-Note-Draft#association-mode

Yes, there is an issue

you should at least point it out in the documentation. so many people encountered this bug before.

Actually it is mentioned in the document, but maybe not quite clear.
image

It should specified that it won't use the setting from tags to make it more clear

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rfyiamcool picture rfyiamcool  路  3Comments

sredxny picture sredxny  路  3Comments

Ganitzsh picture Ganitzsh  路  3Comments

izouxv picture izouxv  路  3Comments

superwf picture superwf  路  3Comments