mysqlgoormgo-gorm

Gorm append in association mode fails my validation checks even when both the objects exist


enter image description here

I am using the GORM ORM library for Golang to model the above relation. Here is the model code for the same:

package models

import (
    "errors"
    "fmt"
    "regexp"

    "gorm.io/gorm"
)

type User struct {
    ID               uint              `gorm:"primaryKey;autoIncrement" json:"id"`
    FirstName        string            `gorm:"column:first_name" json:"first_name"`
    LastName         string            `gorm:"column:last_name" json:"last_name"`
    Email            string            `gorm:"column:email" json:"email"`
    Bio              string            `gorm:"column:bio" json:"bio"`
    Blogs            []Blog            `gorm:"foreignKey:author_id"`
    BlogReactions    []BlogReaction    `gorm:"foreignKey:user_id"`
    CommentReactions []CommentReaction `gorm:"foreignKey:user_id"`
    Comments         []Comment         `gorm:"foreignKey:user_id"`
    Followers        []User            `gorm:"many2many:has_followers"`
}

func (*User) TableName() string {
    return "user"
}

func (u *User) validate() (err error) {

    fmt.Println(u)

    if u.FirstName == "" {
        return errors.New("first name cannot be empty")
    }

    emailMatched, _ := regexp.MatchString("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", u.Email)

    if !emailMatched {
        return errors.New("invalid email format")
    }

    if len(u.Bio) > 200 {
        return errors.New("bio too long")
    }

    return
}

func (u *User) BeforeSave(tx *gorm.DB) (err error) {
    validationErr := u.validate()
    return validationErr
}

I have a validation check before save. When I try to append a new follower:

    dbErr := database.DB.
        Model(&models.User{ID: uint(id)}).
        Association("Followers").
        Append(&models.User{ID: uint(follower_id)})

I get the validation error since the object has no information other than the ID:

first name cannot be empty

If the primary key is present in the object fed into Model() shouldn't the other fields auto populate? How can I resolve this?


Solution

  • I solved it, the solution was to create 2 User instances and populate them with the user and follower data using the First. Here's the complete endpoint function:

    func AddFollower(w http.ResponseWriter, r *http.Request) {
        id, parseIDErr := strconv.ParseUint(mux.Vars(r)["id"], 10, 64)
        follower_id, parseFollowerIDErr := strconv.ParseUint(mux.Vars(r)["follower_id"], 10, 64)
    
        //These 2 instances must be fully populated before the append operation
        var user models.User
        var follower models.User
    
        if parseIDErr != nil || parseFollowerIDErr != nil {
            w.WriteHeader(http.StatusBadRequest)
            return
        }
    
        tx := database.DB.Begin()
    
        //Find the user
        if err := tx.First(&user, id).Error; err != nil {
            utilities.HandleDBError(err, r.URL.String(), w, "user")
            return
        }
        
        // Find the follower
        if err := tx.First(&follower, follower_id).Error; err != nil {
            utilities.HandleDBError(err, r.URL.String(), w, "user")
            return
        }
    
    
        // Here the validation shouldn't fire since all fields are populated
        if err := tx.
            Model(&user).
            Association("Followers").
            Append(&follower); err != nil {
            tx.Rollback()
            utilities.HandleDBError(err, r.URL.String(), w, "user")
            return
        }
    
        if err := tx.Commit().Error; err != nil {
            utilities.HandleDBError(err, r.URL.String(), w, "user")
            return
        }
    
        w.WriteHeader(http.StatusCreated)
    }