Your Question

The minimized models are:

type User struct {
    gorm.Model
    Companies        []*Company `gorm:"many2many:companies_users"`
}

type Company struct {
    gorm.Model
    Users           []*User        `gorm:"many2many:companies_users"`
}

// the custom join table

type CompanyUser struct {
    CompanyID uint `gorm:"primaryKey"`
    UserID    uint `gorm:"primaryKey"`
    CreatedAt time.Time
    DeletedAt gorm.DeletedAt
}

func (CompanyUser) TableName() string {
    return "companies_users"
}

After migrate & setup:

db.AutoMigrate(...)
db.SetupJoinTable(...)

Three tables are created successfully, and Preload(u.Companies) works well, but there aren't any foreign key constraints on the join table:

-- postgresql
SELECT 
  tc.constraint_name, 
  tc.table_name, 
  kcu.column_name, 
  ccu.table_name AS foreign_table_name, 
  ccu.column_name AS foreign_column_name 
FROM 
  information_schema.table_constraints tc 
  JOIN information_schema.key_column_usage kcu 
    ON tc.constraint_name = kcu.constraint_name 
  JOIN information_schema.constraint_column_usage ccu 
    ON ccu.constraint_name = tc.constraint_name 
WHERE 
  tc.constraint_type = 'FOREIGN KEY' 
  AND tc.table_name = 'companies_users';

-- this returns 0 rows.

The document you expected this should be explained

Expected answer

Maybe a clear example? Thanks a lot! 🙏

Comment From: Octobug

Well...my current workaround is to declare an extra HasMany association:

type User struct {
    gorm.Model
    Companies          []*Company     `gorm:"many2many:companies_users"`
    CompanyMemberships []*CompanyUser `gorm:"foreignKey:UserID"`
}

Weird but can create expected foreign key at least.