github.com/systematiccaos/gorm@v1.22.6/schema/relationship_test.go (about) 1 package schema_test 2 3 import ( 4 "sync" 5 "testing" 6 7 "github.com/systematiccaos/gorm" 8 "github.com/systematiccaos/gorm/schema" 9 ) 10 11 func checkStructRelation(t *testing.T, data interface{}, relations ...Relation) { 12 if s, err := schema.Parse(data, &sync.Map{}, schema.NamingStrategy{}); err != nil { 13 t.Errorf("Failed to parse schema") 14 } else { 15 for _, rel := range relations { 16 checkSchemaRelation(t, s, rel) 17 } 18 } 19 } 20 21 func TestBelongsToOverrideForeignKey(t *testing.T) { 22 type Profile struct { 23 gorm.Model 24 Name string 25 } 26 27 type User struct { 28 gorm.Model 29 Profile Profile `gorm:"ForeignKey:ProfileRefer"` 30 ProfileRefer int 31 } 32 33 checkStructRelation(t, &User{}, Relation{ 34 Name: "Profile", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Profile", 35 References: []Reference{{"ID", "Profile", "ProfileRefer", "User", "", false}}, 36 }) 37 } 38 39 func TestBelongsToOverrideReferences(t *testing.T) { 40 type Profile struct { 41 gorm.Model 42 Refer string 43 Name string 44 } 45 46 type User struct { 47 gorm.Model 48 Profile Profile `gorm:"ForeignKey:ProfileID;References:Refer"` 49 ProfileID int 50 } 51 52 checkStructRelation(t, &User{}, Relation{ 53 Name: "Profile", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Profile", 54 References: []Reference{{"Refer", "Profile", "ProfileID", "User", "", false}}, 55 }) 56 } 57 58 func TestBelongsToWithOnlyReferences(t *testing.T) { 59 type Profile struct { 60 gorm.Model 61 Refer string 62 Name string 63 } 64 65 type User struct { 66 gorm.Model 67 Profile Profile `gorm:"References:Refer"` 68 ProfileRefer int 69 } 70 71 checkStructRelation(t, &User{}, Relation{ 72 Name: "Profile", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Profile", 73 References: []Reference{{"Refer", "Profile", "ProfileRefer", "User", "", false}}, 74 }) 75 } 76 77 func TestBelongsToWithOnlyReferences2(t *testing.T) { 78 type Profile struct { 79 gorm.Model 80 Refer string 81 Name string 82 } 83 84 type User struct { 85 gorm.Model 86 Profile Profile `gorm:"References:Refer"` 87 ProfileID int 88 } 89 90 checkStructRelation(t, &User{}, Relation{ 91 Name: "Profile", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Profile", 92 References: []Reference{{"Refer", "Profile", "ProfileID", "User", "", false}}, 93 }) 94 } 95 96 func TestSelfReferentialBelongsTo(t *testing.T) { 97 type User struct { 98 ID int32 `gorm:"primaryKey"` 99 Name string 100 CreatorID *int32 101 Creator *User 102 } 103 104 checkStructRelation(t, &User{}, Relation{ 105 Name: "Creator", Type: schema.BelongsTo, Schema: "User", FieldSchema: "User", 106 References: []Reference{{"ID", "User", "CreatorID", "User", "", false}}, 107 }) 108 109 } 110 111 func TestSelfReferentialBelongsToOverrideReferences(t *testing.T) { 112 type User struct { 113 ID int32 `gorm:"primaryKey"` 114 Name string 115 CreatedBy *int32 116 Creator *User `gorm:"foreignKey:CreatedBy;references:ID"` 117 } 118 119 checkStructRelation(t, &User{}, Relation{ 120 Name: "Creator", Type: schema.BelongsTo, Schema: "User", FieldSchema: "User", 121 References: []Reference{{"ID", "User", "CreatedBy", "User", "", false}}, 122 }) 123 } 124 125 func TestHasOneOverrideForeignKey(t *testing.T) { 126 type Profile struct { 127 gorm.Model 128 Name string 129 UserRefer uint 130 } 131 132 type User struct { 133 gorm.Model 134 Profile Profile `gorm:"ForeignKey:UserRefer"` 135 } 136 137 checkStructRelation(t, &User{}, Relation{ 138 Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile", 139 References: []Reference{{"ID", "User", "UserRefer", "Profile", "", true}}, 140 }) 141 } 142 143 func TestHasOneOverrideReferences(t *testing.T) { 144 type Profile struct { 145 gorm.Model 146 Name string 147 UserID uint 148 } 149 150 type User struct { 151 gorm.Model 152 Refer string 153 Profile Profile `gorm:"ForeignKey:UserID;References:Refer"` 154 } 155 156 checkStructRelation(t, &User{}, Relation{ 157 Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile", 158 References: []Reference{{"Refer", "User", "UserID", "Profile", "", true}}, 159 }) 160 } 161 162 func TestHasOneOverrideReferences2(t *testing.T) { 163 164 type Profile struct { 165 gorm.Model 166 Name string 167 } 168 169 type User struct { 170 gorm.Model 171 ProfileID uint `gorm:"column:profile_id"` 172 Profile *Profile `gorm:"foreignKey:ID;references:ProfileID"` 173 } 174 175 checkStructRelation(t, &User{}, Relation{ 176 Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile", 177 References: []Reference{{"ProfileID", "User", "ID", "Profile", "", true}}, 178 }) 179 } 180 181 func TestHasOneWithOnlyReferences(t *testing.T) { 182 type Profile struct { 183 gorm.Model 184 Name string 185 UserRefer uint 186 } 187 188 type User struct { 189 gorm.Model 190 Refer string 191 Profile Profile `gorm:"References:Refer"` 192 } 193 194 checkStructRelation(t, &User{}, Relation{ 195 Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile", 196 References: []Reference{{"Refer", "User", "UserRefer", "Profile", "", true}}, 197 }) 198 } 199 200 func TestHasOneWithOnlyReferences2(t *testing.T) { 201 type Profile struct { 202 gorm.Model 203 Name string 204 UserID uint 205 } 206 207 type User struct { 208 gorm.Model 209 Refer string 210 Profile Profile `gorm:"References:Refer"` 211 } 212 213 checkStructRelation(t, &User{}, Relation{ 214 Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile", 215 References: []Reference{{"Refer", "User", "UserID", "Profile", "", true}}, 216 }) 217 } 218 219 func TestHasManyOverrideForeignKey(t *testing.T) { 220 type Profile struct { 221 gorm.Model 222 Name string 223 UserRefer uint 224 } 225 226 type User struct { 227 gorm.Model 228 Profile []Profile `gorm:"ForeignKey:UserRefer"` 229 } 230 231 checkStructRelation(t, &User{}, Relation{ 232 Name: "Profile", Type: schema.HasMany, Schema: "User", FieldSchema: "Profile", 233 References: []Reference{{"ID", "User", "UserRefer", "Profile", "", true}}, 234 }) 235 } 236 237 func TestHasManyOverrideReferences(t *testing.T) { 238 type Profile struct { 239 gorm.Model 240 Name string 241 UserID uint 242 } 243 244 type User struct { 245 gorm.Model 246 Refer string 247 Profile []Profile `gorm:"ForeignKey:UserID;References:Refer"` 248 } 249 250 checkStructRelation(t, &User{}, Relation{ 251 Name: "Profile", Type: schema.HasMany, Schema: "User", FieldSchema: "Profile", 252 References: []Reference{{"Refer", "User", "UserID", "Profile", "", true}}, 253 }) 254 } 255 256 func TestMany2ManyOverrideForeignKeyAndReferences(t *testing.T) { 257 type Profile struct { 258 gorm.Model 259 Name string 260 UserRefer uint 261 } 262 263 type User struct { 264 gorm.Model 265 Profiles []Profile `gorm:"many2many:user_profiles;ForeignKey:Refer;JoinForeignKey:UserReferID;References:UserRefer;JoinReferences:ProfileRefer"` 266 Profiles2 []Profile `gorm:"many2many:user_profiles2;ForeignKey:refer;JoinForeignKey:user_refer_id;References:user_refer;JoinReferences:profile_refer"` 267 Refer uint 268 } 269 270 checkStructRelation(t, &User{}, Relation{ 271 Name: "Profiles", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile", 272 JoinTable: JoinTable{Name: "user_profiles", Table: "user_profiles"}, 273 References: []Reference{ 274 {"Refer", "User", "UserReferID", "user_profiles", "", true}, 275 {"UserRefer", "Profile", "ProfileRefer", "user_profiles", "", false}, 276 }, 277 }, Relation{ 278 Name: "Profiles2", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile", 279 JoinTable: JoinTable{Name: "user_profiles2", Table: "user_profiles2"}, 280 References: []Reference{ 281 {"Refer", "User", "User_refer_id", "user_profiles2", "", true}, 282 {"UserRefer", "Profile", "Profile_refer", "user_profiles2", "", false}, 283 }, 284 }) 285 } 286 287 func TestMany2ManyOverrideForeignKey(t *testing.T) { 288 type Profile struct { 289 gorm.Model 290 Name string 291 UserRefer uint 292 } 293 294 type User struct { 295 gorm.Model 296 Profiles []Profile `gorm:"many2many:user_profiles;ForeignKey:Refer;References:UserRefer"` 297 Refer uint 298 } 299 300 checkStructRelation(t, &User{}, Relation{ 301 Name: "Profiles", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile", 302 JoinTable: JoinTable{Name: "user_profiles", Table: "user_profiles"}, 303 References: []Reference{ 304 {"Refer", "User", "UserRefer", "user_profiles", "", true}, 305 {"UserRefer", "Profile", "ProfileUserRefer", "user_profiles", "", false}, 306 }, 307 }) 308 } 309 310 func TestMany2ManyOverrideJoinForeignKey(t *testing.T) { 311 type Profile struct { 312 gorm.Model 313 Name string 314 UserRefer uint 315 } 316 317 type User struct { 318 gorm.Model 319 Profiles []Profile `gorm:"many2many:user_profile;JoinForeignKey:UserReferID;JoinReferences:ProfileRefer"` 320 Refer uint 321 } 322 323 checkStructRelation(t, &User{}, Relation{ 324 Name: "Profiles", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile", 325 JoinTable: JoinTable{Name: "user_profile", Table: "user_profile"}, 326 References: []Reference{ 327 {"ID", "User", "UserReferID", "user_profile", "", true}, 328 {"ID", "Profile", "ProfileRefer", "user_profile", "", false}, 329 }, 330 }) 331 } 332 333 func TestBuildReadonlyMany2ManyRelation(t *testing.T) { 334 type Profile struct { 335 gorm.Model 336 Name string 337 UserRefer uint 338 } 339 340 type User struct { 341 gorm.Model 342 Profiles []Profile `gorm:"->;many2many:user_profile;JoinForeignKey:UserReferID;JoinReferences:ProfileRefer"` 343 Refer uint 344 } 345 346 checkStructRelation(t, &User{}, Relation{ 347 Name: "Profiles", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile", 348 JoinTable: JoinTable{Name: "user_profile", Table: "user_profile"}, 349 References: []Reference{ 350 {"ID", "User", "UserReferID", "user_profile", "", true}, 351 {"ID", "Profile", "ProfileRefer", "user_profile", "", false}, 352 }, 353 }) 354 } 355 356 func TestMany2ManyWithMultiPrimaryKeys(t *testing.T) { 357 type Tag struct { 358 ID uint `gorm:"primary_key"` 359 Locale string `gorm:"primary_key"` 360 Value string 361 } 362 363 type Blog struct { 364 ID uint `gorm:"primary_key"` 365 Locale string `gorm:"primary_key"` 366 Subject string 367 Body string 368 Tags []Tag `gorm:"many2many:blog_tags;"` 369 SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"` 370 LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"` 371 } 372 373 checkStructRelation(t, &Blog{}, 374 Relation{ 375 Name: "Tags", Type: schema.Many2Many, Schema: "Blog", FieldSchema: "Tag", 376 JoinTable: JoinTable{Name: "blog_tags", Table: "blog_tags"}, 377 References: []Reference{ 378 {"ID", "Blog", "BlogID", "blog_tags", "", true}, 379 {"Locale", "Blog", "BlogLocale", "blog_tags", "", true}, 380 {"ID", "Tag", "TagID", "blog_tags", "", false}, 381 {"Locale", "Tag", "TagLocale", "blog_tags", "", false}, 382 }, 383 }, 384 Relation{ 385 Name: "SharedTags", Type: schema.Many2Many, Schema: "Blog", FieldSchema: "Tag", 386 JoinTable: JoinTable{Name: "shared_blog_tags", Table: "shared_blog_tags"}, 387 References: []Reference{ 388 {"ID", "Blog", "BlogID", "shared_blog_tags", "", true}, 389 {"ID", "Tag", "TagID", "shared_blog_tags", "", false}, 390 }, 391 }, 392 Relation{ 393 Name: "LocaleTags", Type: schema.Many2Many, Schema: "Blog", FieldSchema: "Tag", 394 JoinTable: JoinTable{Name: "locale_blog_tags", Table: "locale_blog_tags"}, 395 References: []Reference{ 396 {"ID", "Blog", "BlogID", "locale_blog_tags", "", true}, 397 {"Locale", "Blog", "BlogLocale", "locale_blog_tags", "", true}, 398 {"ID", "Tag", "TagID", "locale_blog_tags", "", false}, 399 }, 400 }, 401 ) 402 } 403 404 func TestMultipleMany2Many(t *testing.T) { 405 type Thing struct { 406 ID int 407 } 408 409 type Person struct { 410 ID int 411 Likes []Thing `gorm:"many2many:likes"` 412 Dislikes []Thing `gorm:"many2many:dislikes"` 413 } 414 415 checkStructRelation(t, &Person{}, 416 Relation{ 417 Name: "Likes", Type: schema.Many2Many, Schema: "Person", FieldSchema: "Thing", 418 JoinTable: JoinTable{Name: "likes", Table: "likes"}, 419 References: []Reference{ 420 {"ID", "Person", "PersonID", "likes", "", true}, 421 {"ID", "Thing", "ThingID", "likes", "", false}, 422 }, 423 }, 424 Relation{ 425 Name: "Dislikes", Type: schema.Many2Many, Schema: "Person", FieldSchema: "Thing", 426 JoinTable: JoinTable{Name: "dislikes", Table: "dislikes"}, 427 References: []Reference{ 428 {"ID", "Person", "PersonID", "dislikes", "", true}, 429 {"ID", "Thing", "ThingID", "dislikes", "", false}, 430 }, 431 }, 432 ) 433 } 434 435 func TestSelfReferentialMany2Many(t *testing.T) { 436 type User struct { 437 ID int32 `gorm:"primaryKey"` 438 Name string 439 CreatedBy int32 440 Creators []User `gorm:"foreignKey:CreatedBy"` 441 AnotherPro interface{} `gorm:"-"` 442 } 443 444 checkStructRelation(t, &User{}, Relation{ 445 Name: "Creators", Type: schema.HasMany, Schema: "User", FieldSchema: "User", 446 References: []Reference{{"ID", "User", "CreatedBy", "User", "", true}}, 447 }) 448 449 user, err := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{}) 450 if err != nil { 451 t.Fatalf("failed to parse schema") 452 } 453 454 relSchema := user.Relationships.Relations["Creators"].FieldSchema 455 if user != relSchema { 456 t.Fatalf("schema should be same, expects %p but got %p", user, relSchema) 457 } 458 } 459 460 type CreatedByModel struct { 461 CreatedByID uint 462 CreatedBy *CreatedUser 463 } 464 465 type CreatedUser struct { 466 gorm.Model 467 CreatedByModel 468 } 469 470 func TestEmbeddedRelation(t *testing.T) { 471 checkStructRelation(t, &CreatedUser{}, Relation{ 472 Name: "CreatedBy", Type: schema.BelongsTo, Schema: "CreatedUser", FieldSchema: "CreatedUser", 473 References: []Reference{ 474 {"ID", "CreatedUser", "CreatedByID", "CreatedUser", "", false}, 475 }, 476 }) 477 478 userSchema, err := schema.Parse(&CreatedUser{}, &sync.Map{}, schema.NamingStrategy{}) 479 if err != nil { 480 t.Fatalf("failed to parse schema, got error %v", err) 481 } 482 483 if len(userSchema.Relationships.Relations) != 1 { 484 t.Fatalf("expects 1 relations, but got %v", len(userSchema.Relationships.Relations)) 485 } 486 487 if createdByRel, ok := userSchema.Relationships.Relations["CreatedBy"]; ok { 488 if createdByRel.FieldSchema != userSchema { 489 t.Fatalf("expects same field schema, but got new %p, old %p", createdByRel.FieldSchema, userSchema) 490 } 491 } else { 492 t.Fatalf("expects created by relations, but not found") 493 } 494 } 495 496 func TestSameForeignKey(t *testing.T) { 497 type UserAux struct { 498 gorm.Model 499 Aux string 500 UUID string 501 } 502 503 type User struct { 504 gorm.Model 505 Name string 506 UUID string 507 Aux *UserAux `gorm:"foreignkey:UUID;references:UUID"` 508 } 509 510 checkStructRelation(t, &User{}, 511 Relation{ 512 Name: "Aux", Type: schema.HasOne, Schema: "User", FieldSchema: "UserAux", 513 References: []Reference{ 514 {"UUID", "User", "UUID", "UserAux", "", true}, 515 }, 516 }, 517 ) 518 } 519 520 func TestBelongsToSameForeignKey(t *testing.T) { 521 522 type User struct { 523 gorm.Model 524 Name string 525 UUID string 526 } 527 528 type UserAux struct { 529 gorm.Model 530 Aux string 531 UUID string 532 User User `gorm:"ForeignKey:UUID;references:UUID;belongsTo"` 533 } 534 535 checkStructRelation(t, &UserAux{}, 536 Relation{ 537 Name: "User", Type: schema.BelongsTo, Schema: "UserAux", FieldSchema: "User", 538 References: []Reference{ 539 {"UUID", "User", "UUID", "UserAux", "", false}, 540 }, 541 }, 542 ) 543 } 544 545 func TestHasOneWithSameForeignKey(t *testing.T) { 546 type Profile struct { 547 gorm.Model 548 Name string 549 ProfileRefer int // not used in relationship 550 } 551 552 type User struct { 553 gorm.Model 554 Profile Profile `gorm:"ForeignKey:ID;references:ProfileRefer"` 555 ProfileRefer int 556 } 557 558 checkStructRelation(t, &User{}, Relation{ 559 Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile", 560 References: []Reference{{"ProfileRefer", "User", "ID", "Profile", "", true}}, 561 }) 562 } 563 564 func TestHasManySameForeignKey(t *testing.T) { 565 type Profile struct { 566 gorm.Model 567 Name string 568 UserRefer uint 569 } 570 571 type User struct { 572 gorm.Model 573 UserRefer uint 574 Profile []Profile `gorm:"ForeignKey:UserRefer"` 575 } 576 577 checkStructRelation(t, &User{}, Relation{ 578 Name: "Profile", Type: schema.HasMany, Schema: "User", FieldSchema: "Profile", 579 References: []Reference{{"ID", "User", "UserRefer", "Profile", "", true}}, 580 }) 581 }