Many To Many

Many To Many

Many to Many add a join table between two models.

For example, if your application includes users and languages, and a user can speak many languages, and many users can speak a specified language.

// User has and belongs to many languages, `user_languages` is the join table
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
gorm.Model
Name string
}

When using GORM AutoMigrate to create a table for User, GORM will create the join table automatically

Back-Reference

// User has and belongs to many languages, use `user_languages` as join table
type User struct {
gorm.Model
Languages []*Language `gorm:"many2many:user_languages;"`
}

type Language struct {
gorm.Model
Name string
Users []*User `gorm:"many2many:user_languages;"`
}

Override Foreign Key

For a many2many relationship, the join table owns the foreign key which references two models, for example:

type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
gorm.Model
Name string
}

// Join Table: user_languages
// foreign key: user_id, reference: users.id
// foreign key: language_id, reference: languages.id

To override them, you can use tag foreignKey, reference, joinForeignKey, joinReferences, not necessary to use them together, you can just use one of them to override some foreign keys/references

type User struct {
gorm.Model
Profiles []Profile `gorm:"many2many:user_profiles;foreignKey:Refer;joinForeignKey:UserReferID;References:UserRefer;JoinReferences:UserRefer"`
Refer uint
}

type Profile struct {
gorm.Model
Name string
UserRefer uint
}

// Which creates join table: user_profiles
// foreign key: user_refer_id, reference: users.refer
// foreign key: profile_refer, reference: profiles.user_refer

Self-Referential Many2Many

Self-referencing many2many relationship

type User struct {
gorm.Model
Friends []*User `gorm:"many2many:user_friends"`
}

// Which creates join table: user_friends
// foreign key: user_id, reference: users.id
// foreign key: friend_id, reference: users.id

Eager Loading

GORM allows eager loading has many associations with Preload, refer Preloading (Eager loading) for details

CRUD with Many2Many

Please checkout Association Mode for working with many2many relations

Customize JoinTable

JoinTable can be a full-featured model, like having Soft DeleteHooks supports, and define more fields, you can setup it with SetupJoinTable, for example:

type Person struct {
ID int
Name string
Addresses []Address `gorm:"many2many:person_addresses;"`
}

type Address struct {
ID uint
Name string
}

type PersonAddress struct {
PersonID int
AddressID int
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}

func (PersonAddress) BeforeCreate(db *gorm.DB) error {
// ...
}

// Change model Person's field Addresses's join table to PersonAddress
// PersonAddress must defined all required foreign keys or it will raise error
err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})

FOREIGN KEY Constraints

You can setup OnUpdate, OnDelete constraints with tag constraint, it will be created when migrating with GORM, for example:

type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_speaks;"`
}

type Language struct {
Code string `gorm:"primarykey"`
Name string
}

// CREATE TABLE `user_speaks` (`user_id` integer,`language_code` text,PRIMARY KEY (`user_id`,`language_code`),CONSTRAINT `fk_user_speaks_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE,CONSTRAINT `fk_user_speaks_language` FOREIGN KEY (`language_code`) REFERENCES `languages`(`code`) ON DELETE SET NULL ON UPDATE CASCADE);

Composite Foreign Keys

If you are using Composite Primary Keys for your models, GORM will enable composite foreign keys by default

You are allowed to override the default foreign keys, to specify multiple foreign keys, just separate those keys’ name by commas, for example:

type Tag struct {
ID uint `gorm:"primaryKey"`
Locale string `gorm:"primaryKey"`
Value string
}

type Blog struct {
ID uint `gorm:"primaryKey"`
Locale string `gorm:"primaryKey"`
Subject string
Body string
Tags []Tag `gorm:"many2many:blog_tags;"`
LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"`
SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"`
}

// Join Table: blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: blog_locale, reference: blogs.locale
// foreign key: tag_id, reference: tags.id
// foreign key: tag_locale, reference: tags.locale

// Join Table: locale_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: blog_locale, reference: blogs.locale
// foreign key: tag_id, reference: tags.id

// Join Table: shared_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: tag_id, reference: tags.id

Also check out Composite Primary Keys