How can i add enum in gorm?

Viewed 7899

I am writing PostgreSQL table schema.

type TestTable struct {
    ID        int    `gorm:"column:id;primaryKey;autoIncrement"`
    CarType   string `gorm:"column:car_type"`
}

So how can i add car types like "SEDAN", "HATCHBACK", "MINIVAN" as enum data type

5 Answers

Assuming you are using GORM. First in your database create a type.

CREATE TYPE car_type AS ENUM (
    'SEDAN',
    'HATCHBACK',
    'MINIVAN');

Then you will need to define the following model:

import "database/sql/driver"

type carType string

const (
    SEDAN  carType = "SEDAN"
    HATCHBACK carType = "HATCHBACK"
    MINIVAN carType = "MINIVAN"
)

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

func (ct carType) Value() (driver.Value, error) {
    return string(ct), nil
}

type MyTable struct {
    gorm.Model
    CarType carType `sql:"car_type"`
}

func (MyTable) TableName() string {
    return "my_table"
}

on a side note- if you decide to go with slightly different approach: you can define your enums as int, and leverage iota. then you can use code generator to create sql Scaner/Valuer but also json/text representations. for example: https://github.com/dmarkham/enumer

in order to extend Nick's answer I am adding the following for automation:

Assuming you have DBClient struct, you can create a method that creates this car type:

func (psqlClient *DBClient) CreateCarTypeEnum() error {
    result := psqlClient.db.Exec("SELECT 1 FROM pg_type WHERE typname = 'car_type';")

    switch {
    case result.RowsAffected == 0:
        if err := psqlClient.db.Exec("CREATE TYPE car_type AS ENUM ('SEDAN', 'HATCHBACK', 'MINIVAN');").Error; err != nil {
            log.Error().Err(err).Msg("Error creating car_type ENUM")
            return err
        }

        return nil
    case result.Error != nil:
        return result.Error

    default:
        return nil
    }
}

An update, it will not work with sql:"car_type" instead use gorm:"car_type". That too once you have manually created the custom enum type into the database.

Here's an answer that doesn't require you to create a SQL type beforehand. My source for this solution was this github issue

For your field tag, use this:

type TestTable struct {
    ID        int    `gorm:"column:id;primaryKey;autoIncrement"`
    CarType   string `sql:"type:ENUM('SEDAN', 'HATCHBACK', 'MINIVAN')" gorm:"column:car_type"`
}

You'll also need to add the Scan and Value methods attached to your carType type.

type carType string

const (
    SEDAN carType = "SEDAN"
    HATCHBACK carType = "HATCHBACK"
    MINIVAN carType = "MINIVAN"
)

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

func (self carType) Value() (driver.Value, error) {
    return string(self), nil
}

I would also suggest renaming the Go carType to just car or style but I suppose that's outside the scope of this answer.

I ran into this recently. I'm hopeful that this syntax still works with modern gorm versions, since I'm using an older version and this works for me.

In my opinion any answer that requires you to write a custom migration and execute a SQL command should be unacceptable. This should be gorm's responsibility.

Related