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.
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/gormUpdating GORM
Same as install, but with the -u flag:
go get -u gorm.io/gormA 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"`
}Eager loading related rows
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.