4

I'm trying to Preload data from a One to Many relationship, yet I always get an "ApiKeys: unsupported relations for schema Client" error. (The reason structs are pointers is because I'm using gqlgen and that's the default configuration)

type Client struct {
    //  Client ID
    ID int `json:"id"`
    UserName string `json:"userName"`
    //  Client login hashed password
    Password string `json:"password"`
    //  ApiKeys
    APIKeys []*APIKey `json:"apiKeys"`
}
    
type APIKey struct {
    //  ApiKey Index
    ID int `json:"id"`
    //  ApiKey Value
    Key string `json:"key"`
    //  ApiKey Client Relation
    ClientID int `json:"clientID"`
    //  ApiKey Client Info
    Client *Client `json:"client"`
}

And this is the function that calls the Preload of ApiKeys.

func (r *queryResolver) ClientInfoResolver(username string, password string) (*model.Client, error) {
    var clients []*model.Client
    var client *model.Client
    query := r.Resolver.DB
    
    query = query.Where("user_name = ? AND password = ?", username, password).Preload("ApiKeys").Find(&clients)
    
    if query.Error != nil {
        return client, query.Error
    }
    return clients[0], nil
}

I understand by gorm's documentation that the foreign key for the relation is ClientID, despite not being explicit (doesn't work by specifying it either) am I understanding something wrong here?

robbrit
  • 17,560
  • 4
  • 48
  • 68
Helios
  • 43
  • 1
  • 1
  • 5
  • All of the examples in the gorm docs have `[]Foo`, not `[]*Foo`. – hobbs Mar 26 '21 at 03:20
  • I am aware of that, so far has not been a limitation for many to many or one to one relationships, that code is autogenerated by gqlgen. If I have to change it, I'll change it though. – Helios Mar 26 '21 at 03:51
  • use `[]APIKey` instead of `[]*APIKey` – M_x Mar 26 '21 at 07:45

2 Answers2

9

You list APIKeys as the struct field name but try and use ApiKeys as the FK.

.Preload("ApiKeys")
// Should be
.Preload("APIKeys")

Or, if you want to use ApiKeys as the foreign key, use a Gorm struct tag to do this.

Full working example

package main

import (
    "fmt"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

type Client struct {
    //  ApiKey Index
    ID int `json:"id"`
    UserName string `json:"userName"`
    //  Client login hashed password
    Password string `json:"password"`
    //  ApiKeys
    APIKeys []*APIKey `json:"apiKeys"`
}

type APIKey struct {
    //  ApiKey Index
    ID int `json:"id"`
    //  ApiKey Value
    Key string `json:"key"`
    //  ApiKey Client Relation
    ClientID int `json:"clientID"`
    //  ApiKey Client Info
    Client *Client `json:"client"`
}

func main() {

    db, err := gorm.Open(sqlite.Open("many2many.db"), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }

    // Migrate the schema
    err = db.AutoMigrate(&APIKey{}, &Client{})
    if err != nil {
        fmt.Print(err)
    }

    clientOne := Client{
        UserName:          "Client One",
    }
    db.Create(&clientOne)

    apiKeyOne := APIKey{
        Key:"one",
        Client: &clientOne,
    }
    apiKeyTwo := APIKey{
        Key:"two",
        Client: &clientOne,
    }

    db.Create(&apiKeyOne)
    db.Create(&apiKeyTwo)

    // Fetch from DB
    fetchedClient := Client{}

    db.Debug().Preload("APIKeys").Find(&fetchedClient, clientOne.ID)
    fmt.Println(fetchedClient)

    db.Delete(&clientOne)
    db.Delete(&apiKeyOne)
    db.Delete(&apiKeyTwo)
}
Christian
  • 1,676
  • 13
  • 19
  • I can't believe this was mas mistake, even when overriding I was still using a different capitalization for 'API'. Thank you for your time! – Helios Mar 26 '21 at 17:59
  • This little miss caused me so much frustration and problems. I cannot thank you guys enough. – Supreeth Padavala Apr 26 '22 at 20:42
0

Has it been resolved? I also encountered the same problem。
when i trying to Preload data from a One to Many relationship,
report:unsupported relations for schema TeacherInfo

github.com/99designs/gqlgen v0.13.0

gorm.io/gorm v1.21.8

models_gen:

func (TeacherInfo) TableName() string {
return "teacher_info"

}

type TeacherInfo struct {
ID           int             `json:"id" gorm:"primaryKey"`
Name         string          `json:"name"`
Avatar       string          `json:"avatar" `
Info         string          `json:"info" `
Score        float64         `json:"score" `
Role         int             `json:"role" `
TeacherScore []*TeacherScore `json:"teacherScore" gorm:"foreignkey:TID; references:ID"`

}

func (TeacherScore) TableName() string {
return "teacher_score"

}

type TeacherScore struct {
ID      int     `json:"id" `
TID     int     `json:"t_id" `
Comment string  `json:"comment" `
UID     int     `json:"u_id" `
Score   float64 `json:"score" `

}

resolvers:

func (r *queryResolver) TeacherScore(ctx context.Context, id int) (*model.TeacherInfo, error) {
var teacherInfo model.TeacherInfo


wrong: dao.DB.Debug().Preload("teacher_score").First(&teacherInfo)

right:  here is teacherInfo's cloume TeacherScore
dao.DB.Debug().Preload("TeacherScore").First(&teacherInfo)

    return &teacherInfo, nil

}

resolved

liukai
  • 1
  • 1
  • 1
    This does not look like a solution to his problem. Are you trying to get a solution to your issues as well? It may be better you ask it as a separate question. – John David Apr 24 '21 at 13:17