Reputation: 4831
How can I store an embedded struct with GORM if I have a type like this
type A struct {
point GeoPoint
}
type GeoPoint struct {
Lat float64
Lon float64
}
GORM tries to add it in a new table, but I want to add it as another field.
How can this be done?
Upvotes: 14
Views: 13836
Reputation: 76
For anyone, who's searching the way to put struct inside GORM model and make it marshalling and unmarshalling automatically.
This solution is based on chris's answer. And it works!
For example, I want to put array of Childrens inside Parent as marshalled JSON:
type Child struct {
Lat float64
Lng float64
}
type ChildArray []Children
func (sla *ChildArray) Scan(src interface{}) error {
return json.Unmarshal(src.([]byte), &sla)
}
func (sla ChildArray) Value() (driver.Value, error) {
val, err := json.Marshal(sla)
return string(val), err
}
type Parent struct {
*gorm.Model
Childrens ChildArray `gorm:"column:childrens;type:longtext"`
}
Upvotes: 6
Reputation: 581
First, using gorm you never define fields with the first letter in lower case.
If you make sure GeoPoint is linked as Aid , you can use ForeingKey tag like, MAKE SURE Id is table A 's primary key.
type A struct {
Id int `gorm:"column:"id""`
Point GeoPoint `gorm:"column:geo_point;ForeignKey:OrderId"`
}
type GeoPoint struct {
Aid int
Lat float64
Lon float64
}
func main(){
...
...
var as []A
if e:=db.Model(&A{}).Find(&as).Error;e!=nil{
handle(e)
}
fmt.Println(as)
}
If not linked by primary key. You can use middleware like
type A struct {
Id int `gorm:"column:"id""`
Point GeoPoint `gorm:"column:geo_point"`
}
func (a *A) AfterFind()error{
return db.Model(&GeoPoint{}).First(&(a.Point)).Error
}
type GeoPoint struct {
Aid int
Lat float64
Lon float64
}
func main(){
...
...
var as []A
if e:=db.Model(&A{}).Find(&as).Error;e!=nil{
handle(e)
}
fmt.Println(as)
}
Upvotes: -1
Reputation: 4351
I am not familiar with Gorm, but with sqlx you will need to implement the sql.Scanner and sql.Valuer interfaces to allow the data to be converted to the postgres point
type. The code below isn't checking for errors so it will need some tweaks.
https://play.golang.org/p/2-Y-wSeAWnj
package main
import (
"database/sql/driver"
"fmt"
"strconv"
"strings"
)
type GeoPoint struct {
Lat float64
Lon float64
}
func (gp GeoPoint) Value() (driver.Value, error) {
return fmt.Sprintf("(%d,%d)", gp.Lat, gp.Lon), nil
}
func (gp GeoPoint) Scan(src interface{}) error {
raw := src.(string)
coords := raw[1 : len(raw)-1]
vals := strings.Split(coords, ",")
gp.Lat, _ = strconv.ParseFloat(vals[0], 64)
gp.Lon, _ = strconv.ParseFloat(vals[1], 64)
return nil
}
func main() {
gp := GeoPoint{Lat: 112, Lon: 53}
d, _ := gp.Value()
fmt.Printf("%+v\n", d)
gp.Scan("(53, -110)")
fmt.Printf("%+v\n", gp)
}
Upvotes: 0