Search code examples
mysqlgomodelgo-gorm

How to pass dynamic table name in gorm model


I am using Gorm ORM for my current application. I have one model correspondents to many tables with identical table structures(i.e. column name and type). So my requirement how can I change the table name dynamically while doing the query.

For e.g.

I have product model like Product.go

type Product struct{
  ID int
  Name strig
  Quantity int
}

And we have different products like shirts, jeans and so on and we have same tables like shirts, jeans.

Now I wanted to query the product as per name of the product how can we do that also want to have table created through migrations. But there is only One model than how can we run use Automigrate feature with Gorm.


Solution

  • Updated for GORM v2

    DEPRECATED: TableName will not allow dynamic table name anymore, its result will be cached for future uses.

    There is a much easier way to create several tables using the same struct:

    // Create table `shirts` & `jeans` with the same fields as in struct Product
    db.Table("shirts").AutoMigrate(&Product{})
    db.Table("jeans").AutoMigrate(&Product{})
    
    // Query data from those tables
    var shirts []Product
    var jeans []Product
    db.Table("shirts").Find(&shirts)
    db.Table("jeans").Where("quantity > 0").Find(&shirts)
    

    But, now on the second thought, I would suggest using the embedded struct so that you won't have to call Table in every query and you can also have additional fields per model while still sharing the same table structure.

    type ProductBase struct {
      ID int
      Name strig
      Quantity int
    }
    
    type Shirt struct {
      ProductBase
      NeckType string
    }
    
    type Jean struct {
      ProductBase
      Ripped bool
    }
    
    db.AutoMigrate(&Shirt{}, &Jean{})
    
    shirt, jeans := Shirt{}, make([]Jean, 0)
    db.Where("neck_type = ?", "Mandarin Collar").Last(&shirt)
    db.Where("ripped").Find(&jeans)
    

    Old answer for GORM v1

    You're almost there by using table field inside the struct:

    type Product struct{
      ID int
      Name strig
      Quantity int
    
      // private field, ignored from gorm
      table string `gorm:"-"`
    }
    
    func (p Product) TableName() string {
      // double check here, make sure the table does exist!!
      if p.table != "" {
        return p.table
      }
      return "products" // default table name
    }
    
    // for the AutoMigrate
    db.AutoMigrate(&Product{table: "jeans"}, &Product{table: "skirts"}, &Product{})
    
    // to do the query
    prod := Product{table: "jeans"}
    db.Where("quantity > 0").First(&prod)
    

    Unfortunately, that does not work with db.Find() when you need to query for multiple records... The workaround is to specify your table before doing the query

    prods := []*Product{}
    db.Table("jeans").Where("quantity > 0").Find(&prods)