package gorm_test import ( "fmt" "strconv" "testing" "time" "github.com/jinzhu/gorm" ) type Person struct { Id int Name string Addresses []*Address `gorm:"many2many:person_addresses;"` } type PersonAddress struct { gorm.JoinTableHandler PersonID int AddressID int DeletedAt *time.Time CreatedAt time.Time } func (*PersonAddress) Add(handler gorm.JoinTableHandlerInterface, db *gorm.DB, foreignValue interface{}, associationValue interface{}) error { foreignPrimaryKey, _ := strconv.Atoi(fmt.Sprint(db.NewScope(foreignValue).PrimaryKeyValue())) associationPrimaryKey, _ := strconv.Atoi(fmt.Sprint(db.NewScope(associationValue).PrimaryKeyValue())) if result := db.Unscoped().Model(&PersonAddress{}).Where(map[string]interface{}{ "person_id": foreignPrimaryKey, "address_id": associationPrimaryKey, }).Update(map[string]interface{}{ "person_id": foreignPrimaryKey, "address_id": associationPrimaryKey, "deleted_at": gorm.Expr("NULL"), }).RowsAffected; result == 0 { return db.Create(&PersonAddress{ PersonID: foreignPrimaryKey, AddressID: associationPrimaryKey, }).Error } return nil } func (*PersonAddress) Delete(handler gorm.JoinTableHandlerInterface, db *gorm.DB, sources ...interface{}) error { return db.Delete(&PersonAddress{}).Error } func (pa *PersonAddress) JoinWith(handler gorm.JoinTableHandlerInterface, db *gorm.DB, source interface{}) *gorm.DB { table := pa.Table(db) return db.Joins("INNER JOIN person_addresses ON person_addresses.address_id = addresses.id").Where(fmt.Sprintf("%v.deleted_at IS NULL OR %v.deleted_at <= '0001-01-02'", table, table)) } func TestJoinTable(t *testing.T) { DB.Exec("drop table person_addresses;") DB.AutoMigrate(&Person{}) DB.SetJoinTableHandler(&Person{}, "Addresses", &PersonAddress{}) address1 := &Address{Address1: "address 1"} address2 := &Address{Address1: "address 2"} person := &Person{Name: "person", Addresses: []*Address{address1, address2}} DB.Save(person) DB.Model(person).Association("Addresses").Delete(address1) if DB.Find(&[]PersonAddress{}, "person_id = ?", person.Id).RowsAffected != 1 { t.Errorf("Should found one address") } if DB.Model(person).Association("Addresses").Count() != 1 { t.Errorf("Should found one address") } if DB.Unscoped().Find(&[]PersonAddress{}, "person_id = ?", person.Id).RowsAffected != 2 { t.Errorf("Found two addresses with Unscoped") } if DB.Model(person).Association("Addresses").Clear(); DB.Model(person).Association("Addresses").Count() != 0 { t.Errorf("Should deleted all addresses") } } func TestEmbeddedMany2ManyRelationship(t *testing.T) { type EmbeddedPerson struct { ID int Name string Addresses []*Address `gorm:"many2many:person_addresses;"` } type NewPerson struct { EmbeddedPerson ExternalID uint } DB.Exec("drop table person_addresses;") DB.AutoMigrate(&NewPerson{}) address1 := &Address{Address1: "address 1"} address2 := &Address{Address1: "address 2"} person := &NewPerson{ExternalID: 100, EmbeddedPerson: EmbeddedPerson{Name: "person", Addresses: []*Address{address1, address2}}} if err := DB.Save(person).Error; err != nil { t.Errorf("no error should return when save embedded many2many relationship, but got %v", err) } if err := DB.Model(person).Association("Addresses").Delete(address1).Error; err != nil { t.Errorf("no error should return when delete embedded many2many relationship, but got %v", err) } association := DB.Model(person).Debug().Association("Addresses") if count := association.Count(); count != 1 || association.Error != nil { t.Errorf("Should found one address, but got %v, error is %v", count, association.Error) } if association.Clear(); association.Count() != 0 { t.Errorf("Should deleted all addresses") } }