How to add value before create using gorm? - go

I have this post.go model
package models
type Post struct {
Id uint `json:"ID"`
Name string `json:"Name"`
Message string `gorm:"type:text; index" json:"Message"`
Status string `gorm:"type:varchar(255); index" json:"Status"`
Desc string `gorm:"type:text; index" json:"Desc"`
}
func (p *Post) BeforeCreate() (err error) {
p.Status = "todo"
return nil
}
I need when I create any record by default put the status into the todo value
in my controller:
config.DB.Model(&models.Post{}).Create(&posts)
The result is I got a null value in status in the database

BeforeCreate interface signature is incorrect it should be BeforeCreate(*gorm.DB) error
func (p *Post) BeforeCreate(tx *gorm.DB) (err error) {
p.Status = "todo"
return nil
}
Another way would be to add default-values to the post struct
type Post struct {
Id uint `json:"ID"`
Name string `json:"Name"`
Message string `gorm:"type:text; index" json:"Message"`
Status string `gorm:"type:varchar(255); index; default: todo" json:"Status"`
Desc string `gorm:"type:text; index" json:"Desc"`
}
Output:
db.Create(&Post{}) // INSERT INTO `posts` (`name`,`message`,`status`,`desc`) VALUES ("","","todo","") RETURNING `id`

Related

Unable to scan type of uuid.UUID into UUID in sqlmock Golang

I have the following test function.
func (s *Suite) Test_delete() {
var (
id = uuid.New()
name = "test"
quantity = 2
)
s.mock.ExpectQuery(regexp.QuoteMeta(
`SELECT * FROM "items" WHERE code = $1 AND "items"."deleted_at" IS NULL`,
)).
WithArgs(id).
WillReturnRows(sqlmock.NewRows([]string{"code", "name", "quantity"}).
AddRow(id, name, quantity))
err := s.repository.DeleteItem(id.String())
require.NoError(s.T(), err)
}
Now the problem is trying to scan the id variable into the row for the column code. In my Product struct, I have code defined as follows.
type Item struct {
Code uuid.UUID `gorm:"type:uuid;primaryKey" json:"code"`
Name string `json:"name" validate:"required"`
Quantity int `json:"qty" validate:"required,min=0"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}
func (i *Item) BeforeCreate(tx *gorm.DB) (err error) {
if i.Code == uuid.Nil {
i.Code = uuid.New()
}
return
}
Now the problem is when I try to run the test function, it is somehow unable to scan uuid.UUID into UUID even though they are both the same types. Here is the exact error message.
msql: Scan error on column index 0, name "code": Scan: unable to scan type uuid.UUID into UUID
Could someone help me on this part?
Read this. I assume there is type casting error. Changing WillReturnRows(sqlmock.NewRows([]string{"code", "name", "quantity"}).AddRow(id, name, quantity))->WillReturnRows(sqlmock.NewRows([]string{"code", "name", "quantity"}).AddRow(id.String(), name, quantity)) may help.

How to skip Hook in one-to-many-association model?

I have two models and their BeforeSave() Hooks
type Store struct {
ID int `json:"id"`
Name string `json:"name"`
Products []*Product `json:"products"`
}
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
}
func (s *Store) BeforeSave(db *gorm.DB) error {
.....
return nil
}
func (p *Product) BeforeSave(db *gorm.DB) error{
......
return nil
}
When I insert a new Store into database using Gorm db.Create(&product), both Hooks are invoked. Is there any way to skip hook of Product (which means only invoke Store's BeforeSave())?

A foreign key constraint fails when insert to table with gorm

When I try to insert to a table with gorm which has one to many relationship, I get this error:
Error 1452: Cannot add or update a child row: a foreign key constraint fails (`Todo`.`todos`, CONSTRAINT `fk_users_todo_list` FOREIGN KEY (`fk_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE)....
These are my models:
type Base struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `gorm:"index"`
}
type User struct {
Base
FirstName string `form:"first_name" gorm:"type:varchar(20)"`
LastName string `form:"last_name" gorm:"type:varchar(20)"`
UserName string `form:"user_name" gorm:"unique;type:varchar(20)"`
Email string `form:"email" gorm:"type:varchar(100)"`
Password string `form:"password" gorm:"type:varchar(30)"`
Gender types.UserGender `form:"gender" gorm:"type:smallint"`
TodoList []Todo `gorm:"foreignKey:FkID;constraint:onDelete:SET NULL,onUpdate:CASCADE" json:"omitempty"`
}
type Todo struct {
Base
Name string
Description string
Status types.TaskStatus
FkID uint
}
And this is the function that I wrote:
func (d *DB) AddTODO(todo *models.Todo, userName string) error {
user := &models.User{UserName: userName}
err := d.db.Model(&user).Where("user_name = ?", userName).Association("TodoList").
Append([]models.Todo{*todo})
if err != nil {
return err
}
return nil
}
I'm using MariaDB.
It want user from gorm result. Change your method maybe like this:
// arg user is from gorm result somewhere
func (d *DB) AddTODO(user *models.User, todo *models.Todo) error {
return d.db.Model(user).Association("TodoList").Append([]models.Todo{*todo})
}
TodoList lack references keyword. It should be like this.
type User struct {
Base
FirstName string `form:"first_name" gorm:"type:varchar(20)"`
LastName string `form:"last_name" gorm:"type:varchar(20)"`
UserName string `form:"user_name" gorm:"unique;type:varchar(20)"`
Email string `form:"email" gorm:"type:varchar(100)"`
Password string `form:"password" gorm:"type:varchar(30)"`
Gender types.UserGender `form:"gender" gorm:"type:smallint"`
TodoList []Todo `gorm:"foreignKey:FkID;references:ID;constraint:onDelete:SET NULL,onUpdate:CASCADE" json:"omitempty"`
}

Custom Gorm preloading does not fetch data

I have a query that fetches rows from jobs table and its author (each job has an author), but I want to select specific fields.
type User struct {
ID uint `gorm:"primarykey" json:"-"`
UUID uuid.UUID `gorm:"type:uuid not null" json:"-"`
Email string `gorm:"type:varchar(255); not null" json:"email"`
Name string `gorm:"type:varchar(255); not null" json:"name"`
AvatarURL string `gorm:"type:varchar(255); not null" json:"avatar_url"`
Provider string `gorm:"type:varchar(255); not null" json:"provider"`
ProviderID string `gorm:"type:varchar(255); not null" json:"-"`
Jobs []Job `json:"-"`
}
type Job struct {
ID uint `gorm:"primarykey" json:"id"`
Title string `gorm:"type:varchar(255); not null" json:"title"`
Content string `gorm:"not null" json:"content"`
UserID uint `json:"-"`
User User `json:"author"`
}
func (jobRepo repository) FindAll() ([]entity.Job, error) {
var jobs []entity.Job
if dbc := jobRepo.db.Preload("User", func(db *gorm.DB) *gorm.DB {
return db.Select("Name", "Email")
}).Find(&jobs); dbc.Error != nil {
return nil, dbc.Error
}
return jobs, nil
}
The custom preload does not behave as desired. If I do not specify concrete fields, the query works and returns everything. Otherwise, If I specify some fields, it returns nothing.
This is because you didn't select primary key. Add "ID" into select clause:
func(db *gorm.DB) *gorm.DB {
return db.Select("ID", "Name", "Email")
}
Otherwise GORM doesn't know how to join users to jobs.

Marshal JSON field in database to struct using Pop

I'm using Go Buffalo's ORM Pop and want to store JSON in a field and be able to marshal it into a struct.
e.g.
schema.sql
CREATE TABLE tree (
id uuid PRIMARY KEY,
name text NOT NULL,
fruit json,
);
main.go
type Tree struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Fruit []Fruit `json:"fruit" db:"fruit"`
}
type Fruit struct {
ID int `json:"id"`
Name string `json:"name"`
}
I get the following error:
sql: converting argument $25 type: unsupported type []Fruit, a slice of struct
UPDATE
Based on feedback added the following methods:
type Fruits []Fruit
// Value implements the driver.Valuer interface
func (f Fruits) Value() (driver.Value, error) {
return json.Marshal(of)
}
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data []byte
if b, ok := value.([]byte); !ok {
data = b
return errors.New("type assertion to []byte for Fruit failed")
}
return json.Unmarshal(data, &of)
}
Now receive the error:
Cannot fetch from database: unable to fetch records: sql: Scan error on column index 26, name "fruit": unexpected end of JSON input
Update 2
Updated Scan method to the following and fixed error:
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data = []byte(value.([]uint8))
return json.Unmarshal(data, &of)
}
Based on the help provided by #mkopriva
You need to provide Value and Scan methods for your struct
// Tree tall tree with many fruit
type Tree struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Fruit []Fruit `json:"fruit" db:"fruit"`
}
// Fruit fruit found on a tree
type Fruit struct {
ID int `json:"id"`
Name string `json:"name"`
}
// Creates a splice of Fruit
type Fruits []Fruit
// Value implements the driver.Valuer interface
func (f Fruits) Value() (driver.Value, error) {
return json.Marshal(of)
}
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data = []byte(value.([]uint8))
return json.Unmarshal(data, &f)
}

Resources