Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang GORM embedded struct

Tags:

go

go-gorm

I want to join and preload 2 table and combine the result, but I could not get the desired result.

models.go

type Product struct {
    ID          uint   `json:"-"`
    Name        string `json:"name"`
    Description string `json:"description"`
}

type TransactionItem struct {
    ID        uint `json:"-"`
    ProductID uint `json:"-"`
    Qty       int  `json:"qty"`
    Price     int  `json:"price"`
    Product   // I want to combine properties of Product into Transaction Item
}

controllers.go

var items []TransactionItem
if e := db.Preload("Product").Find(&items).Error; e != nil {
    log.Println(e)
}

Desired result:

[
  {
     "qty": 1,
     "price": 50,
     "name": "Product name",
     "description": "This is the product description"
  }, { ... }
]

I have tried using "gorm:foreigKey=ProductID;references=ID;embedded;embeddedPrefix:product", and switching and trying out different combinations but still could not get the product.name and product.description to merge the data to TransactionItem. If I change Transaction Item to this:

type TransactionItem struct {
    ID        uint `json:"-"`
    ProductID uint `json:"-"`
    Qty       int  `json:"qty"`
    Price     int  `json:"price"`
    Product   Product 
}

//This will work and display data from product table, but the result is as follow:
[
  {
     "qty": 1,
     "price": 50,
     "product": {
       "name": "Product name",
       "description": "This is the product description"
     }
  }, { ... }
]

I can achieve the desired result by looping this result and manually set the name & description, but I think there should be a way for GORM to automatically do this, right? Thank you in advance.

like image 841
Charas Avatar asked Feb 22 '26 00:02

Charas


1 Answers

To achieve your desired result where the Product fields are embedded directly within the TransactionItem in the resulting JSON, you have to use embedded structs and alias the embedded struct fields with the desired JSON field names.

Given the following structs, GORM automatically embeds the fields of the Product struct into TransactionItem:

type Product struct {
    ID          uint   `json:"-"`
    Name        string `json:"name"`
    Description string `json:"description"`
}

type TransactionItem struct {
    ID        uint `json:"-"`
    ProductID uint `json:"-"`
    Qty       int  `json:"qty"`
    Price     int  `json:"price"`
    Product   `gorm:"foreignKey:ProductID"` // Embed Product struct
}

When GORM loads a TransactionItem, it should automatically load and embed the corresponding Product without the need to explicitly call Preload("Product").

However, this will generate the JSON structure like this:

{
    "qty": 1,
    "price": 50,
    "ID": 1,
    "Name": "Product name",
    "Description": "This is the product description"
}

To generate your desired JSON structure, you would need to add JSON field tags to alias the fields of the embedded Product struct in the TransactionItem struct, like this:

type TransactionItem struct {
    ID          uint `json:"-"`
    ProductID   uint `json:"-"`
    Qty         int  `json:"qty"`
    Price       int  `json:"price"`
    Name        string `json:"name"`        // Alias for Product.Name
    Description string `json:"description"` // Alias for Product.Description
    Product     `gorm:"foreignKey:ProductID"` // Embed Product struct
}

This should generate your desired JSON structure:

{
    "qty": 1,
    "price": 50,
    "name": "Product name",
    "description": "This is the product description"
}

Remember to run this loading code without explicit preloading:

var items []TransactionItem
if e := db.Find(&items).Error; e != nil {
    log.Println(e)
}

And note that this technique aliases the Product fields in TransactionItem, effectively overwriting them, so it's crucial to ensure there are no naming collisions between the fields in Product and TransactionItem.

like image 57
syswe Avatar answered Feb 27 '26 10:02

syswe