GORM provides comprehensive support for managing relationships between models, including belongs to, has one, has many, and many to many relationships.
A belongs to association sets up a one-to-one connection with another model, where the declaring model has a foreign key.
type User struct {
gorm.Model
Name string
CompanyID int
Company Company // Belongs to Company
}
type Company struct {
gorm.Model
Name string
}Customization:
type User struct {
gorm.Model
Name string
CompanyID int
Company Company `gorm:"foreignKey:CompanyID;references:ID"`
}A has one association sets up a one-to-one connection with another model, where the other model has a foreign key.
type User struct {
gorm.Model
Name string
Profile Profile // Has one Profile
}
type Profile struct {
gorm.Model
UserID int // Foreign key
Bio string
}Customization:
type User struct {
gorm.Model
Name string
Profile Profile `gorm:"foreignKey:UserID;references:ID"`
}A has many association sets up a one-to-many connection with another model.
type User struct {
gorm.Model
Name string
Orders []Order // Has many Orders
}
type Order struct {
gorm.Model
UserID int // Foreign key
Amount float64
}Customization:
type User struct {
gorm.Model
Name string
Orders []Order `gorm:"foreignKey:UserID;references:ID"`
}A many to many association creates a join table between two models.
type User struct {
gorm.Model
Name string
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
}
// GORM will create a join table named 'user_languages'
// with columns: user_id, language_idCustom Join Table:
type User struct {
gorm.Model
Name string
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
}
type UserLanguage struct {
UserID uint `gorm:"primaryKey"`
LanguageID uint `gorm:"primaryKey"`
Proficiency string
CreatedAt time.Time
}
// Setup custom join table
db.SetupJoinTable(&User{}, "Languages", &UserLanguage{})Polymorphic associations allow a model to belong to more than one type of model on a single association.
type Image struct {
gorm.Model
URL string
OwnerID uint
OwnerType string // "User" or "Company"
}
type User struct {
gorm.Model
Name string
Images []Image `gorm:"polymorphic:Owner;"`
}
type Company struct {
gorm.Model
Name string
Images []Image `gorm:"polymorphic:Owner;"`
}Get an association manager to work with relationships.
func (db *DB) Association(column string) *AssociationFind associated records.
func (association *Association) Find(out interface{}, conds ...interface{}) errorUsage:
var user User
db.First(&user, 1)
// Find all associated orders
var orders []Order
db.Model(&user).Association("Orders").Find(&orders)
// Find with conditions
db.Model(&user).Association("Orders").Find(&orders, "amount > ?", 100)Add new associations without replacing existing ones.
func (association *Association) Append(values ...interface{}) errorUsage:
var user User
db.First(&user, 1)
// Append new orders
order1 := Order{Amount: 100}
order2 := Order{Amount: 200}
db.Model(&user).Association("Orders").Append(&order1, &order2)
// Append existing records (many2many)
var languages []Language
db.Find(&languages, []int{1, 2, 3})
db.Model(&user).Association("Languages").Append(&languages)Replace all associations with new ones.
func (association *Association) Replace(values ...interface{}) errorUsage:
var user User
db.First(&user, 1)
// Replace all orders
newOrders := []Order{
{Amount: 100},
{Amount: 200},
}
db.Model(&user).Association("Orders").Replace(&newOrders)
// Replace with empty slice to clear all
db.Model(&user).Association("Orders").Replace([]Order{})Delete the relationship between source and arguments, only delete the reference.
func (association *Association) Delete(values ...interface{}) errorUsage:
var user User
db.First(&user, 1)
// Delete specific associations
var order Order
db.First(&order, 1)
db.Model(&user).Association("Orders").Delete(&order)
// Delete multiple
var orders []Order
db.Where("amount < ?", 10).Find(&orders)
db.Model(&user).Association("Orders").Delete(&orders)
// For belongs to and has one/many: sets foreign key to null
// For many2many: deletes join table recordsRemove all associations.
func (association *Association) Clear() errorUsage:
var user User
db.First(&user, 1)
// Clear all orders
db.Model(&user).Association("Orders").Clear()
// Clear many2many associations
db.Model(&user).Association("Languages").Clear()Count the number of associated records.
func (association *Association) Count() (int64, error)Usage:
var user User
db.First(&user, 1)
// Count associated orders
count, err := db.Model(&user).Association("Orders").Count()
if err != nil {
// Handle error
}
fmt.Println("Order count:", count)Preload associations when querying to avoid N+1 queries.
func (db *DB) Preload(query string, args ...interface{}) *DBUsage:
// Preload single association
var users []User
db.Preload("Orders").Find(&users)
// Preload with conditions
db.Preload("Orders", "amount > ?", 100).Find(&users)
// Preload nested associations
db.Preload("Orders.Items").Find(&users)
db.Preload("Orders.Items.Product").Find(&users)
// Preload multiple associations
db.Preload("Orders").Preload("Profile").Find(&users)
// Preload all associations
db.Preload(clause.Associations).Find(&users)
// Custom preload query
db.Preload("Orders", func(db *gorm.DB) *gorm.DB {
return db.Order("orders.amount DESC").Limit(5)
}).Find(&users)Use joins to load associations in a single query.
// INNER JOIN with association
var users []User
db.Joins("Company").Find(&users)
// Specify join conditions
db.Joins("LEFT JOIN companies ON companies.id = users.company_id").Find(&users)
// Join with conditions
db.Joins("Company", db.Where(&Company{Name: "Acme Corp"})).Find(&users)
// Join nested associations
db.Joins("Orders.Items").Find(&users)// Create user with profile
user := User{
Name: "Alice",
Profile: Profile{
Bio: "Software Developer",
},
}
db.Create(&user)
// Create with has many
user := User{
Name: "Alice",
Orders: []Order{
{Amount: 100},
{Amount: 200},
},
}
db.Create(&user)
// Create with many2many
user := User{
Name: "Alice",
Languages: []Language{
{Name: "English"},
{Name: "Spanish"},
},
}
db.Create(&user)// Skip creating associations
db.Omit("Profile").Create(&user)
db.Omit("Orders").Create(&user)
// Skip all associations
db.Omit(clause.Associations).Create(&user)// Create user and only specified fields of associations
db.Select("Name", "Profile").Create(&user)Save all fields including associations.
// Update user and all associations
db.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user)
// Or configure globally
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
FullSaveAssociations: true,
})var user User
db.Preload("Orders").First(&user, 1)
// Modify associations
user.Orders[0].Amount = 150
user.Orders = append(user.Orders, Order{Amount: 300})
// Save with associations
db.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user)Delete associations when deleting a record.
// Delete user and their orders
db.Select("Orders").Delete(&user)
// Delete multiple associations
db.Select("Orders", "Profile").Delete(&user)
// Delete all associations
db.Select(clause.Associations).Delete(&user)Configure foreign key constraints for cascade delete.
type User struct {
gorm.Model
Name string
Orders []Order `gorm:"constraint:OnDelete:CASCADE;"`
}
type Order struct {
gorm.Model
UserID int
Amount float64
}
// When user is deleted, orders will be automatically deleted by databaseConfigure associations using struct tags.
type User struct {
gorm.Model
// Foreign key configuration
CompanyID int
Company Company `gorm:"foreignKey:CompanyID;references:ID"`
// Constraint configuration
Profile Profile `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
// Many2many configuration
Languages []Language `gorm:"many2many:user_languages;joinForeignKey:UserID;joinReferences:LanguageID"`
// Polymorphic configuration
Images []Image `gorm:"polymorphic:Owner;polymorphicValue:user"`
}foreignKey: Specify foreign key column namereferences: Specify reference column namepolymorphic: Specify polymorphic typepolymorphicValue: Specify polymorphic valuemany2many: Specify join table namejoinForeignKey: Specify foreign key in join tablejoinReferences: Specify reference key in join tableconstraint: Specify foreign key constraint (OnUpdate, OnDelete)Association mode provides helper methods without needing to reload data.
var user User
db.First(&user, 1)
// Get association mode
association := db.Model(&user).Association("Orders")
// Check if association is loaded
if association.Error != nil {
// Handle error
}
// Work with association
association.Append(&Order{Amount: 100})
association.Count()For many2many relationships, you can specify a custom join table model.
func (db *DB) SetupJoinTable(model interface{}, field string, joinTable interface{}) errorUsage:
type User struct {
gorm.Model
Name string
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
}
type UserLanguage struct {
UserID uint `gorm:"primaryKey"`
LanguageID uint `gorm:"primaryKey"`
Proficiency string
CreatedAt time.Time
}
// Setup custom join table
err := db.SetupJoinTable(&User{}, "Languages", &UserLanguage{})
if err != nil {
// Handle error
}
// Now creates and queries will use UserLanguage table with all fieldsUse composite foreign keys for complex relationships.
type User struct {
ID uint
LocationID uint
Name string
}
type Location struct {
ID uint
Name string
}
type Order struct {
ID uint
UserID uint
LocationID uint
User User `gorm:"foreignKey:UserID,LocationID;references:ID,LocationID"`
}Create relationships within the same model.
type User struct {
gorm.Model
Name string
ManagerID *uint
Manager *User `gorm:"foreignKey:ManagerID"`
Reports []User `gorm:"foreignKey:ManagerID"`
}
// Usage
var manager User
db.Preload("Reports").First(&manager, 1)
var employee User
db.Preload("Manager").First(&employee, 10)Configure association behavior globally or per-session.
// Global configuration
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
FullSaveAssociations: true,
})
// Per-session configuration
db.Session(&gorm.Session{
FullSaveAssociations: true,
}).Create(&user)
// Skip hooks for associations
db.Session(&gorm.Session{
SkipHooks: true,
}).Create(&user)