Allow shared foreign key for many2many jointable

This commit is contained in:
Jinzhu 2022-09-21 17:29:38 +08:00
parent 490625981a
commit 118a3f469e
3 changed files with 71 additions and 31 deletions

View File

@ -191,7 +191,8 @@ func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Fiel
err error err error
joinTableFields []reflect.StructField joinTableFields []reflect.StructField
fieldsMap = map[string]*Field{} fieldsMap = map[string]*Field{}
ownFieldsMap = map[string]bool{} // fix self join many2many ownFieldsMap = map[string]*Field{} // fix self join many2many
referFieldsMap = map[string]*Field{}
joinForeignKeys = toColumns(field.TagSettings["JOINFOREIGNKEY"]) joinForeignKeys = toColumns(field.TagSettings["JOINFOREIGNKEY"])
joinReferences = toColumns(field.TagSettings["JOINREFERENCES"]) joinReferences = toColumns(field.TagSettings["JOINREFERENCES"])
) )
@ -229,7 +230,7 @@ func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Fiel
joinFieldName = strings.Title(joinForeignKeys[idx]) joinFieldName = strings.Title(joinForeignKeys[idx])
} }
ownFieldsMap[joinFieldName] = true ownFieldsMap[joinFieldName] = ownField
fieldsMap[joinFieldName] = ownField fieldsMap[joinFieldName] = ownField
joinTableFields = append(joinTableFields, reflect.StructField{ joinTableFields = append(joinTableFields, reflect.StructField{
Name: joinFieldName, Name: joinFieldName,
@ -242,9 +243,6 @@ func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Fiel
for idx, relField := range refForeignFields { for idx, relField := range refForeignFields {
joinFieldName := strings.Title(relation.FieldSchema.Name) + relField.Name joinFieldName := strings.Title(relation.FieldSchema.Name) + relField.Name
if len(joinReferences) > idx {
joinFieldName = strings.Title(joinReferences[idx])
}
if _, ok := ownFieldsMap[joinFieldName]; ok { if _, ok := ownFieldsMap[joinFieldName]; ok {
if field.Name != relation.FieldSchema.Name { if field.Name != relation.FieldSchema.Name {
@ -254,14 +252,22 @@ func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Fiel
} }
} }
fieldsMap[joinFieldName] = relField if len(joinReferences) > idx {
joinTableFields = append(joinTableFields, reflect.StructField{ joinFieldName = strings.Title(joinReferences[idx])
Name: joinFieldName, }
PkgPath: relField.StructField.PkgPath,
Type: relField.StructField.Type, referFieldsMap[joinFieldName] = relField
Tag: removeSettingFromTag(appendSettingFromTag(relField.StructField.Tag, "primaryKey"),
"column", "autoincrement", "index", "unique", "uniqueindex"), if _, ok := fieldsMap[joinFieldName]; !ok {
}) fieldsMap[joinFieldName] = relField
joinTableFields = append(joinTableFields, reflect.StructField{
Name: joinFieldName,
PkgPath: relField.StructField.PkgPath,
Type: relField.StructField.Type,
Tag: removeSettingFromTag(appendSettingFromTag(relField.StructField.Tag, "primaryKey"),
"column", "autoincrement", "index", "unique", "uniqueindex"),
})
}
} }
joinTableFields = append(joinTableFields, reflect.StructField{ joinTableFields = append(joinTableFields, reflect.StructField{
@ -317,31 +323,37 @@ func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Fiel
f.Size = fieldsMap[f.Name].Size f.Size = fieldsMap[f.Name].Size
} }
relation.JoinTable.PrimaryFields = append(relation.JoinTable.PrimaryFields, f) relation.JoinTable.PrimaryFields = append(relation.JoinTable.PrimaryFields, f)
ownPrimaryField := schema == fieldsMap[f.Name].Schema && ownFieldsMap[f.Name]
if ownPrimaryField { if of, ok := ownFieldsMap[f.Name]; ok {
joinRel := relation.JoinTable.Relationships.Relations[relName] joinRel := relation.JoinTable.Relationships.Relations[relName]
joinRel.Field = relation.Field joinRel.Field = relation.Field
joinRel.References = append(joinRel.References, &Reference{ joinRel.References = append(joinRel.References, &Reference{
PrimaryKey: fieldsMap[f.Name], PrimaryKey: of,
ForeignKey: f, ForeignKey: f,
}) })
} else {
relation.References = append(relation.References, &Reference{
PrimaryKey: of,
ForeignKey: f,
OwnPrimaryKey: true,
})
}
if rf, ok := referFieldsMap[f.Name]; ok {
joinRefRel := relation.JoinTable.Relationships.Relations[relRefName] joinRefRel := relation.JoinTable.Relationships.Relations[relRefName]
if joinRefRel.Field == nil { if joinRefRel.Field == nil {
joinRefRel.Field = relation.Field joinRefRel.Field = relation.Field
} }
joinRefRel.References = append(joinRefRel.References, &Reference{ joinRefRel.References = append(joinRefRel.References, &Reference{
PrimaryKey: fieldsMap[f.Name], PrimaryKey: rf,
ForeignKey: f,
})
relation.References = append(relation.References, &Reference{
PrimaryKey: rf,
ForeignKey: f, ForeignKey: f,
}) })
} }
relation.References = append(relation.References, &Reference{
PrimaryKey: fieldsMap[f.Name],
ForeignKey: f,
OwnPrimaryKey: ownPrimaryField,
})
} }
} }
} }

View File

@ -10,7 +10,7 @@ import (
func checkStructRelation(t *testing.T, data interface{}, relations ...Relation) { func checkStructRelation(t *testing.T, data interface{}, relations ...Relation) {
if s, err := schema.Parse(data, &sync.Map{}, schema.NamingStrategy{}); err != nil { if s, err := schema.Parse(data, &sync.Map{}, schema.NamingStrategy{}); err != nil {
t.Errorf("Failed to parse schema") t.Errorf("Failed to parse schema, got error %v", err)
} else { } else {
for _, rel := range relations { for _, rel := range relations {
checkSchemaRelation(t, s, rel) checkSchemaRelation(t, s, rel)
@ -305,6 +305,33 @@ func TestMany2ManyOverrideForeignKey(t *testing.T) {
}) })
} }
func TestMany2ManySharedForeignKey(t *testing.T) {
type Profile struct {
gorm.Model
Name string
Kind string
ProfileRefer uint
}
type User struct {
gorm.Model
Profiles []Profile `gorm:"many2many:user_profiles;foreignKey:Refer,Kind;joinForeignKey:UserRefer,Kind;References:ProfileRefer,Kind;joinReferences:ProfileR,Kind"`
Kind string
Refer uint
}
checkStructRelation(t, &User{}, Relation{
Name: "Profiles", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile",
JoinTable: JoinTable{Name: "user_profiles", Table: "user_profiles"},
References: []Reference{
{"Refer", "User", "UserRefer", "user_profiles", "", true},
{"Kind", "User", "Kind", "user_profiles", "", true},
{"ProfileRefer", "Profile", "ProfileR", "user_profiles", "", false},
{"Kind", "Profile", "Kind", "user_profiles", "", false},
},
})
}
func TestMany2ManyOverrideJoinForeignKey(t *testing.T) { func TestMany2ManyOverrideJoinForeignKey(t *testing.T) {
type Profile struct { type Profile struct {
gorm.Model gorm.Model

View File

@ -3,17 +3,18 @@ module gorm.io/gorm/tests
go 1.16 go 1.16
require ( require (
github.com/denisenkom/go-mssqldb v0.12.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/jinzhu/now v1.1.5 github.com/jinzhu/now v1.1.5
github.com/lib/pq v1.10.6 github.com/lib/pq v1.10.7
github.com/mattn/go-sqlite3 v1.14.14 // indirect github.com/mattn/go-sqlite3 v1.14.15 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect
gorm.io/driver/mysql v1.3.5 gorm.io/driver/mysql v1.3.6
gorm.io/driver/postgres v1.3.8 gorm.io/driver/postgres v1.3.10
gorm.io/driver/sqlite v1.3.6 gorm.io/driver/sqlite v1.3.6
gorm.io/driver/sqlserver v1.3.2 gorm.io/driver/sqlserver v1.3.2
gorm.io/gorm v1.23.8 gorm.io/gorm v1.23.9
) )
replace gorm.io/gorm => ../ replace gorm.io/gorm => ../