如何在Gorm中支持枚举值

huangapple go评论76阅读模式
英文:

How to support Enum value in Gorm

问题

你好,我是你的中文翻译助手。根据你提供的信息,你在使用Go和Gorm构建一个简单的Web服务,使用Postgres作为数据库。你遇到了一个问题,无法将模型支持枚举值。经过一些研究,你找到了解决方法,但在查找在线资源时遇到了新的错误,无法修复。

每当你尝试从数据库中获取用户时,会出现以下错误:

 panic: interface conversion: interface {} is string, not []uint8
 
 -> userservice/models.(*RoleAllowed).Scan
 ->   /Users/Cipher/work/frontendlabs/shopvending/userservice/models/user.model.go:22

    database/sql.convertAssignRows
      /usr/local/go/src/database/sql/convert.go:385
    database/sql.convertAssignRows
      /usr/local/go/src/database/sql/convert.go:428
    database/sql.(*Rows).Scan
      /usr/local/go/src/database/sql/sql.go:3287
    gorm.io/gorm.(*DB).scanIntoStruct
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/scan.go:67
    gorm.io/gorm.Scan
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/scan.go:293
    gorm.io/gorm/callbacks.Query
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/callbacks/query.go:26
    gorm.io/gorm.(*processor).Execute
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/callbacks.go:130
    gorm.io/gorm.(*DB).First
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/finisher_api.go:129
    userservice/services.UserService.FindUserByEmail
      /Users/Cipher/work/frontendlabs/shopvending/userservice/services/user.service.go:25
    userservice/handlers.ManualLogin
      /Users/Cipher/work/frontendlabs/shopvending/userservice/handlers/auth.handler.go:24
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5.(*Mux).routeHTTP
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:442
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5.(*Mux).ServeHTTP
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:71
    github.com/go-chi/chi/v5.(*Mux).Mount.func1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:314
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5.(*Mux).routeHTTP
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:442
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.Timeout.func1.1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/timeout.go:45
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    userservice/middlewares.CommonMiddleware.func1
      /Users/Cipher/work/frontendlabs/shopvending/userservice/middlewares/contentType.go:8
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.Recoverer.func1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/recoverer.go:38
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.RequestLogger.func1.1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/logger.go:57
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.RealIP.func1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/realip.go:35
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.RequestID.func1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/request_id.go:76
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5.(*Mux).ServeHTTP
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:88
    net/http.serverHandler.ServeHTTP
      /usr/local/go/src/net/http/server.go:2947
    net/http.(*conn).serve
      /usr/local/go/src/net/http/server.go:1991
    created by net/http.(*Server).Serve
      /usr/local/go/src/net/http/server.go:3102

你尝试将字节转换为字符串,但仍然出现错误。

你的用户服务文件如下:

package services

import (
	"github.com/sirupsen/logrus"
	"userservice/database"
	"userservice/dto"
	"userservice/models"
)

type UserService struct{}

func (u UserService) FindUserByEmail(email string) *models.User {
	var user models.User
	if err := database.Instance.Where("email = ?", email).First(&user); err.Error != nil {
		logrus.Errorf("Can not find user with email %s", err.Error)
		return nil

	}
	return &user

}

func (u UserService) CreateUser(newUser dto.CreateNewUserDTO) *models.User {
	createNewUser := models.User{
		Email:    newUser.Email,
		Password: newUser.Password,
	}
	createNewUser.HashPassword(createNewUser.Password)
	err := database.Instance.Create(&createNewUser)
	if err.Error != nil {
		logrus.Errorf("Error saving food to the database: %s", err.Error)
		return nil
	}

	return &createNewUser

}

问题出现在调用"FindUserByEmail"时,出现了无法序列化的错误。

你的用户模型文件如下:

package models

import (
	"database/sql/driver"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"time"
)

type RoleAllowed string

const (
	admin   RoleAllowed = "admin"
	seller  RoleAllowed = "seller"
	rider   RoleAllowed = "rider"
	regular RoleAllowed = "regular"
)

func (st *RoleAllowed) Scan(value interface{}) error {
	*st = RoleAllowed(value.([]byte))
	return nil
}

func (st RoleAllowed) Value() (driver.Value, error) {
	return string(st), nil
}

type StatusAllowed string

const (
	suspended StatusAllowed = "suspended"
	active    StatusAllowed = "active"
	inactive  StatusAllowed = "inactive"
)

func (st *StatusAllowed) Scan(value interface{}) error {
	*st = StatusAllowed(value.([]byte))
	return nil
}

func (st StatusAllowed) Value() (driver.Value, error) {
	return string(st), nil
}

type User struct {
	ID              uint           `json:"id" gorm:"type:bigserial;primaryKey;autoIncrement"`
	Email           string         `json:"email" gorm:"type:varchar(255);unique;not null"`
	Password        string         `json:"password" gorm:"type:varchar(255);unique;not null"`
	OTPCode         int            `json:"otp_code"`
	Role            RoleAllowed    `json:"role" gorm:"type:role_allowed;default:'regular'"`
	Status          StatusAllowed  `json:"status" gorm:"type:status_allowed;default:'active'"`
	IsEmailVerified bool           `json:"isEmailVerified" gorm:"default:false"`
	OTPExpireTime   time.Time      `json:"otp_expire_time"`
	CreatedAt       time.Time      `json:"createdAt"`
	UpdatedAt       time.Time      `json:"updatedAt"`
	DeletedAt       gorm.DeletedAt `gorm:"index"`
}

func (u *User) HashPassword(password string) error {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
	if err != nil {
		return err
	}
	u.Password = string(bytes)
	return nil
}

func (u *User) CheckPasswordHash(password string) error {
	err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
	if err != nil {
		return err
	}
	return nil
}

请问你需要如何解决这个问题?

英文:

Good day guys, I am new to go and Gorm, I am building a simple web service using Postgres, I had an issue getting my model to support enum values, after some research I found a way to to do it. But I began to get new errors which I can't seem to fix after researching resources online.

whenever I try to fetch a user from the database I get this error:

 panic: interface conversion: interface {} is string, not []uint8
 
 -> userservice/models.(*RoleAllowed).Scan
 ->   /Users/Cipher/work/frontendlabs/shopvending/userservice/models/user.model.go:22

    database/sql.convertAssignRows
      /usr/local/go/src/database/sql/convert.go:385
    database/sql.convertAssignRows
      /usr/local/go/src/database/sql/convert.go:428
    database/sql.(*Rows).Scan
      /usr/local/go/src/database/sql/sql.go:3287
    gorm.io/gorm.(*DB).scanIntoStruct
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/scan.go:67
    gorm.io/gorm.Scan
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/scan.go:293
    gorm.io/gorm/callbacks.Query
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/callbacks/query.go:26
    gorm.io/gorm.(*processor).Execute
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/callbacks.go:130
    gorm.io/gorm.(*DB).First
      /Users/Cipher/go/pkg/mod/gorm.io/gorm@v1.23.8/finisher_api.go:129
    userservice/services.UserService.FindUserByEmail
      /Users/Cipher/work/frontendlabs/shopvending/userservice/services/user.service.go:25
    userservice/handlers.ManualLogin
      /Users/Cipher/work/frontendlabs/shopvending/userservice/handlers/auth.handler.go:24
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5.(*Mux).routeHTTP
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:442
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5.(*Mux).ServeHTTP
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:71
    github.com/go-chi/chi/v5.(*Mux).Mount.func1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:314
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5.(*Mux).routeHTTP
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:442
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.Timeout.func1.1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/timeout.go:45
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    userservice/middlewares.CommonMiddleware.func1
      /Users/Cipher/work/frontendlabs/shopvending/userservice/middlewares/contentType.go:8
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.Recoverer.func1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/recoverer.go:38
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.RequestLogger.func1.1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/logger.go:57
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.RealIP.func1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/realip.go:35
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5/middleware.RequestID.func1
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/middleware/request_id.go:76
    net/http.HandlerFunc.ServeHTTP
      /usr/local/go/src/net/http/server.go:2109
    github.com/go-chi/chi/v5.(*Mux).ServeHTTP
      /Users/Cipher/go/pkg/mod/github.com/go-chi/chi/v5@v5.0.7/mux.go:88
    net/http.serverHandler.ServeHTTP
      /usr/local/go/src/net/http/server.go:2947
    net/http.(*conn).serve
      /usr/local/go/src/net/http/server.go:1991
    created by net/http.(*Server).Serve
      /usr/local/go/src/net/http/server.go:3102

I have tried to convert the byte to string, I still get the error.

my user service file:

package services

import (
	"github.com/sirupsen/logrus"
	"userservice/database"
	"userservice/dto"
	"userservice/models"
)

type UserService struct{}

//func (u UserService) FindUserById(id int) *models.User {
//	user := u.User
//	if err := database.Instance.Where("id = ?", id).First(&user); err != nil {
//		logrus.Errorf("Can not find user with id %d", id)
//		return nil
//
//	}
//	return user
//
//}

func (u UserService) FindUserByEmail(email string) *models.User {
	var user models.User
	if err := database.Instance.Where("email = ?", email).First(&user); err.Error != nil {
		logrus.Errorf("Can not find user with email %s", err.Error)
		return nil

	}
	return &user

}

func (u UserService) CreateUser(newUser dto.CreateNewUserDTO) *models.User {
	createNewUser := models.User{
		Email:    newUser.Email,
		Password: newUser.Password,
	}
	createNewUser.HashPassword(createNewUser.Password)
	err := database.Instance.Create(&createNewUser)
	if err.Error != nil {
		logrus.Errorf("Error saving food to the database: %s", err.Error)
		return nil
	}

	return &createNewUser

}

the issue is when I call "FindUserByEmail" for some reason it can't serialise.

it was working, until I introduced the enum value integration. now I get the error above.

my user model file:

package models

import (
	"database/sql/driver"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"time"
)

type RoleAllowed string

const (
	admin   RoleAllowed = "admin"
	seller  RoleAllowed = "seller"
	rider   RoleAllowed = "rider"
	regular RoleAllowed = "regular"
)

func (st *RoleAllowed) Scan(value interface{}) error {
	*st = RoleAllowed(value.([]byte))
	return nil
}

func (st RoleAllowed) Value() (driver.Value, error) {
	return string(st), nil
}

type StatusAllowed string

const (
	suspended StatusAllowed = "suspended"
	active    StatusAllowed = "active"
	inactive  StatusAllowed = "inactive"
)

func (st *StatusAllowed) Scan(value interface{}) error {
	*st = StatusAllowed(value.([]byte))
	return nil
}

func (st StatusAllowed) Value() (driver.Value, error) {
	return string(st), nil
}

type User struct {
	ID              uint           `json:"id" gorm:"type:bigserial;primaryKey;autoIncrement"`
	Email           string         `json:"email" gorm:"type:varchar(255);unique;not null"`
	Password        string         `json:"password" gorm:"type:varchar(255);unique;not null""`
	OTPCode         int            `json:"otp_code"`
	Role            RoleAllowed    `json:"role" gorm:"type:role_allowed;default:'regular'"`
	Status          StatusAllowed  `json:"status" gorm:"type:status_allowed;default:'active'"`
	IsEmailVerified bool           `json:"isEmailVerified" gorm:"default:false"`
	OTPExpireTime   time.Time      `json:"otp_expire_time"`
	CreatedAt       time.Time      `json:"createdAt"`
	UpdatedAt       time.Time      `json:"updatedAt"`
	DeletedAt       gorm.DeletedAt `gorm:"index"`
}

func (u *User) HashPassword(password string) error {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
	if err != nil {
		return err
	}
	u.Password = string(bytes)
	return nil
}

func (u *User) CheckPasswordHash(password string) error {
	err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
	if err != nil {
		return err
	}
	return nil
}

please any assistance on how to resolve this issue?

答案1

得分: 2

我编辑了下面的代码:

package models

import (
	"database/sql/driver"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"time"
)

type RoleAllowed string

const (
	admin   RoleAllowed = "admin"
	seller  RoleAllowed = "seller"
	rider   RoleAllowed = "rider"
	regular RoleAllowed = "regular"
)

func (st *RoleAllowed) Scan(value interface{}) error {
	b, ok := value.([]byte)
	if !ok {
		*st = RoleAllowed(b)
	}
	return nil
}

func (st RoleAllowed) Value() (driver.Value, error) {
	return string(st), nil
}

type StatusAllowed string

const (
	suspended StatusAllowed = "suspended"
	active    StatusAllowed = "active"
	inactive  StatusAllowed = "inactive"
)

func (st *StatusAllowed) Scan(value interface{}) error {
	b, ok := value.([]byte)
	if !ok {
		*st = StatusAllowed(b)
	}
	return nil
}

func (st StatusAllowed) Value() (driver.Value, error) {
	return string(st), nil
}

type User struct {
	ID              uint           `json:"id" gorm:"type:bigserial;primaryKey;autoIncrement"`
	Email           string         `json:"email" gorm:"type:varchar(255);unique;not null"`
	Password        string         `json:"password" gorm:"type:varchar(255);unique;not null"`
	OTPCode         int            `json:"otp_code"`
	Role            RoleAllowed    `json:"role" gorm:"type:role_allowed;default:'regular'"`
	Status          StatusAllowed  `json:"status" gorm:"type:status_allowed;default:'active'"`
	IsEmailVerified bool           `json:"isEmailVerified" gorm:"default:false"`
	OTPExpireTime   time.Time      `json:"otp_expire_time"`
	CreatedAt       time.Time      `json:"createdAt"`
	UpdatedAt       time.Time      `json:"updatedAt"`
	DeletedAt       gorm.DeletedAt `gorm:"index"`
}

func (u *User) HashPassword(password string) error {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
	if err != nil {
		return err
	}
	u.Password = string(bytes)
	return nil
}

func (u *User) CheckPasswordHash(password string) error {
	err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
	if err != nil {
		return err
	}
	return nil
}

我需要检查类型,然后分配我需要的正确值。谢谢 @mkopriva。

英文:

I edited my code below

package models
import (
"database/sql/driver"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"time"
)
type RoleAllowed string
const (
admin   RoleAllowed = "admin"
seller  RoleAllowed = "seller"
rider   RoleAllowed = "rider"
regular RoleAllowed = "regular"
)
func (st *RoleAllowed) Scan(value interface{}) error {
b, ok := value.([]byte)
if !ok {
*st = RoleAllowed(b)
}
return nil
}
func (st RoleAllowed) Value() (driver.Value, error) {
return string(st), nil
}
type StatusAllowed string
const (
suspended StatusAllowed = "suspended"
active    StatusAllowed = "active"
inactive  StatusAllowed = "inactive"
)
func (st *StatusAllowed) Scan(value interface{}) error {
b, ok := value.([]byte)
if !ok {
*st = StatusAllowed(b)
}
return nil
}
func (st StatusAllowed) Value() (driver.Value, error) {
return string(st), nil
}
type User struct {
ID              uint           `json:"id" gorm:"type:bigserial;primaryKey;autoIncrement"`
Email           string         `json:"email" gorm:"type:varchar(255);unique;not null"`
Password        string         `json:"password" gorm:"type:varchar(255);unique;not null""`
OTPCode         int            `json:"otp_code"`
Role            RoleAllowed    `json:"role" gorm:"type:role_allowed;default:'regular'"`
Status          StatusAllowed  `json:"status" gorm:"type:status_allowed;default:'active'"`
IsEmailVerified bool           `json:"isEmailVerified" gorm:"default:false"`
OTPExpireTime   time.Time      `json:"otp_expire_time"`
CreatedAt       time.Time      `json:"createdAt"`
UpdatedAt       time.Time      `json:"updatedAt"`
DeletedAt       gorm.DeletedAt `gorm:"index"`
}
func (u *User) HashPassword(password string) error {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
if err != nil {
return err
}
u.Password = string(bytes)
return nil
}
func (u *User) CheckPasswordHash(password string) error {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
if err != nil {
return err
}
return nil
}

I had to check the type then assign the right value I need. thanks @mkopriva

huangapple
  • 本文由 发表于 2022年8月27日 15:59:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/73509279.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定