Skip to content

GORM cheatsheet for Go

A handful of GORM snippets I keep around when I start a project in Go — installing, modeling, validations, many-to-many relations, hooks, and the queries I run most often.

2 min read
  • go
  • gorm
  • snippets

This article was originally written in 2021. The snippets still work with GORM v2; if you're on a newer release, double-check the official docs for any API drift.

A small grab-bag of GORM snippets I lean on across personal projects. The list grows whenever I find something worth keeping. Full reference at gorm.io.

Installing GORM

go get gorm.io/gorm

Updating GORM

Same as install, but with the -u flag:

go get -u gorm.io/gorm

A simple model

package models
 
import (
  "gorm.io/gorm"
)
 
type Article struct {
  gorm.Model
  Title string
}

Setting up tables and migrations (AutoMigrate)

package models
 
import (
  "fmt"
 
  "gorm.io/driver/postgres"
  "gorm.io/gorm"
)
 
type User struct {
  gorm.Model
  Username string
  Email    string `gorm:"unique" binding:"required"`
}
 
// dbDsn = postgresql://postgres_user:postgres_pass@database:5432/my-app
func SetupModels(dbDsn string) *gorm.DB {
  db, err := gorm.Open(postgres.Open(dbDsn), &gorm.Config{})
  if err != nil {
    fmt.Println(err)
    panic("Failed to load the database")
  }
 
  db.AutoMigrate(
    &User{},
  )
 
  return db
}

Basic CRUD

package main
 
import (
  "gorm.io/gorm"
)
 
type Article struct {
  gorm.Model
  Title string
}
 
// create
article := Article{ Title: "My new article" }
db.Create(&article)
 
// get all
articles := []Article{}
db.Find(&articles)
 
// get one
id := 1
db.First(&article, id)
 
// update
article.Title = "Article title updated!"
db.Save(&article)
 
// delete
db.Delete(&article)
db.Where("id = ?", "2").Delete(&article)

Field-level validations

Required:

package models
 
import (
  "gorm.io/gorm"
)
 
type Article struct {
  gorm.Model
  Title            string `binding:"required"`
  ShortDescription string `binding:"required"`
}

Unique:

package models
 
import (
  "gorm.io/gorm"
)
 
type User struct {
  gorm.Model
  Email string `gorm:"unique" binding:"required"`
}

Other field tags are documented here.

Many-to-many with mutual references

The join table will be called article_categories.

package models
 
import (
  "gorm.io/gorm"
)
 
type Article struct {
  gorm.Model
  Categories []*Category `gorm:"many2many:article_categories"`
}
 
type Category struct {
  gorm.Model
  Articles []*Article `gorm:"many2many:article_categories"`
}

Using the many-to-many model above, we can fetch articles along with their categories:

articles := []models.Article{}
db.Preload("Categories").Find(&articles)

Many-to-many create without auto-creating associations (Skip Auto Create/Update)

For many-to-many relations GORM upserts (updates or inserts) the related rows before writing the join row. To skip that step, use Omit:

article := models.Article{ Title: "My new article" }
category := models.Category{ ID: 1 } // already exists in the database
article.Categories = append(article.Categories, category)
 
r := db.Omit("Categories.*").Create(&article)

In this example the category with ID 1 is already in the database, so we don't want GORM to try creating it again. We just want to associate the new article with the existing category.

Get a record by id

Several ways. With First:

article := models.Article{}
 
db.First(&article, 10)
db.First(&article, "10")
db.First(&article, "id = ?", "10")

With Where:

article := models.Article{}
 
db.Where("id = ?", id).First(&article)

Ordering by a field

Sorting by created_at on the articles table:

articles := []models.Article{}
db.Order("created_at desc").Find(&articles)
db.Order("created_at asc").Find(&articles)

Counting rows

var count int64
db.Model(&models.Article{}).Count(&count)

Detecting "not found" (ErrRecordNotFound)

import (
  "errors"
  "fmt"
 
  "gorm.io/gorm"
)
 
name := "design"
category := models.Category{}
 
r := db.Where("name = ?", name).First(&category)
 
if r.Error != nil {
  if errors.Is(r.Error, gorm.ErrRecordNotFound) {
    fmt.Printf("category with name %s doesn't exist", name)
  }
}

Mutating a record before persisting it (BeforeCreate hook)

package models
 
import (
  "fmt"
 
  "github.com/gosimple/slug"
  "gorm.io/gorm"
)
 
type Category struct {
  gorm.Model
  Name string
  Slug string
  Url  string
}
 
func (c *Category) BeforeCreate(tx *gorm.DB) error {
  slg := slug.Make(c.Name)
  url := fmt.Sprintf("/%s/", slg)
 
  c.Slug = slg
  c.Url = url
 
  return nil
}

That's all for now. I'll keep adding snippets whenever I run into something worth saving.