Bidirectional many2many relationship in gorm doesnt work properly

Viewed 88

Summary

Tried to implement DUPLICATE: Bidirectional many2many relationship solution. After appending different users to the "Followings" and "Followers" fields, any user added to the Following field is also being added to Followers field, and vice versa. The same happens with BlockedBy and Blocking fields.

I defined User Following and Blockingstruct types as follows

type User struct {
    Email                 string          `gorm:"size:100;not null;unique" json:"email"`
    ...
    UserId                string          `gorm:"primary_key;not null" json:"userid"`
}
type Following struct {
    ID         string  `gorm:"primary_key" json:"id"`
    User       User    `gorm:"foreignKey:ID;references:UserId"`
    Followings []*User `gorm:"many2many:user_relation;foreignKey:ID;joinForeignKey:UserA;references:UserId;joinReferences:UserB;constraint:OnDelete:CASCADE"`
    Followers  []*User `gorm:"many2many:user_relation;foreignKey:ID;joinForeignKey:UserB;references:UserId;joinReferences:UserA;constraint:OnDelete:CASCADE"`
}
type Blocking struct {
    ID        string  `gorm:"primary_key" json:"id"`
    User      User    `gorm:"foreignKey:ID"`
    BlockedBy []*User `gorm:"many2many:user_blocks;foreignKey:ID;joinForeignKey:UserIdBlocked;joinReferences:UserId;constraint:OnDelete:CASCADE"`
    Blocking  []*User `gorm:"many2many:user_blocks;foreignKey:ID;joinForeignKey:UserId;joinReferences:userIdBlocked;constraint:OnDelete:CASCADE"`
}

Loading method

Here's how I first initialized and load some data for testing purposes: // Load autogenerates the tables and records

func Load() {
    db, err := database.Connect()
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // Creates table for each model
    err = db.Debug().DropTableIfExists(
        &models.User{},
        &models.Following{},
        &models.Blocking{},
    ).Error
    if err != nil {
        log.Fatal(err)
    }

    //ERROR while using Postgres on Post model
    err = db.Debug().AutoMigrate(
        &models.User{},
        &models.Following{},
        &models.Blocking{},
    ).Error
    if err != nil {
        log.Fatal(err)
    }
    for i, _ := range users {
        err = db.Debug().Model(&models.User{}).Create(&users[i]).Error
        if err != nil {
            log.Fatal(err)
        }

        userfollowings[i].ID = users[i].UserId
        blocked_users[i].ID = users[i].UserId
        err = db.Debug().Model(&models.Following{}).Create(&userfollowings[i]).Error
        if err != nil {
            log.Fatal(err)
        }
        blocked_users[i].ID = users[i].UserId
        err = db.Debug().Model(&models.Blocking{}).Create(&blocked_users[i]).Error
        if err != nil {
            log.Fatal(err)
        }

        err = db.Debug().Model(&userfollowings[i]).Related(&userfollowings[i].User, "user_id").Error
        if err != nil {
            log.Fatal(err)
        }
        err = db.Debug().Model(&blocked_users[i]).Related(&blocked_users[i].User, "user_id").Error
        if err != nil {
            log.Fatal(err)
        }
        if i == 0 {
            err = db.Model(&userfollowings[i]).Association("Followings").Append(&users[1], &users[2]).Error
            if err != nil {
                log.Fatal(err)
            }
        } else if i == 1 {
            err = db.Model(&userfollowings[i]).Association("Followings").Append(&users[2]).Error
            if err != nil {
                log.Fatal(err)
            }
        } else if i == 2 {
            err = db.Model(&userfollowings[i]).Association("Followings").Append(&users[3]).Error
            if err != nil {
                log.Fatal(err)
            }
        } else if i == 3 {
            err = db.Model(&userfollowings[i]).Association("Followings").Append(&users[0]).Error
            if err != nil {
                log.Fatal(err)
            }
            fo_info := models.Following{}
            
            if err != nil {
                log.Fatal(err)
            }
            console.Pretty(&fo_info)
        }
    }
}

Displaying Following model query result for UserId: 4

[2021-12-27 12:17:03]  [0.90ms]  SELECT * FROM `users` INNER JOIN `user_relation` ON `user_relation`.`user_user_id` = `users`.`user_id` WHERE (`user_relation`.`following_id` IN ('4'))  
[0 rows affected or returned ] 
{
"id": "4",
"User": {
"email": "q4@gmail.com",
"id": "4",
"displayName": "John Lennon",
},
"Followings": [
{
"email": "q@gmail.com",
...,
"userid": "1",
"displayName": "Ringo Starr",
},
],
"Followers": [
{
"email": "q@gmail.com",
...
"userid": "1",
"displayName": "Ringo Starr",
}
]
}

Expected result

[2021-12-27 12:17:03]  [0.90ms]  SELECT * FROM `users` INNER JOIN `user_relation` ON `user_relation`.`user_user_id` = `users`.`user_id` WHERE (`user_relation`.`following_id` IN ('4'))  
[0 rows affected or returned ] 
{
"id": "4",
"User": {
"email": "q4@gmail.com",
"id": "4",
"displayName": "John Lennon",
},
"Followings": [
{
"email": "q@gmail.com",
...,
"userid": "1",
"displayName": "Ringo Starr",
},
],
"Followers": [
{
"email": "q2@gmail.com",
...
"userid": "2",
"displayName": "George Harrison",
}
]
}

Since last Friday, I'm unable to make this working out. Tried different resource from Golang Gorm documentation and searched similar SOF Q&A, which didn't provide me any solution or guidance.

0 Answers
Related