Search code examples
gogo-gorm

GORM query with two or more models


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.


Solution

  • 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.