Using go 1.5, and gorm.
Say I want to make an events table that has a created_by_id and an updated_by_id columns.
I write my models like
type By struct {
ByID sql.NullInt64
By *User
}
type CreatedBy struct {
By
}
type UpdatedBy struct {
By
}
type Event struct {
CreatedBy
UpdatedBy
}
When I try to save an event object, the value for the column by_id will try to be saved rather than the values for created_by_id and updated_by_id. What do I need to do to make sure the column names of ByID attribute are different for CreatedBy and UpdatedBy?
The problem is that you're embedding both CreatedBy and UpdatedBy into Event, so calls to Event.By are ambiguous and not allowed (you'd have to be able to specify Event.CreatedBy.By and Event.UpdatedBy.By explicitly to disambiguate the two fields).
The solution, most likely, would be to not embed the types, but actually create a struct with explicit fields:
type Event struct {
CreatedBy CreatedBy
UpdatedBy UpdatedBy
}
gorm should now know how to disambiguate the two columns.
Of course if you're only going to embed By into CreatedBy and UpdatedBy for the purposes of column mapping then you shouldn't need to declare new structs:
type By struct {
ByID sql.NullInt64
By *User
}
type Event struct {
CreatedBy By
UpdatedBy By
}
Related
I understand on golang we have public and private fields
package main
type User struct {
DisplayName string
title *string
}
Displayname is public so I can read it from another package. title is private I cannot read direclty
what about I add a public method like this
package main
type User struct {
DisplayName string
title *string
}
func (user *User) PublicTitle() string {
return user.title
}
type EmployeeUser User
So I should be able to read title by localUser.PublicTitle() in another package?
package utility
var localUser *main.EmployeeUser
localUser.PublicTitle()
I have tried it seems not working. I am a bit confused.
Thanks for help
The type EmployeeUser is a new type. When you define a new type based on an existing one, the methods of base type are not promoted to the new type.
To do that, you have to embed:
type EmployeeUser struct {
User
}
I am using golang fiber and GORM. I am trying to store the data from DTO. For example, I have created a post entity and post dto like below.
type User struct {
gorm.Model
Name string
Address string
Phone string
}
This is my DTO
type PostDto struct {
Name string `json:"name"`
Address string `json:"address"`
Phone string `json:"phone"`
}
I am trying to store the data like below
var postDto models.PostDto
c.BodyParser(&postDto)
err := database.DB.Model(models.User{}).Create(&postDto).Error
fmt.Println(err.Error())
But I am getting the below error
panic: reflect: call of reflect.Value.Field on string Value
Could anyone help to resolve this issue?
Thanks in advance
I don't know what you are doing? this is go language, not java, why do you define two structs?
You can use tag to complete that you want.
type User struct {
gorm.Model `json:"-" gorm:"-"`
Name string `json:"name" gorm:"column:name"`
Address string `json:"address" gorm:"column:address"`
Phone string `json:"phone" gorm:"column:phone"`
}
var user User
err := database.DB.Model(User{}).Create(&user).Error
fmt.Println(err.Error())
User and PostDto structures are not same, using one model to get and save data from another is wrong.
Either create a function to convert from PostDto to User, adn use its output in Create
func (postDTo PostDTo) ToUser() *User {
return &User{Name: postDTo.Name, Address: postDTo.Address, Phone: postDTo.Phone}
}
OR, as User and PostDto structs are so similar, add json ignore to gorm.Model in User and use it instead of PostDto
type User struct {
gorm.Model `json:"-"`
Name string `json:"name"`
Address string `json:"address"`
Phone string `json:"phone"`
}
var postData models.User
c.BodyParser(&postData)
I receive this error
controllers/users.go:61:36: user.ID undefined (type models.User has no field or method ID)
when using
var user models.User
...
jwtToken, err := generateJWT(user.ID, user.Username)
The definition of User model:
package models
import "time"
type BaseModel struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
}
type User struct {
BaseModel BaseModel `gorm:"embedded"`
Username string `json:"username"`
Password string `json:"password"`
}
Actually I put BaseModel in different file but in the same package with User. Migration works fine as table users have all columns in BaseModel. What is the problem? I use golang 1.18 and latest version of GORM
You have used BaseModel as attribute in your model so even though gorm can very well map it to the table column to access it, you have to use the attribute name
To access you would do
jwtToken, err := generateJWT(user.BaseModel.ID, user.Username)
you could also try this next code to see if it works otherwise the above will work for sure
type BaseModel struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
}
type User struct {
BaseModel `gorm:"embedded"`
Username string `json:"username"`
Password string `json:"password"`
}
now you might be able to access it like your original pattern
jwtToken, err := generateJWT(user.ID, user.Username)
tl;dr
How can I dynamize the JSON fields of a struct?
I am using gorm as the ORM. I have my model definition as a struct:
type UserAccessToken struct {
Id uint `gorm:"primaryKey" json:"-"`
CreatedAt time.Time `json:"createdAt"`
DeletedAt gorm.DeletedAt `json:"deletedAt"`
User User `gorm:"constraint:OnDelete:CASCADE" json:"-"`
UserId uint `gorm:"not null" json:"-"`
IpAddress string `gorm:"not null" json:"ipAddress"`
Token string `gorm:"not null" json:"token"`
}
I have multiple endpoints and I want to use different fields of the struct serialized on each endpoint.
For example, on POST /user-access-tokens/ user will be basic authenticated and a new access token will be created and returned on the response. However, on GET /user-access-tokens/ list API endpoint, the response will be a list of the UserAccessTokens, but the Token won't be on the response. And I am planning to create another endpoint only to be used by the administration interface, and all fields will be available there.
There are two I can think of:
Creating a different struct and reading from the database into this struct.
Adding a method like Map(scenario string) to the model struct which will create a map of the struct according to scenario, but in this case, I also need to add function to handle multiple instances of the struct.
What is the recommended way for this?
You can use another struct:
type UserAccessstruct {
Id uint `gorm:"primaryKey" json:"-"`
CreatedAt time.Time `json:"createdAt"`
DeletedAt gorm.DeletedAt `json:"deletedAt"`
User User `gorm:"constraint:OnDelete:CASCADE" json:"-"`
UserId uint `gorm:"not null" json:"-"`
IpAddress string `gorm:"not null" json:"ipAddress"`
}
type UserAccessToken {
UserAccessstruct
Token string `gorm:"not null" json:"token"`
}
func Get() {
var userAccess UserAccessToken
db.Select(selectQuery).First(&userAccess)
return userAccess // Full return
return userAccess.UserAccessstruct // Inner struct without token
}
So gorm.Model provides some base properties or fields:
ID uint `json:"-" gorm:"primary_key"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
DeletedAt *time.Time `json:"-" sql:"index"`
and you can use it as so
type User struct {
gorm.Model
Name string
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // set field size to 255
}
So when I was working on my Model Controller(s) for delete (or anything where I needed to compare the ID)
This does not work, give me an error:
c.Ctx.DB.Delete(&models.Address{ID: id})
unknown field 'ID' in struct literal of type
github.com/NlaakStudios/PASIT/models".Address
And, this does not work, give me an error:
c.Ctx.DB.Delete(&models.Address{gorm.Model.ID: id})
invalid field name gorm.Model.ID in struct initializer id int
If I remove the gorm.Model and define the field myself in each model ... it works.
type User struct {
ID uint `json:"-" gorm:"primary_key"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
DeletedAt *time.Time `json:"-" sql:"index"`
Name string
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // set field size to 255
}
How do I access those four base fields?
You're very close in your last example. You can remove the gorm.Model inheritance from your struct if you want/need (I personally do that for clarity), but to access that value you'd just need to build up your struct a little more. For example...
type Address struct {
gorm.Model
Name string
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // set field size to 255
}
c.Ctx.DB.Delete(&models.Address{gorm.Model: gorm.Model{ID: id}})
Give that a try and see if that works for you. Alternatively revert to your method without inheriting the gorm.Model
I worked with this: removing package Name 'gorm'
c.Ctx.DB.Delete(&models.Address{Model: gorm.Model{ID: id}})
looks like this is an interesting question - but of category 'selfmade problem':
what about
c.Ctx.DB.Delete(&model.Address{}, id)
then you can keep the benefits (or not - depends on taste) of gorm.Model