Gorm preload gives ambiguous column error - go

I have following structs
type Employee struct {
EmployeeID int64 `gorm:"primary_key;column:employee_id"`
EmployeeCode string `gorm:"column:employee_code"`
FirstName string `gorm:"column:first_name"`
LastName string `gorm:"column:last_name"`
DesignationID int64 `gorm:"column:designation_id;"`
Designation *Designation `gorm:"foreignkey:DesignationID"`
}
type Designation struct {
DesignationID int64 `gorm:"primary_key;column:designation_id"`
DesignationName string `gorm:"column:designation_name"`
}
func GetEmployee(id int64) (*Employee, error) {
db := connection.GetConn() //get connection
defer db.Close()
employee := &Employee{}
err := db.Model(employees).Preload("Designation", func(db *gorm.DB) *gorm.DB {
return db.Join("INNER JOIN employees ON employees.designation_id = designations.id").Order("employees.first_name DESC")
}).Find(employee).Error
return employee, err
}
The query that gets generated is
SELECT * FROM employees;
SELECT * FROM designations INNER JOIN employees ON employees.designation_id = designations.id WHERE id IN (1,2) order by employees.first_name DESC;
I have a join which is similar to what I have written in above function. Here I am not able to justfiy the join but in actual case I know that join is required and the preloading table and the join table both have id field in them.
Since both tables have same column name i.e. id in that case MYSQL throws error saying
Error 1052: Column 'id' in where clause is ambiguous
I am facing this issue in gorm v1.9.16
I have not found any resource where similar issue is mentioned.
How do we solve this issue?

Could you try with a recent version (> v1.20.x)?
The issue has been closed https://github.com/go-gorm/gorm/issues/2653
The issue seems to be fixed in the new version https://github.com/go-gorm/gorm/commit/2ae0653af2bc19cd31f687e797b189c85f0ac3f6

Related

Gorm joined tables selects only few field

I have some of this code
type User struct {
Id uint `gorm:"column:id"`
Name string `gorm:"column:name"`
WalletId uint `gorm:"column:wallet_id"`
Wallet Wallet `gorm:"foreignkey:club_id"`
}
type Wallet struct {
Id uint `gorm:"column:id"`
Money uint `gorm:"column:money"`
Valute string `gorm:"column:valute"`
}
I can query users and their wallets like this
var users []users
db.Joins("Wallet").Find(&users)
this will generate stmt like this
SELECT user.id, user.name, user.wallet_id, wallet.id, wallet.Money, wallet.Valute from user join wallet on user.wallet_id = wallet.id
I need to query all users and only `Valute' field from their wallets
I want to use a gorm like
var users []users
db.Joins("Wallet", db.Select("Valute")).Find(&users)
for generate this stmt
SELECT user.id, user.name, user.wallet_id, wallet.Valute from user join wallet on user.wallet_id = wallet.id
For choose a wallet field. But it doesn't work at all. Although there is such a use in the function annotation
// Joins specify Joins conditions
// db.Joins("Account").Find(&user)
// db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu#example.org").Find(&user)
// db.Joins("Account", DB.Select("id").Where("user_id = users.id AND name = ?", "someName").Model(&Account{}))
func (db *DB) Joins(query string, args ...interface{}) (tx *DB) {
In eager loading by Preload i can choose fields like
db.Limit(1000).Preload("Wallet",
func(db *gorm.DB) *gorm.DB {
return db.Select("valute")
},
).Find(&users)
But this isnt good usage for me, it has a bad perfomance when querying one object. I like to know how can i do the same with Joins
PS: I dont want to use a lightweigh model for wallet which queries only valute, i want to use single model for db presentation
I also want Gorm to support this feature but the best solution I came up with is Smart Select but it requires us to manually write query and select fields
type CustomUser struct {
Id uint `gorm:"column:id"`
Name string `gorm:"column:name"`
Valute string `gorm:"column:valute"`
}
var users []CustomUser
db.Raw("SELECT users.id, users.name, Wallet.valute FROM `users` LEFT JOIN `wallets` `Wallet` ON `users`.`wallet_id` = `Wallet`.`id`").Find(&users)

Gorm get all data from table with condition on nested table

I have a table with golang struct like this:
Order {
ID
TransactionID
Transaction
}
Transaction {
ID
ProfileID
Profile
}
Profile {
ID
AccountID
Account
}
How to get all the order with condition of account id with gorm?
I have tried this:
var orders []*Order
res := r.db.
Joins("Transaction").
Preload("Transaction.Profile").
Where("Transaction.Profile.account_id = 1").
Find(&orders)
But it does not work.
This solution should work based on the structs definitions you provided. First, let me show the code and then I'll go through each step:
package main
import (
"fmt"
_ "github.com/lib/pq"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type Order struct {
Id int
TransactionId int
Transaction Transaction
}
type Transaction struct {
Id int
ProfileId int
Profile Profile
}
type Profile struct {
Id int
AccountId int
Account Account
}
type Account struct {
Id int
}
func main() {
dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&Account{})
db.AutoMigrate(&Profile{})
db.AutoMigrate(&Transaction{})
db.AutoMigrate(&Order{})
db.Create(&Account{})
db.Create(&Profile{AccountId: 1})
db.Create(&Transaction{ProfileId: 1})
db.Create(&Order{TransactionId: 1})
// order + transaction + profile + account
var order Order
db.Debug().Preload("Transaction.Profile.Account").Joins("inner join transactions t on orders.transaction_id = t.id").Joins("inner join profiles p on p.id = t.profile_id").Joins("inner join accounts a on p.account_id = a.id").First(&order, "a.id = ?", 1)
fmt.Println(order)
}
Let's take a closer look at the code.
Structs definitions
Nothing changed here. Be sure to know the GORM conventions when you declare the structs as GORM will create relations, foreign keys, and constraints based on this.
Preparing the database
Here, you can find the connection to Postgres, the auto-migrating commands to synchronize the tables, and the insert of some dummy data.
The query
Here, we used a lot of methods provided by the GORM package for Go. Let's recap them in a short list:
Debug: it prints to the console the Raw SQL query. It's useful when dealing with complex queries
Preload: loads the related entities but it doesn't include them in the final query produced by Gorm
Joins: it specifies which tables have to be referenced in a JOIN clause. With the Joins we add the clause to the query.
First: it's used both for fetching only one record and also for specifying some filter such as in our case (e.g. a.id = ?).
Let me know if this clarifies your issue, thanks!

Am trying to create a database with gorm by taking the users inpute using and api like structure e.g c.BodyParser(new(User))

var (
err error
User []AcctDetails
)
type (
AcctDetails struct {
gorm.Model
ID uint
AcctName string gorm:"default:John Doe"
AcctNumber string
UsersPhoneNo string
}
)
func CreateDb(c fiber.Ctx) error {
db := database.DB
c.BodyParser(new(User))
// user := User
db.Create(&User{ID: AcctDetails.ID, AcctName: AcctDetails.AcctName, AcctNumber: AcctDetails.AcctNumber, UsersPhoneNo: AcctDetails.UsersPhoneNo})
return c.JSON(User)
}
You can try to use db.AutoMigrate(&AcctDetails{}).
This will create your table at the db.
db.Create will create a record and assign a value to the fields specified.

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 :)

Gorm with golang: Preload does not work as expected

I have following structs
type Employee struct {
EmployeeID int64 `gorm:"primary_key;column:employee_id"`
EmployeeCode string `gorm:"column:employee_code"`
FirstName string `gorm:"column:first_name"`
LastName string `gorm:"column:last_name"`
DesignationID int64 `gorm:"column:designation_id;"`
Designation *Designation
}
type Designation struct {
DesignationID int64 `gorm:"primary_key;column:designation_id"`
DesignationName string `gorm:"column:designation_name"`
}
func GetEmployee(id int64) (*Employee, error) {
db := connection.GetConn() //get connection
defer db.Close()
employee := &Employee{}
err := db.Model(employee).Preload("Designation").Find(employee).Error
return employee, err
}
In the tables I have following records:
employee :
employee_id | employee_code | first_name | last_name | designation_id
1 | EMP1 | Raj | Mane | 1
designation:
designation_id | designation_name
1 | Engineer
The employee.designation_id is marked as foreign key referring to designation table
When I call the function GetEmployee, it returns error saying can't preload field Designation for model.Employee
I have referred many questions related to Preload but none of the solution has worked. I think only difference between the other working cases is the primary id column name. Can anyone suggest what is happening and what am I missing here?
In GORM default foreign key uses owner’s type name plus its primary key.
GORM provides a way to customize the foreign key, for example:
type Employee struct {
EmployeeID int64 `gorm:"primary_key;column:employee_id"`
EmployeeCode string `gorm:"column:employee_code"`
FirstName string `gorm:"column:first_name"`
LastName string `gorm:"column:last_name"`
DesignationID int64 `gorm:"column:designation_id;"`
Designation *Designation `gorm:"foreignkey:DesignationID"`
}
Just simply change your variable name from DesignationID to DesignationDesignationID. It will work. As per document ForignKey must be TABLE_NAME + PRIMARY_KEY_NAME so here table is DESIGNATION and primary key DESIGNATIONID
type Employee struct {
EmployeeID int64 `gorm:"primary_key;column:employee_id"`
EmployeeCode string `gorm:"column:employee_code"`
FirstName string `gorm:"column:first_name"`
LastName string `gorm:"column:last_name"`
DesignationDesignationID int64
Designation *Designation
}
type Designation struct {
DesignationID int64 `gorm:"primary_key;`
DesignationName string `gorm:"column:designation_name"`
}

Resources