Unable to preload clause.Associations - go

I am using GORM in combination with Fiber. When querying all users with preloaded clause.Associations I get the following error:
can't preload field ###as### for entities.User
What does that mean? Without the preload of clause.Associations it works normally and but it does not show one-to-one associations.
func UsersGetAll(c *fiber.Ctx) error {
db := database.DBConn
users := []entities.User{}
records := db.Preload(clause.Associations).Find(&users)
if records.Error != nil {
log.Println(records.Error)
return c.Status(500).SendString(records.Error.Error())
}
return c.JSON(records.Value)
}

Well looks like a problem with gorm tags:
I will suppose the struct is something like this :
type Address struct {
ID int `gorm:"column:id;primaryKey"`
}
type User struct {
Name string `gorm:"column:name"`
Age string `gorm:"column:age"`
Address Address `gorm:"references:ID"`
}
And the code:
user:=User{}
if err:=db.Preload("Address").Find(&user).Error {
panic(err)
}
I think you also should look at this: Gorm relationship error: need to define a valid foreign key for relations or it need to implement the Valuer/Scanner interface

Related

Foreign Key Constraint in gorm

I want to create a Booking-Class relationship, where every Booking can be assigned only one Class.
type Class struct {
Id int `json:"id"`
Name string `json:"name"`
}
type Booking struct {
Id int `json:"id"`
User string `json:"user"`
Members int `json:"members"`
ClassId int `json:"classid"`
}
I understand that it is similar to gorm's "belongs-to" relationship explained here https://gorm.io/docs/belongs_to.html but I was wondering if it's possible to achieve "foreign key constraint" without defining the field of type Class inside the Booking model (only ClassId int). To make sure that non-existent ClassId-s are used I defined functions:
func Find(slice []int, val int) bool {
for _, item := range slice {
if item == val {
return true
}
}
return false
}
func GetClassKeys(d *gorm.DB) []int {
var keys []int
rows, _ := d.Raw(`SELECT id
FROM classes`).Rows()
defer rows.Close()
for rows.Next() {
var key int
rows.Scan(&key)
keys = append(keys, key)
}
return keys
}
And perform this check before creating/updating a booking:
if !Find(GetClassKeys(db), booking.ClassId) {
log.Println("Wrong class id value")
return
}
But this doesn't handle the case of removed class id (which regular databases do automatically). I was wondering is there a way to achieve a normal database foreign key functionality with gorm by simply referencing the primary key of Class in a User model? Thanks in advance
I don't think the Migrator tool will help you because it assumes you're going to use the default Gorm patterns for defining relationships, and you've explicitly decided NOT to use these.
The only remaining option is to manage the constraint yourself, either through an SQL script or easier, some custom queries you run alongside your AutoMigrate call:
import "gorm.io/gorm/clause"
// somewhere you must be calling
db.AutoMigrate(&Class{}, &Booking{})
// then you'd have:
constraint := "fk_booking_classid"
var count int64
err := db.Raw(
"SELECT count(*) FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_schema = ? AND table_name = ? AND constraint_name = ?",
db.Migrator().CurrentDatabase(),
"bookings",
constraint,
).Scan(&count).Error
// handle error
if (count == 0) {
err := db.Exec(
"ALTER TABLE bookings ADD CONSTRAINT ? FOREIGN KEY class_id REFERENCES classes(id)",
clause.Table{Name: constraint},
).Error
// handle error
}
Of course, this negates the advantage of having an automated migration (which means when things change like the field name, the constraint will be updated without changes to the migration code).
I'd be looking at why you don't want to define the foreign key as gorm expects, as this smells of you trying to use the database model directly as your JSON API Request/Response model, and that usually doesn't lead to a good end :)

How to get all fields in a response in graphql without passing any field names in a query

I'm building a graphql interface using golang. I'm using gqlgen package to implement it.
Here I need to pass all field names in a query to get it in response, But the problem is my data is huge, it is having more than 30 fields it would be difficult to pass all fields in a query.
This is my query
{Model{id, name, email, mobile,...............}}
Like this I need to pass all fields name.
Instead Im looking for a result which will return all fields without passing any fields. I mean if not passing any field names it should return all.
For example
{Model{}}
First, you really should list out all the fields in your query. That is the nature of graphql. It is verbose, but most client libraries get the fields from your data structure anyway, so it's not that bad.
So I recommend listing out all fields manually!
Using Scalars (must be on v0.11.3 or below, see https://github.com/99designs/gqlgen/issues/1293)
But if you insist, if there is a will, there is way. You can use GraphQL's scalar types and make your own. See this doc for how to make them with gqlgen: https://gqlgen.com/reference/scalars/
In your schema, you can make a JSON scalar:
scalar JSON
type Query {
random: JSON!
}
Make a model for this
// in your own models.go
// You can really play with this to make it better, easier to use
type JSONScalar json.RawMessage
// UnmarshalGQL implements the graphql.Unmarshaler interface
func (y *JSONScalar) UnmarshalGQL(v interface{}) error {
data, ok := v.(string)
if !ok {
return fmt.Errorf("Scalar must be a string")
}
*y = []byte(data)
return nil
}
// MarshalGQL implements the graphql.Marshaler interface
func (y JSONScalar) MarshalGQL(w io.Writer) {
_, _ = w.Write(y)
}
Then link the scalar to your custom type in the gql.yml
models:
JSON:
model: github.com/your-project/path/graph/model.JSONScalar
When you run the generate (use gqlgen v0.11.3 or below, gqlgen version), your resolvers will now use the custom type you made. And it's easy to use:
func (r *queryResolver) random(ctx context.Context) (model.JSONScalar, error) {
// something is the data structure you want to return as json
something := struct {
Value string
}{
Value: "Hello World",
}
d, _ := json.Marshal(something)
return model1.JSONScalar(d), nil
}
The resulting query of
// Query
{
random
}
// Response
{
"random" : {
"Value": "Hello World!"
}
}

Multi value list foreign key sql gorm

type Users struct {
ID int64
Email string
Permissions string
}
type UserPermissions struct {
ID int64
Description json.RawMessage
}
The user json should be like this:
{
"status": 200,
"data": {
"id": 1,
"email": "hello#hello.com",
"permisions": [{
"id":"1",
"description":"Create permission"
},
{
"id":"3",
"description":"Edit permission"
}]
}
}
I have the following string in the Permissions of my User:
';1;3;5;7;' every number is the id related to the UserPermissions struct/table
How can I match the string with the user permission table using gorm?.
I'm using mysql
An answer to your question (if you are using PostgreSQL as your DB).
If you want to query for the Permissions that a given User record has, given its ID
perms := []Permission{}
err := db.Joins("JOIN users u ON id::string IN STRING_TO_ARRAY(TRIM(';' FROM u.permissions), ';')").
Where("u.id = ?", userID).
Find(&perms).
Error
if err != nil {
log.Fatal(err) // or something like that
}
As you can see, we are doing a funky join with functions that clean and split the permission field into individual string IDs. This is clunky, brittle and super slow and convoluted, and stems from the faulty relational design that you started with.
EDIT: Nevermind, for MySQL the equivalent solution is very much more complicated, so I suggest you instead use the advice below.
A better way
If you have control over the database design, the better way to do this would be
by not storing an array of IDs as a string, never a good thing in database design, but instead using a foreign key on the many side of the HasMany relationship*.
This would look like this as go structs:
type User struct {
ID int64 `json:"id"`
Email string `json:"email"`
Permissions []UserPermission `json:"permissions"`
}
type UserPermissions struct {
ID int64 `json:"id"`
UserID int64 `json:"-"`
Description json.RawMessage `json:"description"`
}
// now you can use gorm to query the permissions that are related to a user
user := User{}
err := db.Preload("Permissions").First(&user, userID).Error
if err != nil {
log.Fatal(err)
}
// now you can access user.Permissions[i].Description for example.
// or marshal to json
out, err := json.Marshal(user)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(out))
*: I am assuming that the relationship between User and UserPermission is One-to-many. This may very well not be the case, and a Many-to-many relationship actually makes sense (One user can have many permissions, one permission can belong to many users). If this is the case, the concepts are the same and you should be able to modify this solution following the Gorm guide on many to many relationships.

gorm golang one2many same table

I'm trying to create a self-reference in a (my)sql table using golang gorm. At the moment my code looks like this:
type Person struct {
gorm.Model
Name string
Children []*Person `gorm:"ForeignKey:ParentID"`
ParentID uint
}
func main() {
/* code to get database connection omitted */
p := &Person{Name:"Sally"}
db.Create(p)
children := []*Person{ {Name:"Jane", ParentID:p.ID},
{Name:"Tom", ParentID:p.ID}}
for _, child := range children {
db.Create(child)
}
var children2 []*Person
db.Model(p).Related(children2, "ParentID")
}
The code is failing with an error "reflect.Value.Set using unaddressable value".
Does anybody know how to get this relationship working using go gorm?
Many thanks in advance :)
Fortunately gorm have added lately this feature (reference: here).
In your case should be like this:
type Person struct {
gorm.Model
Name string
Children []*Person `gorm:"many2many: children;association_jointable_foreignkey:children_id"`
}

How to populate and embedded array with gorm?

I have 2 structs with data like this:
type User struct {
Pics Pic[]
}
type Pic struct {
Id int
UserId int64
}
Although everytime I insert an User, Each of the pics are inserted on their table everytime I find the users, pics are not populated:
var users []User
db.Limit(pagesize).Where("updated_at > ?", date).Find(&users)
Am I doing something wrong?
Your models (the structs) don't really make sense because User have a Pic array indicates a 'one to many' user to pics relationship however your user has no id property itself and there for cannot be related to items on the Pic table.
User should have a property Id which will be it's primary key and UserId is a foreign key on Pic that relates to it. Without the 'relation' between these two tables/entities there's no way you're going to return pics by querying users.
I'm not sure what all you need to do to make your code work since the example is incomplete but the first thing you need is an Id property which you should designate as a Primarykey with gorm annotations. You also should have annotations on the Pic struct saying UserId is a foreign key and Id is it's primary key.
Also, just fyi your array is not embedded. Embedding is a language feature which you're not using, if you embed the property it has no name and it's properties can be accessed directly from an instance of the embedding type.
I had these issues once. Then I used Join function.
See my example that works just fine:
type FileType struct {
Id int
}
type File struct {
Id int
FileType `xorm:"extends"`
}
file := File{Id: id}
has, err := eng.
Join("INNER", "FileType", "FileType.IdFileType = File.IdFileType").
Get(&file)
You probably know by now. You got to think as if you are creating a SQL table with 1-to-many relationship. Here is an example:
type Entry struct {
ID int
Name string
...
ContainerID int
}
type Container struct {
ID int
Tag int
Name string
...
Entries []Entry `gorm:"foreignkey:ContainerID"`
}
The trick is to populate it. I am yet to find how to make it in one try. For every such dependency, you got to run something like:
c := getContainerFromDB(...)
if err := getROConn().Model(c).Related(&c.Entries, "Entries").Error; err != nil {
return errors.Wrap(err, "error getting container field")
}
Try Preload
db.Limit(pagesize).Where("updated_at > ?", date).Preload("Pics").Find(&users)

Resources