Как правильно связать модель для нормализованных таблиц с GORM? - PullRequest
0 голосов
/ 25 марта 2020

Предположим, у меня есть несколько таблиц разрешений пользователей, которые нормированы следующим образом:

entity-relationship-diagram

Модель этих трех таблиц будет выглядеть следующим образом:

type Role struct {
    RoleID      string             `gorm:"type:varchar(25); primary_key" json:"id"`
    Name        string             `gorm:"type:varchar(50)" json:"name"`
    Description string             `gorm:"type:text(250)" json:"description"`
    CreatedAt   string             `gorm:"type:timestamp" json:"createdAt"`
    Permission  []PermissionToRole `gorm:"foreignkey:RoleID; association_foreignkey:RoleID" json:"permissions"`
}

type Permission struct {
    PermID      string `json:"id" gorm:"column:permission_id; type:varchar(25); primary_key"`
    Name        string `json:"name" gorm:"type:varchar(50)"`
    Description string `json:"description" gorm:"text(250)"`
}

type PermissionToRole struct {
    RoleID     string     `json:"-" gorm:"type:varchar(25); primary_key"`
    PermID     string     `json:"-" gorm:"column:permission_id; type:varchar(25); primary_key"`
    Permission Permission `gorm:"foreignkey:PermID; association_foreignkey:PermID" json:"permissions"`
}

Но результат этой модели не тот, который я хочу. Я ожидаю, чтобы сделать массив разрешений, но это выглядит так (при выводе на JSON):

"permissions": [
    {
        "permissions": {
            "id": "create_user",
            "name": "Create user account",
            "description": "Give the permission to create a user account"
        }  
    }
]

Что я хочу:

"permissions": [
    {
        "id": "create_user",
        "name": "Create user account",
        "description": "Give the permission to create a user account"
    }
]

У любого есть ответ на это? Нужна помощь Спасибо.

1 Ответ

0 голосов
/ 30 марта 2020

Нет необходимости создавать PermissionToRole, вы можете просто использовать многие ко многим, которые сериализуются в JSON формат, который вы хотите. Если вы уверены, что вам нужно определить отдельную таблицу, вам нужно убедиться, что имена столбцов совпадают (как имена столбцов таблицы, так и ссылки), вот пример:

type Role struct {
    RoleID      string       `gorm:"type:varchar(25); primary_key" json:"id"`
    Name        string       `gorm:"type:varchar(50)" json:"name"`
    Description string       `gorm:"type:text(250)" json:"description"`
    CreatedAt   string       `gorm:"type:timestamp" json:"createdAt"`
    Permissions []Permission `gorm:"many2many:permission_to_roles;association_foreignkey:perm_id;foreignkey:role_id;association_jointable_foreignkey:perm_id;jointable_foreignkey:role_id;" json:"permissions"`
}

type Permission struct {
    PermID      string `json:"id" gorm:"type:varchar(25); primary_key"`
    Name        string `json:"name" gorm:"type:varchar(50)"`
    Description string `json:"description" gorm:"text(250)"`
}

type PermissionToRole struct {
    RoleID string `json:"-" gorm:"type:varchar(25); primary_key"`
    PermID string `json:"-" gorm:"type:varchar(25); primary_key"`
}

и полный код который использует это:

package main

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "log"
    "os"
)

type Role struct {
    RoleID      string       `gorm:"type:varchar(25); primary_key" json:"id"`
    Name        string       `gorm:"type:varchar(50)" json:"name"`
    Description string       `gorm:"type:text(250)" json:"description"`
    CreatedAt   string       `gorm:"type:timestamp" json:"createdAt"`
    Permissions []Permission `gorm:"many2many:permission_to_roles;association_foreignkey:perm_id;foreignkey:role_id;association_jointable_foreignkey:perm_id;jointable_foreignkey:role_id;" json:"permissions"`
}

type Permission struct {
    PermID      string `json:"id" gorm:"type:varchar(25); primary_key"`
    Name        string `json:"name" gorm:"type:varchar(50)"`
    Description string `json:"description" gorm:"text(250)"`
}

type PermissionToRole struct {
    RoleID string `json:"-" gorm:"type:varchar(25); primary_key"`
    PermID string `json:"-" gorm:"type:varchar(25); primary_key"`
}

func sample() error {
    _ = os.Remove("test.db") // Remove file to make sure DB is empty
    db, err := gorm.Open("sqlite3", "test.db")
    if err != nil {
        return fmt.Errorf("open DB failed: %w", err)
    }
    defer db.Close()
    db.LogMode(true)

    err = db.AutoMigrate(
        &Role{},
        &Permission{},
    ).Error
    if err != nil {
        return fmt.Errorf("migration failed: %w", err)
    }

    // Put some sample data in DB
    sampleRoles := []Role{
        {RoleID: "role0", Name: "n0"},
        {RoleID: "role1", Name: "n1"},
        {RoleID: "role2", Name: "n2"},
        {RoleID: "role3", Name: "n3"},
    }
    for idx := range sampleRoles {
        err = db.Create(&sampleRoles[idx]).Error
        if err != nil {
            return fmt.Errorf("failed to create: %w", err)
        }
    }

    samplePermissions := []Permission{
        {PermID: "perm0"},
        {PermID: "perm1"},
        {PermID: "perm2"},
        {PermID: "perm3"},
    }
    for idx := range samplePermissions {
        err = db.Create(&samplePermissions[idx]).Error
        if err != nil {
            return fmt.Errorf("failed to create: %w", err)
        }
    }

    sampleM2m := []PermissionToRole{
        {RoleID: "role2", PermID: "perm3"},
        {RoleID: "role2", PermID: "perm1"},
        {RoleID: "role2", PermID: "perm0"},
    }
    for idx := range sampleM2m {
        err = db.Create(&sampleM2m[idx]).Error
        if err != nil {
            return fmt.Errorf("failed to create: %w", err)
        }
    }

    // Do query
    var role Role
    err = db.Preload("Permissions").First(&role, "role_id = ?", "role2").Error
    if err != nil {
        return fmt.Errorf("error in query: %w", err)
    }
    fmt.Printf("%+v\n", role)
    return nil
}

func main() {
    err := sample()
    if err != nil {
        log.Fatal(err)
    }
}
...