I have a query with 2 Joins to other tables, the query executes and returns all the fields from the primary table but I am not sure how I can get access to the joined fields.
in func LastTest() I am setting the var result to the TestResultDetail table, I would also like access to TestResult and SeqTestStage values in my result which are joined in the query.
Post query I can access all of the result fields in the TestResult table but none of the tables I joined on.
TestResultDetail has a FK relation to TestResult.
There are many TestResultDetail records for a single TestResult record.
For example seq_test_stage.description or any field in seq_test_stage comes over as empty.
main.go
package main
import (
"./models"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
)
func main() {
router := gin.Default()
opt := router.Group("opt/v2")
{
opt.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
opt.GET("/last-completed-test/:serial", LastTest)
}
// Add API handlers here
router.Run(":3000")
}
func LastTest(c *gin.Context) {
// Connection to the database
db := models.InitDB()
defer db.Close()
var result models.TestResultDetail
type Response struct {
Status string
Id int64
Serial string
Product string
Stage string
}
// get param and query
serial := c.Params.ByName("serial")
db.Model(&models.TestResultDetail{}).
Select("test_result.id, test_result.serial, test_result.product, test_result_detail.stage_id, seq_test_stage.description").
Joins("join test_result on test_result.ID = test_result_detail.result_id").
Joins("join seq_test_stage on seq_test_stage.ID = test_result_detail.stage_id").
Where("test_result.serial = ?", serial).
Order("test_result_detail.id desc").
Limit(1).
Scan(&result)
if result.ID != 0 {
res1 := &Response{
Status: "OK",
Id: result.ResultId.ID,
Serial: result.ResultId.Serial,
Product: result.ResultId.Product,
Stage: result.StageId.Description,
}
c.JSON(200, res1)
} else {
// Display JSON error
c.JSON(404, gin.H{"error": "No Records", "code": 404})
}
}
test_result_detail.go
package models
import (
"time"
)
type TestResultDetail struct {
ID int64 `gorm:"primary_key" db:"id" json:"id"`
ResultId TestResult
StatusId ResultStatus
StationId Station
StageId SeqTestStage
OperatorId AuthUser
Failstep string `db:"fail_step" json:"fail_step"`
Shift string `db:"shift" json:"shift"`
SequenceRev int `db:"sequence_rev" json:"sequence_rev"`
DateAdded time.Time `db:"date_added" json:"date_added"`
DateTimestamp time.Time `db:"date_timestamp" json:"date_timestamp"`
DateTime time.Time `db:"date_time" json:"date_time"`
StageOrder int `db:"stage_order" json:"stage_order"`
SerialNumber string `db:"serial_number" json:"serial_number"`
IsRetest int `db:"is_retest" json:"is_retest"`
RetestReason string `db:"retest_reason" json:"retest_reason"`
}
test_result.go
package models
import (
"time"
)
type TestResult struct {
ID int64 `gorm:"primary_key" db:"id" json:"id"`
DateAdded time.Time `db:"date_added" json:"date_added"`
Serial string `db:"serial" json:"serial"`
SequenceId int `db:"sequence_id" json:"sequence_id"`
LastCompletedStage int `db:"last_completed_stage" json:"last_completed_stage"`
LastCompletedSequence int `db:"last_completed_sequence" json:"last_completed_sequence"`
Workorder string `db:"workorder" json:"workorder"`
Product string `db:"product" json:"product"`
IsComplete string `db:"is_complete" json:"is_complete"`
IsScrapped string `db:"is_scrapped" json:"is_scrapped"`
ValueStream string `db:"value_stream" json:"value_stream"`
PromiseDate string `db:"promise_date" json:"promise_date"`
FailLock int `db:"fail_lock" json:"fail_lock"`
SequenceRev int `db:"sequence_rev" json:"sequence_rev"`
DateUpdated time.Time `db:"date_updated" json:"date_updated"`
Date time.Time `db:"date" json:"date"`
Time time.Time `db:"time" json:"time"`
Ptyp2 string `db:"ptyp2" json:"ptyp2"`
WoQty int `db:"wo_qty" json:"wo_qty"`
IsActive int `db:"is_active" json:"is_active"`
IsTimeLock int `db:"is_time_lock" json:"is_time_lock"`
TimeLockTimestamp time.Time `db:"time_lock_timestamp" json:"time_lock_timestamp"`
ScrapReason string `db:"scrap_reason" json:"scrap_reason"`
ScrappedBy int `db:"scrapped_by" json:"scrapped_by"`
}
seq_test_stage.go
package models
import (
"time"
)
type SeqTestStage struct {
ID int64 `gorm:"primary_key" db:"id" json:"id"`
PdcptypeId int `db:"pdcptype_id" json:"pdcptype"`
Description string `db:"description" json:"description"`
LongDescription string `db:"long_description" json:"long_description"`
IsActive int `db:"is_active" json:"is_active"`
DateAdded time.Time `db:"date_added" json:"date_added"`
DateUpdated time.Time `db:"date_updated" json:"date_updated"`
TimeLock int `db:"time_lock" json:"time_lock"`
LockMinutes int `db:"lock_minutes" json:"lock_minutes"`
LockHours int `db:"lock_hours" json:"lock_hours"`
}
Related
I am trying to get extra columns from a many2many relationships on Gorm. Example
Part
type Part struct {
Id unit
Name string
}
Diagram
type Diagram struct {
Id unit
Name string
Parts []Part `gorm:"many2many:diagram_parts;"`
}
DiagramPart
type DiagramPart struct{
DiagramId uint `gorm:"primaryKey"`
PartId uint `gorm:"primaryKey"`
PartDiagramNumber int
PartNumber string
PartDescription string
}
This is what I have done trying to retrieve PartNumber and PartDescription in Parts.
diagram := &Diagram{}
db := s.db.Where("id = ?", 1).
Preload("Parts", func(db *gorm.DB) *gorm.DB {
return db.Select("parts.*, diagram_parts.part_number, diagram_parts.part_description").
Joins("left join diagram_parts on diagram_parts.part_id = parts.id")
}).
First(diagram)
Unfortunately, I am not able to retrieve part_number, part_description. How should I go about it?
You can add field PartNumber and PartDescription on struct Part OR Diagram, then add tag gorm:"-:migration;->" on than fields to ignore migration and to readonly mode. But on your situation, you can add it in struct Part because you already preload it.
source: https://gorm.io/docs/models.html#Field-Level-Permission
here's the example:
package main
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Part struct {
Id uint `gorm:"primaryKey"`
Name string
PartNumber string `gorm:"-:migration;->"`
PartDescription string `gorm:"-:migration;->"`
}
type Diagram struct {
Id uint `gorm:"primaryKey"`
Name string
Parts []Part `gorm:"many2many:diagram_parts;"`
// PartNumber string `gorm:"-:migration;->"`
// PartDescription string `gorm:"-:migration;->"`
}
type DiagramPart struct {
DiagramId uint `gorm:"primaryKey"`
PartId uint `gorm:"primaryKey"`
PartDiagramNumber int
PartNumber string
PartDescription string
}
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
db.AutoMigrate(&Diagram{}, &Part{}, &DiagramPart{})
diagram := &Diagram{}
err = db.Debug().Where("id = ?", 1).
// Select("diagrams.*, diagram_parts.part_number, diagram_parts.part_description").
Preload("Parts", func(db *gorm.DB) *gorm.DB {
return db.Select("parts.*, diagram_parts.part_number, diagram_parts.part_description").
Joins("left join diagram_parts on diagram_parts.part_id = parts.id")
}).
// Joins("left join diagram_parts on diagram_parts.diagram_id = diagrams.id").
First(diagram).Error
if err != nil {
panic(err)
}
fmt.Printf("diagram: %v\n", diagram)
fmt.Println("part number:", diagram.Parts[0].PartNumber)
}
Background
The requirement is to use GORM for inserting rows into PostgreSQL but only using raw query.
My codes are as follow:
const userModelFieldsCreateReturn := "id, uuid, slug, username"
// User Model
func (repo *Repository) CreateUsers(ctx *context.Context, users []*models.User) ([]*models.User, error) {
var returnedUsers []*models.User
inserts := []string{}
for _, user := range users {
// Optional Fields
tierIDString := "NULL" // Default
if user.TierID.Valid {
tierIDString = "'" + strconv.FormatInt(user.TierID.Int64, 10) + "'"
}
statusString := "inactive" // Default
if user.Status != nil {
statusString = string(*user.Status)
}
inserts = append(inserts, "('"+user.Slug+"', "+"'"+user.Username+"', "+"'"+user.Email+"', "+"'"+user.Password+"', "+"'"+user.Key+"', "+tierIDString+", "+"'"+statusString+"')")
}
query := fmt.Sprintf(
constants.CreateQuery,
constants.UsersModel,
userModelFieldsCreate,
strings.TrimSuffix(strings.Join(inserts, ", "), ", "),
userModelFieldsCreateReturn,
)
// Debug
fmt.Println("==========")
fmt.Println(query)
fmt.Println("==========")
// Debug
var scannedUsers []interface{}
// results := repo.DBPostgres.Raw(query).Scan(&scannedUsers)
results := repo.DBPostgres.Raw(query).Scan(&returnedUsers)
if results.Error != nil {
results.Assign()
return nil, results.Error
}
// Debug
// for _, scannedUser := range scannedUsers {
// // var user *models.User
// user := &models.User{}
// byte, _ := json.Marshal(scannedUser)
// _ = json.Unmarshal(byte, user)
// }
// Debug
fmt.Println("=====")
fmt.Println(scannedUsers)
fmt.Println(returnedUsers)
fmt.Println("=====")
// Debug
// return users, nil
return returnedUsers, nil
}
Here is the model:
package models
import (
coreConstants "core/constants"
"database/sql"
"database/sql/driver"
"time"
"gorm.io/gorm"
)
type UserStatus string
const (
Active UserStatus = coreConstants.Active
Inactive UserStatus = coreConstants.Inactive
)
func (us *UserStatus) Scan(value string) error {
*us = UserStatus([]byte(value))
return nil
}
func (us UserStatus) Value() (driver.Value, error) {
return string(us), nil
}
type User struct {
ID uint `gorm:"primarykey"`
UUID string `gorm:"not null;default:md5(random()::text || clock_timestamp()::text)::uuid"`
Slug string `gorm:"not null"`
Username string `gorm:"not null"`
Email string `gorm:"not null"`
Password string `gorm:"not null"`
Key string
TierID sql.NullInt64 // Optional
Tier *Tier
Status *UserStatus `gorm:"not null;default:'inactive'" sql:"type:user_status"` // Have Default
CreatedAt time.Time `gorm:"not null;default:current_timestamp"`
UpdatedAt time.Time `gorm:"not null;default:current_timestamp"`
DeletedAt gorm.DeletedAt `gorm:"index"`
}
The query is as follows (I have tried to execute this query into the database and it ran as expected):
INSERT INTO users (
slug, username, email, password, key, tier_id, status
)
VALUES
('notalentgeek', 'notalentgeek', 'notalentgeek#gmail.com', '$2a$08$B/.rN7cCv4Ii7VHjl1ZppObUfZeCfae2vPvi4PH1siIp4/JOWNo/u', 'b7a1bd7ca3eb87b80685e5957d676609', NULL, 'inactive'), ('notalentgeek', 'notalentgeek', 'mikael.pratama#yahoo.com', '$2a$08$vrK1Dh9RgqJusoyLPxDLuez8/RZEjZNvyBgImw34QXu3VaN6Ht36m', '7802676f557266524253d04e17ef36aa', NULL, 'inactive')
RETURNING (id, uuid, slug, username);
Error
Error happened at line results := repo.DBPostgres.Raw(query).Scan(&returnedUsers) :
sql: Scan error on column index 0, name "row": unsupported Scan, storing driver.Value type string into type *models.User;
It seems that the RETURNING values from the query can't be scanned into []*models.User .
I can use []interface{} :
// Debug
var scannedUsers []interface{}
results := repo.DBPostgres.Raw(query).Scan(&scannedUsers)
Instead of using []*models.User :
var returnedUsers []*models.User
results := repo.DBPostgres.Raw(query).Scan(&returnedUsers)
And:
fmt.Println(scannedUsers)
Will return:
[(3,2320a7e6-d675-bb19-bd8c-84af1c381b39,notalentgeek,notalentgeek) (4,6b69d37c-e17c-1ce1-d47f-ffe3a3e09a8f,notalentgeek,notalentgeek)]
No error, but not a type I want.
Edits
In the query I tried to create multiple Users at once to the database.
If I change RETURNING (id, uuid, slug, username) into RETURNING (id) (just RETURNING a value) the error doesn't happen and the request succeed.
Summary
I defined a 'UserFollowing' as a 'struct' type
type UserFollowing struct {
ID string `gorm:"primaryKey;not null;unique" json:"id"`
User User `gorm:"foreignKey:ID;references:UserId"`
Followings []*User `gorm:"many2many:user_relation;joinForeignKey:UserId;JoinReferences:following_id"`
}
Where type 'User' is defined as
type User struct {
Email string `gorm:"size:100;not null;unique" json:"email"`
Password string `gorm:"size:60;not null" json:"password,omitempty"`
CreatedAt time.Time `gorm:"default:current_timestamp()" json:"created_at"`
UpdatedAt time.Time `gorm:"default:current_timestamp()" json:"updated_at"`
Verified bool `gorm:"default:false" json:"verified"`
AToken string `gorm:"size:100;not null;unique" json:"accessToken"`
RToken string `gorm:"size:100;not null;unique" json:"refreshToken"`
YouAreFollowing bool `json:"youAreFollowing"`
Username string `json:"username"`
Online bool `json:"online"`
NumFollowing uint64 `json:"numFollowing"`
NumFollowers uint64 `json:"numFollowers"`
LastOnline time.Time `gorm:"default:current_timestamp()" json:"lastOnline"`
UserId string `gorm:"primaryKey;not null;unique" json:"userid"`
FollowsYou bool `json:"followsYou"`
BotOwnerId string `json:"botOwnerId"`
Contributions uint64 `json:"contributions"`
Staff bool `json:"staff"`
DisplayName string `gorm:"size:20" json:"displayName"`
CurrentRoomId string `json:"currentRoomId"`
CurrentRoom Room `json:"currentRoom"`
Bio string `gorm:"size:250" json:"bio"`
Avatar string `gorm:"size:100" json:"avatarUrl"`
BannerUrl string `json:"bannerUrl"`
WhisperPrivacySetting string `json:"whisperPrivacySetting"`
Room_Permissions RoomPermissions `json:"roomPermissions"`
}
I tried to append 'User' type elements to the 'Followings' field by doing the following:
for i, _ := range users {
...
userfollowings[i].ID = users[i].UserId
userfollowings[i].Followings = append(userfollowings[i].Followings, users[i])
...
}
Error
cannot use users[i] (type models.User) as type *models.User in append
Unsuccessful Attempt
*userfollowings[i].Followings = append(*userfollowings[i].Followings, users[i])
References consulted
difference-using-pointer-in-struct-fields
Any suggestions on how to solve this problem?
You should use address of the structure instead of direct value if you want to store pointers []*User.
Try to visit this page and run this code:
package main
import "fmt"
type User struct {
UserId string
}
type UserFollowing struct {
ID string
Followings []*User
}
var (
users = []User{{"user-1"}, {"user-2"}, {"user-3"}}
userFollowings = make([]UserFollowing, len(users))
)
func main() {
for i, _ := range users {
// ...
userFollowings[i].ID = users[i].UserId
userFollowings[i].Followings = append(userFollowings[i].Followings, &users[i])
// ...
}
fmt.Println("%#v", userFollowings)
}
I have a table that represents my user data. There is a field that represents a phone number, and I want to store the hash of that automatically in the database at either update or insert.
so my model is :
type Users struct {
gorm.Model
ID uint `gorm:"autoIncrement;unique" json:"id"`
PhoneNumber string `json:"phone_number"`
HashID string `gorm:"primaryKey" json:"hash_id"`
Name string `gorm:"default:dear user" json:"name"`
Rank uint `json:"rank"`
Score uint `json:"score"`
Image string `json:"image"`
Email string `json:"email"`
Address string `json:"address"`
Birthday string `json:"birthday"`
Biography string `json:"biography"
}
How can I tell the GORM to fill the HashID column with the sha256 hash code of the PhoneNumber column while inserting or updating data?
You need something like this:
package main
import (
"crypto/sha256"
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Users struct {
gorm.Model
Key string `json:"phone_number"`
Hash string `gorm:"primaryKey" json:"hash_id"`
}
func (u *Users) BeforeCreate(tx *gorm.DB) (err error) {
h := sha256.Sum256([]byte(u.Key))
u.Hash = fmt.Sprintf("%x", h[:])
return nil
}
func (u *Users) BeforeSave(tx *gorm.DB) (err error) {
h := sha256.Sum256([]byte(u.Key))
u.Hash = fmt.Sprintf("%x", h[:])
return nil
}
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
db.AutoMigrate(&Users{})
u := Users{Key: "123"}
db.Create(&u)
}
Check https://gorm.io/docs/index.html
I am usin jinzhu GORM package for connecting to the DB, etc.
This is my code
package pizzas
import (
"github.com/jinzhu/gorm"
"github.com/gin-gonic/gin"
"speedy-gonzales/db"
"net/http"
)
type Pizza struct {
gorm.Model
Name string `gorm:"not null"`
Image string `sql:"type:text"`
PSizesAndPrices []PizzaPriceSize
}
type PizzaPriceSize struct {
gorm.Model
SizeTitle string `gorm:"column:size_title;type:varchar(50);not null'"`
PriceEur int `gorm:"column:price_eur"`
PriceBam int `gorm:"column:price_bam"`
PizzaID uint `gorm:"index"`
}
func (PizzaPriceSize) TableName() string {
return "pizza_price_sizes"
}
func FetchPizzasWithSizes(c *gin.Context) {
var pizza_model []Pizza
var p_sizes_prices_model []PizzaPriceSize
dB := db.DbConnect()
dB.Debug().Model(&pizza_model).Related(&p_sizes_prices_model)
c.JSON(http.StatusOK, gin.H{
"status": http.StatusOK,
"message": p_sizes_prices_model,
})
return
}
When I run the app this is what I get from debug console:
SELECT * FROM pizza_price_sizes WHERE pizza_price_sizes.deleted_at IS NULL AND ((pizza_id = '0'))
My question is how can I pass pizza_id to this query?