I am trying to save an Array of Structs.
I tried:
type ShiftValue struct {
Hour uint8 `json:"hour"`
Minute uint8 `json:"minute"`
}
type Shift struct {
Start ShiftValue `json:"start"`
End ShiftValue `json:"end"`
}
type Config struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;index;" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
Shifts []Shift `gorm:"type:varchar(100)[];" json:"shifts,"`
}
But is not working. I also tried to save Shifts as pq.StringArray:
type Config struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;index;" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
Shifts pq.StringArray `gorm:"type:varchar(100)[];" json:"shifts,"`
}
Which is kind of working, but I don't know how can I convert a slice of Shift to StringArray.
Should I use GenericArrray?
How can I make the conversion from Slice to GenericArray or StringArray?
When I Unmarshall the data, I do it in the following struct, I validate the data, and after that I want to save it to Database:
type ConfigUpdate struct {
Shifts []Shift `json:"shifts,"`
}
The nearest scenario that can be seen in gorm embedded struct test is
package gorm_test
import "testing"
type BasePost struct {
Id int64
Title string
URL string
}
type Author struct {
ID string
Name string
Email string
}
type HNPost struct {
BasePost
Author `gorm:"embedded_prefix:user_"` // Embedded struct
Upvotes int32
}
type EngadgetPost struct {
BasePost BasePost `gorm:"embedded"`
Author Author `gorm:"embedded;embedded_prefix:author_"` // Embedded struct
ImageUrl string
}
As can be seen, all of these Base Struct have Id to be referred to as foreign key in parent struct.
One more scenario that can be found handled in one of StackOverflow another answer.
type Children 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"`
}
Please verify it on your own as I don't have a gorm set up. I just did the R & D part. I hope, it will help many of us. thanks
Related
Format of bson:
{
name: "root",
sports:" Cricket",
Personal: {
age:32
}
}
Go struct format:
type Person struct {
Name string `bson:"name"`
Age int `bson:"age"`
}
How to bind the value of age into this struct format in golang?
You need an intermediate type that will help you transform your database DTO (Data Transform Object) into model object. It is recommended to separate those two.
package main
import (
"encoding/json"
"fmt"
)
type PersonInfoDto struct {
Age int `json:"age"`
}
type PersonDto struct {
Name string `json:"name"`
Sports string `json:"sports"`
Personal PersonInfoDto `json:"Personal"`
}
type Person struct {
Name string
Age int
}
func main() {
jsonInput := "{ \"name\": \"root\", \"sports\": \"Cricket\", \"Personal\": { \"age\": 32 }}"
var dto PersonDto
err := json.Unmarshal([]byte(jsonInput), &dto)
if err != nil {
fmt.Println(err)
}
// your model object
p := Person{dto.Name, dto.Personal.Age}
fmt.Println(p)
}
When I try to update the Shoppinglist struct with the data I get an "there is no unique or exclusion constraint matching the ON CONFLICT specification (SQLSTATE 42P10)" Error
These are my Structs
type Shoppinglist struct {
Model
ID int `gorm:"primaryKey" json:"id"`
Title string `json:"title"`
Items []Item `json:"items" gorm:"foreignKey:ParentListID;references:ID;"`
Owner string `json:"owner"`
Participants pq.StringArray `gorm:"type:text[]" json:"participants"`
}
type Item struct {
Model
ParentListID int `gorm:"primaryKey" json:"parentListId"`
Title string `json:"title"`
Position int `json:"position"`
Bought bool `json:"bought"`
}
And this is the Code I execute when trying to edit a list
func EditList(id int, data map[string]interface{}) error {
//https://github.com/go-gorm/gorm/issues/3487
shoppinglist := Shoppinglist{
ID: data["id"].(int),
Title: data["title"].(string),
Items: data["items"].([]Item),
Owner: data["owner"].(string),
Participants: data["participants"].([]string),
}
if err := db.Session(&gorm.Session{FullSaveAssociations: true}).Where("id = ?", id).Updates(&shoppinglist).Error; err != nil {
return err
}
return nil
}
This is where I execute the EditList and where I set all the values to pass nito the map:
type Shoppinglist struct {
ID int
Title string
Items []models.Item
Owner string
Participants []string
PageNum int
PageSize int
}
func (s *Shoppinglist) Edit() error {
shoppinglist := map[string]interface{}{
"id": s.ID,
"title": s.Title,
"items": s.Items,
"owner": s.Owner,
"participants": s.Participants,
}
return models.EditList(s.ID, shoppinglist)
}
Before I was just using a []string instead of []Item and that was working perfectly. Now everything updates except for the []Item
These are the SQL Queries executed:
UPDATE "shoppinglists" SET "modified_on"=1628251977096,"title"='kjhdsfgnb',"owner"='janburzinski1#gmail.com',"participants"='{}' WHERE id = 517687 AND "id" = 517687
INSERT INTO "items" ("created_on","modified_on","deleted_at","title","position","bought","parent_list_id") VALUES (1628251977,1628251977116,NULL,'dfkjhgndfjkg',1,false,517687),(1628251977,1628251977116,NULL,'dfgh123',2,true,517687) ON CONFLICT ("parent_list_id") DO UPDATE SET "created_on"="excluded"."created_on","modified_on"="excluded"."modified_on","deleted_at"="excluded"."deleted_at","title"="excluded"."title","position"="excluded"."position","bought"="excluded"."bought" RETURNING "parent_list_id"
I would really like to know how to Update a Relation in Gorm or why this isn't working because I've been looking through all the Association Issues on Github and Stackoverflow and didn't find a answer that worked for me.
The first problem I see here is that your Item has no ID but uses the ParentListID as primary key. That means you can only have one Item for each parent which defeats the purpose of having an array.
Create an ID field (used as primary key) for items and if there's still issues with your approach, please update the question.
PS: would have left this in a comment, but can't.
I just needed to add the * to the []Item and fix the problem with the primarykey and remove the reference.
type Shoppinglist struct {
Model
ID int `gorm:"primaryKey" json:"id"`
Title string `json:"title"`
Items []*Item `json:"items" gorm:"foreignKey:ParentListID;"`
Owner string `json:"owner"`
Participants pq.StringArray `gorm:"type:text[]" json:"participants"`
}
type Item struct {
Model
ID int `gorm:"primaryKey" json:"id"`
ParentListID int `json:"parentListId"`
ItemID int `json:"itemId"`
Title string `json:"title"`
Position int `json:"position"`
Bought bool `json:"bought" gorm:"default:false"`
}
I am making rest API using go and echo, and I'm working on updating data. At first I was using struct to store the payload and then do the update to mongodb
type updatePayload struct {
FullName string `json:"fullName" bson:"fullName"`
FirstName string `json:"firstName" bson:"firstName"`
LastName string `json:"lastName" bson:"lastName"`
Location string `json:"location" bson:"location"`
Gender string `json:"gender" bson:"gender"`
State string `json:"state" bson:"state"`
Subdistrict string `json:"subdistrict" bson:"subdistrict"`
Address string `json:"homeAddress" bson:"homeAddress"`
Profession string `json:"provession" bson:"provession"`
Settings struct {
Email bool `json:"email" bson:"email"`
SMS bool `json:"sms" bson:"sms"`
GCM bool `json:"gcm" bson:"gcm"`
} `json:"settings" bson:"settings"`
Coordinates struct {
Type string `json:"type" bson:"type"`
Coordinates []float64 `json:"coordinates" bson:"coordinates"`
} `json:"coordinates" bson:"coordinates"`
}
The update is working but if I'm not send all the parameters like only send one field , the rest of the fields is updated to, only it was an empty string ""
is there any way I can only update the field that was specified on the request payload.
You have to specify omitempty on the struct
type PayloadUpdateProfile struct {
FullName string `json:"fullName,omitempty" bson:"fullName,omitempty"`
FirstName string `json:"firstName,omitempty" bson:"firstName,omitempty"`
LastName string `json:"lastName,omitempty" bson:"lastName,omitempty"`
}
Bind the payload into the struct as usual
var payload profile.PayloadUpdateProfile
err := c.Bind(&payload)
then convert it
var payloadInterface map[string]interface{}
inrecPayload, _ := json.Marshal(payload)
json.Unmarshal(inrec, &payloadInterface)
UpdateMethod(ctx, filter, payloadInterface)
You can do things like this:
type updatePayload struct {
FullName string `json:"fullName" bson:"fullName"`
FirstName string `json:"firstName" bson:"firstName"`
LastName string `json:"lastName" bson:"lastName"`
Location string `json:"location" bson:"location"`
Gender string `json:"gender" bson:"gender"`
State string `json:"state" bson:"state"`
Subdistrict string `json:"subdistrict" bson:"subdistrict"`
Address string `json:"homeAddress" bson:"homeAddress"`
Profession string `json:"provession" bson:"provession"`
Settings struct {
Email bool `json:"email" bson:"email"`
SMS bool `json:"sms" bson:"sms"`
GCM bool `json:"gcm" bson:"gcm"`
} `json:"settings" bson:"settings"`
Coordinates struct {
Type string `json:"type" bson:"type"`
Coordinates []float64 `json:"coordinates" bson:"coordinates"`
} `json:"coordinates" bson:"coordinates"`
}
func (payload *updatePayload) prepareUpdateQuery() (bson.M, error) {
updateQuery := make(bson.M)
if len(payload.FirstName) != 0 {
updateQuery["firstName"] = payload.FirstName
}
if len(payload.LastName) != 0 {
updateQuery["lastName"] = payload.LastName
}
// do same like above for other fields
updateQ := make(bson.M)
updateQ["$set"] = updateQuery
return updateQ, nil
}
have the method on updatePayload struct and after decoding the api payload , call this method and generate the update query for those fields only which has data from api payload. Once update query is made pass that update query to mongodb update wrapper function .
I have People and Data , where People has one Data and Data belongs to People
how to make a request body JSON for that Association in go gin? I am using gorm for this case,
the documentation of gorm is not clear for me for this case,
i was supposed like
func CreateTodo(db *gorm.DB) func(c *gin.Context) {
var person Person
var data Data
c.bindJSON(&Person)
c.bindJSON(&Data)
db.create(&Person)
db.create(&Data)
c.JSON(200, gin.H{ result : []interface{person, data})
}
type (
Data struct {
ID uint `gorm:"auto_increment"`
PersonID uint
Person *Person `gorm:"foreignkey:PersonID;association_foreignkey:id"`
Address string
Languange string
}
Person struct {
gorm.Model
Email string `gorm:"type:varchar(100);unique_index;not null"`
Password string `gorm:"type:varchar(100);not null"`
Role string `gorm:"size:30;not null"`
DataID uint
Data *Data `gorm:""foreignkey:DataID;association_foreignkey:id"`
}
)
I am sure it will not make the person_id and data_id for FK
what I ask, how I can make the request body for that Association until those request created with FK itself ? should I create then update again for person_id and data_id after it created ??
Gorm will do almost everything for an association link. It seems that "DataID" in your Person struct is useless. See the code below for an example:
package main
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type (
Data struct {
ID uint `gorm:"auto_increment"`
PersonID uint
Person *Person `gorm:"foreignkey:PersonID;association_foreignkey:id"`
Address string
Languange string
}
Person struct {
gorm.Model
Email string `gorm:"type:varchar(100);unique_index;not null"`
Password string `gorm:"type:varchar(100);not null"`
Role string `gorm:"size:30;not null"`
Data *Data `gorm:""foreignkey:PersonID;association_foreignkey:id"`
}
)
func main() {
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic("failed to connect database")
}
db.LogMode(true)
defer db.Close()
// Migrate the schema
db.AutoMigrate(&Person{}, &Data{})
data := &Data{
Address: "Shanghai,China",
Languange: "Chinese",
}
person := &Person{
Email: "zhjw43#163.com",
Data: data,
}
db.Save(person)
db.DropTable("data", "people")
}
We are adding an include parameter in our API w/c API clients can use to include relationships
// request
GET /api/events?include=team1
[{
"id": <event_id>,
"name": <event_name>,
"team1": {
"id": <team_id>,
"name": <team_name>
}
}]
// code
type Event struct {
ID int64 `gorm:"primary_key" json:"id"`
Team1ID int64 `json:"-"`
Team1 Team `json:"team1"`
}
var event Event
Db.Preload("Team1").Find(&event, 1)
c.JSON(http.StatusOK, event)
But we also want to be able to do this:
// request
GET /api/events
[{
"id": <event_id>,
"name": <event_name>,
"team1": <team1_id>
}]
The team1 field is now just an id.
Is there an easy way to do this in Go?
I think I can do this by using a map[string]interface{}, like after fetching the events in the db, convert the event structs to a map[string]interface{} and do the modifications. But I'm wondering if theres an easier solution.
Here's my attempt in using map[string]interface{} - https://play.golang.org/p/-19MWtqhE3 . The code is very verbose. The idea is to use map[string]interface{} for every struct then compose the top level resource that includes the related resources.
What's a better way to do this?
Actually you could just set the team1 to be an interface and then cast it's value, obviously making the proper validations.
type Event struct {
ID int64 `gorm:"primary_key" json:"id"`
Team1ID int64 `json:"-"`
Team1 interface{} `json:"team1"`
Team1Struct Team `json:"-"`
}
And then evaluate:
if value, ok := evt.Team1.(Team); ok {
// The passed value is a Team struct
event.Team1Struct = value
} else {
// The passed value is a string (or something else...)
}
I think the most cleaner way is to use two different functions.
type Event struct {
ID int64 `gorm:"primary_key" json:"id"`
Team1ID int64 `json:"-"`
Team1 Team `json:"team1"`
}
type Team struct {
ID int64 `gorm:"primary_key" json:"id"`
Name string `json:"name"`
}
type EventInformations struct {
ID int64 `json:"id"`
Name string `json:"name"`
Team1 `json"team1"`
}
type EventInformationsTeamIncluded struct {
ID int64 `json:"id"`
Name string `json:"name"`
Team1 Team `json:"team1"`
}
func (e *Event) Informations() *EventInformations {
return &EventInformations{
ID: e.ID,
Name: e.Name,
Team1: e.Team1,
},
}
func (e *Event) InformationsTeamIncluded() *EventInformationsTeamIncluded {
return &EventInformations{
ID: e.ID,
Name: e.Name,
Team1: &Team{
...
},
}
}
So later just call
event.Informations();
Or
event.InformationsTeamIncluded();
There is something not very logical in your code/examples:
The var Team1 is a little bit weird cause there is only one value in the struct so if you don't have plan to add some Team in struct I suggest to replace it by Team if you plan to have any Team replace it by Teams []*Team
The second thing is in your example your return two differents values to a same name, it depends of the context I suggest in one case to return team1_id instead of team1
EDIT : without struct
func (e *Event) Informations() *map[string]interface{} {
return &map[string]interface{}{
"ID": e.ID,
"Name": e.Name,
"Team1": e.Team1,
},
}
func (e *Event) InformationsTeamIncluded() *map[string]interface{} {
// Reuse the previous function
data := e.Informations()
// Add the team1 informations
data["team1"] = map[string]interface{}{
ID: e.Team1.ID,
Name: e.Team1.Name,
}
return data
}