GORM query with two or more models - go

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.

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.

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

Related

How to create the dynamic struct except using interface and Using spread operator

I am trying to create the new API with Go and Fiber framework. I am using MongoDB.
Please refer to my below structure
type Product struct {
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
Brand string `json:"brand"`
Variants []map[string]string `json:"variants"`
Categories []int `json:"categories"`
}
The Variants field will have another set array of objects. But the fields will be dynamic. So I can't define the structure. Now this one I solved with []map[string][string].
My task here I have to form the CSV file with the below format.
[{
Name: "Rock",
Code: "RRR"
Description: "This is rock"
Brand: "R"
...variants
},
{
Name: "Rock",
Code: "RRR"
Description: "This is rock"
Brand: "R"
...variants
},
{
Name: "Rambo",
Code: "RAM"
Description: "This is Rambo"
Brand: "RA"
...variants
},
{
Name: "Rambo",
Code: "RAM"
Description: "This is Rambo"
Brand: "RA"
...variants
}]
Only variants will change for every row. Others will be remaining same
I formed the other fields except for the Variants. I can't create the struct because the data will be dynamic.
I have a lot of confusion
How to create the struct for dynamic fields
How to spread the Variants at the root level
I am from javascript. So, we used the spread operator. Here I am confused about how to do that in Golang.
Help me out, guys
#mkopriva is right
However you can use reflection to dynamically construct a new struct if you like
follow is a example:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type DynamicMap map[string]interface{}
type Product struct {
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
Brand string `json:"brand"`
Variants map[string]string `json:"variants"`
Categories []int `json:"categories"`
}
func (j Product) MarshalJSON() ([]byte, error) {
m := DynamicMap{}
t := reflect.TypeOf(j)
v := reflect.ValueOf(j)
for i := 0; i < t.NumField(); i++ {
if t.Field(i).Tag.Get("json") == "variants" {
dyn := v.Field(i).Interface().(map[string]string)
for key, val := range dyn {
m[key] = val
}
} else if t.Field(i).Type.Kind() == reflect.String {
m[t.Field(i).Tag.Get("json")] = v.Field(i).String()
} else {
m[t.Field(i).Tag.Get("json")] = v.Field(i)
}
}
return json.Marshal(m)
}
func (j Product) UnmarshalJSON(data []byte) error {
fmt.Println("Unmarshal...")
return json.Unmarshal(data, &j)
}
func main() {
p := Product{Name: "aa", Variants: map[string]string{"a": "a", "b": "b"}}
marshal, err := json.Marshal(p)
if err != nil {
fmt.Printf("%v\n", err)
}
fmt.Printf("%s\n", marshal)
}
{"a":"a","b":"b","brand":"","categories":{},"code":"","description":"","name":"aa"}

One-to-many association

I'm having troubles with one-to-many associations in GORM.
I have those two structures and I'd like to get one patient's full history. Here is my sample code:
type Patient struct {
gorm.Model
Prenom string `json:"prenom" gorm:"column:patient_prenom"`
Nom string `json:"nom" gorm:"column:patient_nom"`
Genre string `json:"genre" gorm:"column:patient_genre"`
Naissance string `json:"naissance" gorm:"column:patient_naissance"`
Historique []Historique `gorm:"ForeignKey:Fk_patient_id"`
}
type Historique struct {
Fk_patient_id string
Date_consultation string
Fk_maladie_id uint
Fk_compte_medecin_id uint
Patient Patient
}
func GetPatientWithDiseases(id uint) (*Patient, error) {
patient := &Patient{}
//The line right there works so i can retrieve without the history
//err := GetDB().Find(patient, id).Error
db := GetDB().Preload("tt_historique").Find(patient)
err := db.Error
if err != nil {
return nil, err
}
return patient, nil
}
Where "Historique" uses the foreign key of the patient (Fk_patient_id), and the Historique []Historique is the list of every Historique that should end up in the Patient struct after the query.
However I get this error can't preload field tt_historique for models.Patient. I've tried multiple syntaxes that I've found on Internet in the gorm specifications in the struct but nothing worked. I've only been developing using GO for 3 days and GORM is my first ORM, so maybe I'm missing something really obvious.
Based on the presumption that tt_historique is your table name, there are a couple of things you need to take care of here.
By convention, go-gorm uses pluralized snake case struct names as database tables when constructing a SQL query. In your case, to preload the Historique []Historique field, it would look for the historiques table.
To override this, you need to implement the Tabler interface:
type Patient struct {
gorm.Model
Prenom string `json:"prenom" gorm:"column:patient_prenom"`
Nom string `json:"nom" gorm:"column:patient_nom"`
Genre string `json:"genre" gorm:"column:patient_genre"`
Naissance string `json:"naissance" gorm:"column:patient_naissance"`
Historique []Historique `gorm:"foreignKey:Fk_patient_id"`
}
type Historique struct {
Fk_patient_id string
Date_consultation string
Fk_maladie_id uint
Fk_compte_medecin_id uint
Patient Patient
}
func (Historique) TableName() string {
return "tt_historique"
}
Then, your query would look like this:
db := GetDB().Preload("Historique").Find(patient)

Golang Pointer Understanding

I am learning go and i need to understand something. I am getting few errors. I have created a Product struct and attached a func with it. I also got a product lists as a slice. Actually I am following one example. I was just trying add different endpoints to it.
I have added question in comment in code. Please explain. I need to return the json single object as a response to user. Please guide me.
package data
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Price float32 `json:"price"`
SKU string `json:"sku"`
CreatedOn string `json:"-"`
UpdatedOn string `json:"-"`
DeletedOn string `json:"-"`
}
type Products []*Product
func (p *Products) ToJSON(w io.Writer) error {
e := json.NewEncoder(w)
return e.Encode(p)
}
func (p *Product) FromJSON(r io.Reader) error {
d := json.NewDecoder(r)
return d.Decode(p)
}
var ErrProductNotFound = fmt.Errorf("Product not found")
func GetProduct(id int) (*Product, error) { // this is returning *Product & err. When I use this in GetProduct in handler func it is giving error
for _, p := range productList {
if p.ID == id {
fmt.Println(p)
return p, nil
}
}
return nil, ErrProductNotFound
}
var productList = []*Product{ **// Why in example the teacher doing it like this.** []*Product{&Product{}, &Product{}} **what it the reason? Please explain.
&Product{ // this gives warning : redundant type from array, slice, or map composite literal. need to understand why**
ID: 1,
Name: "Latte",
Description: "chai",
Price: 2.45,
SKU: "abc123",
CreatedOn: time.Now().UTC().String(),
UpdatedOn: time.Now().UTC().String(),
},
&Product{
ID: 2,
Name: "Tea",
Description: "chai",
Price: 1.45,
SKU: "abc1234",
CreatedOn: time.Now().UTC().String(),
UpdatedOn: time.Now().UTC().String(),
},
}
package handlers
func (p *Product) GetProduct(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
p, errr := data.GetProduct(id) **// cannot use data.GetProduct(id) (value of type *data.Product) as *Product value in assignment**
errr = p.ToJSON(rw) // **p.ToJSON undefined (type *Product has no field or method ToJSON)**
if errr != nil {
http.Error(rw, "could not locate the product", http.StatusBadGateway)
}
}
cannot use data.GetProduct(id) (value of type *data.Product) as *Product value in assignment
p.ToJSON undefined (type *Product has no field or method ToJSON)
The problem here is that inside the GetProduct handler the variable p already has a type (*handlers.Product) that is different from the one returned by the data.GetProduct function (*data.Product). So what you can do is to use a different name for the variable that will store the result of data.GetProduct.
Why in example the teacher doing it like this. []*Product{&Product{}, &Product{}} what it the reason? Please explain.
In general because that's one of the available methods for how to initialize a slice of structs, as per the language spec. Why the teacher used this method specifically? Unless the teacher confided the reason to someone, then no one would know, I certainly don't.
this gives warning : redundant type from array, slice, or map composite literal. need to understand why
Because it's true, it is redundant, as per the language spec, in a composite literal expression you can elide the types of the elements and keys.
For example a non-redundant version of the following composite literal:
[]*Product{&Product{}, &Product{}}
would look like this:
[]*Product{{}, {}}
and the result of these two expressions would be the same.

Golang Gorm reverse hasMany relation

We can Easy to get the child of hasMany relation in Golang Gorm with Preload.
But how to get reverse of relation.
type Owner struct {
ID int `gorm:"column:id" json:"id"`
Name string `gorm:"column:name" json:"name"`
Projects []Project `gorm:"foreignkey:OwnerID" json:"projects"`
}
type Project struct {
ID int `gorm:"column:id" json:"id"`
Name string `gorm:"column:name" json:"name"`
OwnerID int `gorm:"column:owner_id" json:"owner_id"`
Gallery []Gallery `gorm:"foreignkey:ProjectID" json:"gallery"`
}
type Gallery struct {
ID int `gorm:"column:id" json:"id"`
ProjectID int `gorm:"column:project_id" json:"project_id"`
Url string `gorm:"column:url" json:"url"`
Title string `gorm:"column:title" json:"title"`
Description string `gorm:"column:description" json:"description"`
}
we can populate Gallery inside Project with Preload, like this:
db.Preload("Gallery").Find(&project)
How to get reverse, we want load project from gallery or owner from project?
the result I want some thing like this, when get project in form of json:
{
"id": 1,
"name": "Name Of Project",
"owner": {
"id":1,
"name": "Owner 1"
},
"gallery":[]
}
I am a bit late to the game with this answer, but you can define inverse relationships (belongs to) on the child models by doing this:
type ParentModel struct {
Children []Child // This is a 'has many'
}
type Child struct {
ParentModelID int // These behave as a 'belongs to'
ParentModel ParentModel // You need both the model and the modelID
}
Full working example
This uses the models from your question. A note on performance, the preloading in gorm isn't done via joins. It's done via additional SQL queries, the more nesting the more queries. If you have commonly executed preloading, it could be worth writing a raw query and a customer scanner to use an optimised query.
package main
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Owner struct {
gorm.Model
Projects []Project
}
type Project struct {
gorm.Model
OwnerID int
Owner Owner
Gallery []Gallery
}
type Gallery struct {
gorm.Model
ProjectID int
Project Project
}
func main() {
db, err := gorm.Open(sqlite.Open("many2many.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
err = db.AutoMigrate(&Owner{}, &Project{}, &Gallery{})
if err != nil {
return
}
ownerOne := Owner{}
db.Create(&ownerOne)
projectOne := Project{Owner: ownerOne}
projectTwo := Project{Owner: ownerOne}
db.Create(&projectOne)
db.Create(&projectTwo)
galleryOne := Gallery{Project: projectOne}
galleryTwo := Gallery{Project: projectOne}
galleryThree := Gallery{Project: projectTwo}
galleryFour := Gallery{Project: projectTwo}
db.Create(&galleryOne)
db.Create(&galleryTwo)
db.Create(&galleryThree)
db.Create(&galleryFour)
// Find by project and preload owners
fetchedProject := Project{}
db.Preload("Owner").Find(&fetchedProject, projectOne.ID)
fmt.Println(fetchedProject.Owner)
// Find by a gallery and preload project and owner
fetchedGallery := Gallery{}
db.Preload("Project.Owner").Find(&fetchedGallery, galleryOne.ID)
fmt.Printf("Gallery.id = %d --> Project.id = %d --> Owner.id = %d\n", fetchedGallery.ID,
fetchedGallery.Project.ID, fetchedGallery.Project.Owner.ID)
}

Go String after variable declaration

Take a look at this snip found at here
import (
"encoding/xml"
"fmt"
"os"
)
func main() {
type Address struct {
City, State string
}
type Person struct {
XMLName xml.Name `xml:"person"`
Id int `xml:"id,attr"`
FirstName string `xml:"name>first"`
LastName string `xml:"name>last"`
Age int `xml:"age"`
Height float32 `xml:"height,omitempty"`
Married bool
Address
Comment string `xml:",comment"`
}
v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
v.Comment = " Need more details. "
v.Address = Address{"Hanga Roa", "Easter Island"}
enc := xml.NewEncoder(os.Stdout)
enc.Indent(" ", " ")
if err := enc.Encode(v); err != nil {
fmt.Printf("error: %v\n", err)
}
}
I can understand in the struct Person, It has a var called Id, which is of type int, but what about the stuff xml:"person" after int? What does it mean? Thanks.
It's a struct tag. Libraries use these to annotate struct fields with extra information; in this case, the module encoding/xml uses these struct tags to denote which tags correspond to the struct fields.
which mean that variable will present in the name of Person example
type sample struct {
dateofbirth string `xml:"dob"`
}
In the above example, the field 'dateofbirth' will present in the name of 'dob' in the XML.
you will see this notation often in go struct.

Resources