How can I change the fields of a struct's json marshal? - go

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
}

Related

Forbid extra keys in request body of gin golang

I want to do a validation for sending the extra keys in the JSON body.
My Model
type User struct {
gorm.Model
Username string `json:"username" validate:"required" gorm:"unique"`
Email string `json:"email" validate:"required,email" gorm:"unique"`
Password string `json:"password" validate:"required" `
}
Now if someone sends an id:111 in the request body then it gets save into db. I want to stop that only.
Validator that I'm using is "github.com/go-playground/validator/v10" but it's not working
validate := validator.New()
err := validate.Struct(user)
The go-playground/validator package you're using won't validate unknown fields in the JSON body by default. To validate only the fields in the struct, you can use the omitempty tag:
type User struct {
gorm.Model
Username string `json:"username" validate:"required" gorm:"unique"`
Email string `json:"email" validate:"required,email" gorm:"unique"`
Password string `json:"password" validate:"required" `
ID int `json:"id,omitempty" validate:"-"`
}
The omitempty tag means that the ID field won't be marshaled into JSON if its value is zero-value (0 for int). The validate:"-" tag means that the field won't be validated.

How do I store the data from DTO Gorm

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)

GORM embedded struct not working correctly

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)

How to init and insert struct with foreign key constraint into db using GORM

I am trying to create a rest API with golang. Each time a user is created, I would like to create a 'Profile' which is associated with that user.
My initial thought was to first create the user, and then separately create the profile referencing the user ID and inserting that into the database.
I'm not sure if this kind of thinking aligns with the way go should be used as i'm just starting with the language.
Using below code, I create the user, but cannot create the profile. I receive this error: using unaddressable value
var db *gorm.DB
func GetDB() *gorm.DB {
return db
}
type User struct {
gorm.Model
Email string `gorm:"type:varchar(100);unique_index"`
Password string `json:"password"`
Name string `json:"name"`
Token string `json:"token";sql:"-"`
}
type Profile struct {
gorm.Model
User User `gorm:"foreignkey:UserRefer"` // use UserRefer as foreign key
UserRefer uint
FirstName string `gorm:"default:'John'"`
LastName string `gorm:"default:'Doe'"`
Description string `gorm:"default:'Mysterious'"`
}
func (user *User) Create() (map[string]interface{}) {
if resp, ok := user.Validate(); !ok {
return resp
}
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
user.Password = string(hashedPassword)
GetDB().Create(user)
profile := Profile{}
profile.UserRefer = user.ID
GetDB().Create(profile)
if user.ID <= 0 {
return u.Message(false, "Failed to create account, connection error.")
}
response := u.Message(true, "Account has been created")
response["user"] = user
return response
}
I'm hoping someone will be able to help me understand what is going wrong here?
You should pass a pointer on gorm's Create method to get your model Filled after creation...
GetDB().Create(&profile)
But, as is showed in https://gorm.io/docs/associations.html gorm Auto create the associations.
You can change your models, like so... (I am supposing that User and Profile has a 1..1 relation ok?)
And everything will get done automatically
type User struct {
gorm.Model
Email string `gorm:"type:varchar(100);unique_index"`
Password string `json:"password"`
Name string `json:"name"`
Token string `json:"token";sql:"-"`
Profile Profile
ProfileID uint
}
type Profile struct {
gorm.Model
UserID uint
FirstName string `gorm:"default:'John'"`
LastName string `gorm:"default:'Doe'"`
Description string `gorm:"default:'Mysterious'"`
}

How to access gorm.Model.ID?

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

Resources