I'm trying to create a self-reference in a (my)sql table using golang gorm. At the moment my code looks like this:
type Person struct {
gorm.Model
Name string
Children []*Person `gorm:"ForeignKey:ParentID"`
ParentID uint
}
func main() {
/* code to get database connection omitted */
p := &Person{Name:"Sally"}
db.Create(p)
children := []*Person{ {Name:"Jane", ParentID:p.ID},
{Name:"Tom", ParentID:p.ID}}
for _, child := range children {
db.Create(child)
}
var children2 []*Person
db.Model(p).Related(children2, "ParentID")
}
The code is failing with an error "reflect.Value.Set using unaddressable value".
Does anybody know how to get this relationship working using go gorm?
Many thanks in advance :)
Fortunately gorm have added lately this feature (reference: here).
In your case should be like this:
type Person struct {
gorm.Model
Name string
Children []*Person `gorm:"many2many: children;association_jointable_foreignkey:children_id"`
}
Related
I have a set of employees defined in a JSON-file:
type PrimitiveEmployee struct {
PrimitiveID string `xml:"ID,attr"`
ContractID []string `xml:"ContractID"`
}
Both ID and ContractID is letters, but they are not necessarily given continuous sequence of letters. For instance, there might exist an employee with PrimitiveID=A and another with PrimitiveID=C, but not an employee with PrimitiveID=B.
I want to convert this data into:
type Employee struct {
ID int
PrimitiveID string
Contracts []Contract
}
The thing is, I want the the employee-ID to start at 0 and increment with one for each time the struct is initialized. Sort of like a an autoincrement ID in a database or iota in a enum.
The employee with PrimitiveID=A will then automatically be created with ID=0, while the employee with PrimitiveID=C will get ID=1.
I just cant figure out how to solve this in a struct.
Greatly appreciate any help or pointers here.
Create a custom type to manage the unique incrementing ID:
type autoInc struct {
sync.Mutex // ensures autoInc is goroutine-safe
id int
}
func (a *autoInc) ID() (id int) {
a.Lock()
defer a.Unlock()
id = a.id
a.id++
return
}
So you can use it in targeted places or at the package level:
var ai autoInc // global instance
You can then create "constructor" functions to leverage this:
func NewEmployee() *Employee {
return &Employee{
ID: ai.ID(),
}
}
Marshaling JSON data to Employee can then be performed and the ID will be preserved - provided the JSON data does not container an ID tag.
https://play.golang.org/p/0iTaQSzTPZ_j
I am using GORM in combination with Fiber. When querying all users with preloaded clause.Associations I get the following error:
can't preload field ###as### for entities.User
What does that mean? Without the preload of clause.Associations it works normally and but it does not show one-to-one associations.
func UsersGetAll(c *fiber.Ctx) error {
db := database.DBConn
users := []entities.User{}
records := db.Preload(clause.Associations).Find(&users)
if records.Error != nil {
log.Println(records.Error)
return c.Status(500).SendString(records.Error.Error())
}
return c.JSON(records.Value)
}
Well looks like a problem with gorm tags:
I will suppose the struct is something like this :
type Address struct {
ID int `gorm:"column:id;primaryKey"`
}
type User struct {
Name string `gorm:"column:name"`
Age string `gorm:"column:age"`
Address Address `gorm:"references:ID"`
}
And the code:
user:=User{}
if err:=db.Preload("Address").Find(&user).Error {
panic(err)
}
I think you also should look at this: Gorm relationship error: need to define a valid foreign key for relations or it need to implement the Valuer/Scanner interface
is there a way to automatically remove associations when an object is saved?
something like this:
type Parent struct {
gorm.Model
Name string
Children []*Child
}
type Child struct {
gorm.Model
Name string
ParentID uint
}
func myFunc(db *gorm.DB) {
p := &Parent{Name: "foo", Children:[]*Child{ {Name:"Bar"}, {Name:"Foobar"}}}
db.Save(&p)
p.Children = p.Children[1:]
db.Save(&p) // both children still exist in the database. i'd like the first child to be deleted here
}
`
I've found some tricks with db.Model(&Parent).Association("Children").Clear(), but that just sets the ParentID value to NULL, rather than deleting the record. Is there a simple way of doing this?
Many thanks in advance :)
I think you just simply use the physical batch delete like following code:
db.Unscoped().Where("parent_id = ?", p.ID).Delete(Child{})
Hope this help.
I stuck with an unique problem. To learn golang, I created a twitter kind of website. It has tweets and each tweets can have comments and each comment can have sub-comments.
Showing struct pd in homepage.html
Env.Tpl.ExecuteTemplate(w, "homePage.html", pd)
where pd is pagedata (I removed extra information for simplicity)
type PageData struct {
TweetView []tweets.TweetView
}
Where tweet.TweetView is
type TweetView struct {
Tweet
CV []comments.Comment
}
where comments.Comment is
type Comment struct {
TweetID int64
ParentCommentID int64
CommentID int64
CreatedAt time.Time
Name string
UserID int64
CommentMsg string
}
This works. but if I change the CV in tweetView with comment.CommentView .. template stop showing TweetView.
comment.CommentView is
type CommentView struct {
Comment
CC []Comment
}
the new TweetView would be defined as
type TweetView struct {
Tweet
CV []comments.CommentView
}
Getting this error, when trying to make a datastore query to extract tweet object into Tweetview
err := datastore.Get(ctx, tweetKey, &tweetView[v])
datastore: flattening nested structs leads to a slice of slices: field
"CV",
I think it is a limitation of golang. What should I do?
I was able to solve the problem. The problem was with datastore.Get query.
It was giving below error when I was running
err := datastore.Get(ctx, tweetKey, &tweetView[v])
datastore: flattening nested structs leads to a slice of slices: field
"CV",
So what I changed the query like this
var tweetTemp Tweet
datastore.Get(ctx, tweetKey, &tweetTemp)
tweetSlice[v].Tweet = tweetTemp
Please let me know if you see problem with this approach
I have 2 structs with data like this:
type User struct {
Pics Pic[]
}
type Pic struct {
Id int
UserId int64
}
Although everytime I insert an User, Each of the pics are inserted on their table everytime I find the users, pics are not populated:
var users []User
db.Limit(pagesize).Where("updated_at > ?", date).Find(&users)
Am I doing something wrong?
Your models (the structs) don't really make sense because User have a Pic array indicates a 'one to many' user to pics relationship however your user has no id property itself and there for cannot be related to items on the Pic table.
User should have a property Id which will be it's primary key and UserId is a foreign key on Pic that relates to it. Without the 'relation' between these two tables/entities there's no way you're going to return pics by querying users.
I'm not sure what all you need to do to make your code work since the example is incomplete but the first thing you need is an Id property which you should designate as a Primarykey with gorm annotations. You also should have annotations on the Pic struct saying UserId is a foreign key and Id is it's primary key.
Also, just fyi your array is not embedded. Embedding is a language feature which you're not using, if you embed the property it has no name and it's properties can be accessed directly from an instance of the embedding type.
I had these issues once. Then I used Join function.
See my example that works just fine:
type FileType struct {
Id int
}
type File struct {
Id int
FileType `xorm:"extends"`
}
file := File{Id: id}
has, err := eng.
Join("INNER", "FileType", "FileType.IdFileType = File.IdFileType").
Get(&file)
You probably know by now. You got to think as if you are creating a SQL table with 1-to-many relationship. Here is an example:
type Entry struct {
ID int
Name string
...
ContainerID int
}
type Container struct {
ID int
Tag int
Name string
...
Entries []Entry `gorm:"foreignkey:ContainerID"`
}
The trick is to populate it. I am yet to find how to make it in one try. For every such dependency, you got to run something like:
c := getContainerFromDB(...)
if err := getROConn().Model(c).Related(&c.Entries, "Entries").Error; err != nil {
return errors.Wrap(err, "error getting container field")
}
Try Preload
db.Limit(pagesize).Where("updated_at > ?", date).Preload("Pics").Find(&users)