Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use reflection type for gorm to generalize an api function

Tags:

go

go-gorm

I'm developping an API with lots of models using go-gorm. So far for the "GetAll" function, i've been using the following (simplified) :

func GetAllUsers(w,r){
//... get params and do some check 
pages := []*models.User{} //<-main problem I have to give the full type 
models.GetDB().Model(&models.User{}).Offset(offset).Limit(pagesize).Find(&pages)
//do the http response(w,pages)
}

But I'd like to avoid copying/pasting the function to just modify the type (here models.User) to get all models from db.

I did try different approach using reflect or interface but none seems to work like :

func GenericGetAll(w http.ResponseWriter, r *http.Request, data interface{}) {
    dtype := reflect.TypeOf(data)
    pages := reflect.New(reflect.SliceOf(dtype)).Elem().Interface()
    log.Printf("%+v", pages)
    //reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(data)), 0, 20)
    //new([]gorm.Model)
    GetDB().LogMode(true).Model(data).Offset(offset).Limit(pagesize).Find(&pages)

utils.Respond(w, pages)

}

So I could use this directly in my handlers :

GenericGetAll(w,r,&models.User{}) for user
GenericGetAll(w,r,&models.Product{}) for Product
GenericGetAll(w,r,&models.Company{}) for Company

This would be cleaner than copying/pasting the other function just to modify the type ...

But using those different approach give problems :

  • []*interface{}, I lose the table name and get a sql error because tablename '' doesn't exist
  • new([]gorm.Model) it lose my tablename and use gorm.model default tablename :/
  • reflect.New(reflect.SliceOf(dtype)).Elem().Interface() and I get an error : unsupported destination, should be slice or struct evenif it did create the slice :/ the log.printf() give an empty array []

How should I use reflect to get a slice from the type in argument of the function ?

Is this only doable with Golang ? I know that polymorphism doesn't exist in golang but ... this is a required features or is there something magic I don't know about golang ? ^^.

Thanks

like image 674
LZR Avatar asked Sep 11 '19 16:09

LZR


1 Answers

I found the solution !!

func GenericGetAll(w http.ResponseWriter, r *http.Request, data interface{}) {
    dtype := reflect.TypeOf(data)
    pages := reflect.New(reflect.SliceOf(dtype)).Interface()
    offset, pagesize, order := GetAllFromDb(r)
    err := error(nil)
    if offset <= 0 && pagesize <= 0 {
        err = errors.New(order)
    }

    if order != "" {
        err = GetDB().LogMode(true).Model(data).Order(order).Offset(offset).Limit(pagesize).Find(pages).Error
    } else {
        err = GetDB().LogMode(true).Model(data).Offset(offset).Limit(pagesize).Find(pages).Error
    }
    if err != nil {
        utils.Respond(w, utils.Message(false, fmt.Sprintf("Error while retrieving data")))
        return
    }
    resp := utils.Message(true, "data returned")
    resp["data"] = pages
    utils.Respond(w, resp)

}

Instead of getting an interface of an Elem()

reflect.New(reflect.SliceOf(dtype)).Elem().Interface()

Which return []*models.User

The solution was to use directly an Interface :

pages := reflect.New(reflect.SliceOf(dtype)).Interface()

Which return *[]*models.User that is accepted by gorm's interface :)

like image 159
LZR Avatar answered Sep 28 '22 10:09

LZR