What's the best way to produce a struct result with 2 or more models combined in Gorm?
Given these example models:
type Book struct {
gorm.Model
Title string
Description string
AuthorID uint
}
type Author struct {
gorm.Model
FirstName string
LastName string
Books []Book
}
I want to create a query to find books by Title
db.Where("title=?", "foo").Find(&books)
So far no problem, but I would also like to include Author.FirstName and Author.LastName in the result. This does not work with any method I tried, since Book struct does not include those fields. The desired result should include all fields from a matching Book plus all fields from Author related to that Book.
Tried to use Select() and Join() functions to specify all the desired fields, which produced the correct SQL statement, but the resulting Book struct still does not contain any Author fields.
CodePudding user response:
Please look at the example below to have a look at how we can produce a struct with two or more models combined in Gorm.
type Driver struct {
gorm.Model
Name string
License string
Cars []Car
}
type Car struct {
gorm.Model
Year int
Make string
ModelName string
DriverID int
}
var (
drivers = []Driver{
{Name: "Shashank", License: "India123"},
{Name: "Tom", License: "India321"},
}
cars = []Car{
{Year: 2000, Make: "Toyota", ModelName: "Tundra", DriverID: 1},
{Year: 2001, Make: "Honda", ModelName: "Accord", DriverID: 1},
}
)
func GetCars(w http.ResponseWriter, r *http.Request) {
var cars []Car
db.Find(&cars)
json.NewEncoder(w).Encode(&cars)
}
// Getting cars with the id, where it will include name of driver & license
func GetCar(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
var car Car
db.First(&car, params["id"])
json.NewEncoder(w).Encode(&car)
}
Please note this is not the full code, It is basically a reference for your solution for the full code you can check my repository. https://github.com/amshashankk/GO_Postgres_Testing/blob/main/main.go
CodePudding user response:
I was able to accomplish your request in this way.
First, I added Author Author
field to the Book
struct. In this way, you can save the information of the author together with his books.
In the query, you've to use Preload("Auhtor")
to let GORM load also the information from the authors
table. This practice is called eager-loading. Below, you can find my working solution:
package main
import (
"fmt"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type Book struct {
gorm.Model
Title string
Description string
AuthorID uint
Author Author
}
type Author struct {
gorm.Model
FirstName string
LastName string
Books []Book
}
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(&Book{})
db.AutoMigrate(&Author{})
book1 := &Book{Title: "Go", Description: "Intro to Golang", AuthorID: 1}
book2 := &Book{Title: "GORM", Description: "Intro to GORM", AuthorID: 1}
author := &Author{FirstName: "John", LastName: "Doe", Books: []Book{*book1, *book2}}
db.Create(author)
var books []Book
db.Preload("Author").Where("title=?", "Go").Find(&books)
for _, v := range books {
fmt.Println("book 1:")
fmt.Printf("title: %q\n\n", v.Title)
fmt.Printf("author details:\n")
fmt.Printf("first name: %q\n", v.Author.FirstName)
fmt.Printf("last name: %q\n", v.Author.LastName)
}
}
Hope this helps you understand.