为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用 gorm 创建记录时验证所属关系

如何使用 gorm 创建记录时验证所属关系

Go
倚天杖 2022-08-01 09:53:09
我有以下型号type PrivateGormModel struct {    ID        uint       `gorm:"primary_key" json:"id"`    CreatedAt time.Time  `json:"-"`    UpdatedAt time.Time  `json:"-"`    DeletedAt *time.Time `json:"-"`}type Employee struct {    PrivateGormModel    Person          `gorm:"embedded" json:"person,omitempty"`    Contact         `gorm:"embedded" json:"contact,omitempty"`    Address         `gorm:"embedded" json:"address,omitempty"`    AltContact      `gorm:"embedded" json:"privateContact,omitempty"`    BankAccount     `gorm:"embedded" json:"bankAccount,omitempty"`    EmployeeGroupID uint `json:"groupID"`    EmployeeGroup   `json:"group"`    EmployeeRoleID  uint `json:"roleID"`    EmployeeRole    `json:"role"`}func (e Employee) Validate() error {    return validation.ValidateStruct(&e,        validation.Field(&e.Person, validation.Required),        validation.Field(&e.Contact),        validation.Field(&e.Address),        validation.Field(&e.AltContact),        validation.Field(&e.BankAccount),        validation.Field(&e.EmployeeGroup),        validation.Field(&e.EmployeeRole),    )}type EmployeeGroup struct {    PrivateGormModel    Title string `json:"title" gorm:"primaryKey;unique"`}func (e EmployeeGroup) Validate() error {    return validation.ValidateStruct(&e,        validation.Field(&e.Title, validation.Required, validation.Length(1, 32), validation.Match(regexp.MustCompile(`^[a-zA-Z0-9_ ]*$`))),    )}type EmployeeRole struct {    PrivateGormModel    Title string `json:"title" gorm:"primaryKey;unique"`}func (e EmployeeRole) Validate() error {    return validation.ValidateStruct(&e,        validation.Field(&e.Title, validation.Required, validation.Length(1, 32), validation.Match(regexp.MustCompile(`^[a-zA-Z0-9_ ]*$`))),    )}
查看完整描述

1 回答

?
天涯尽头无女友

TA贡献1831条经验 获得超9个赞

如何建立关系并不明显,因为似乎您正在为 EmployeeGroup 和 EmployeeRole 使用匿名嵌入式结构,并且您尚未包含这些结构的代码。我将假设您已经正确设置了它,并且gorm很乐意处理涉及匿名嵌入式结构的关系。

我还假设您指的是 BelongsTo 关系,否则您将在哪里放置外键以将 Employee 表与角色或组表链接起来?很明显,外键不在最后两个中。

因此,您已经获得了一个 Employee 结构,当给定无效的 GroupID 或 RoleID 时,您有两种选择:在 ID 不存在时拒绝操作,或者使用给定的 ID 创建新的角色/组。第一个是更理智和通常的处理事情的方式,但gorm也可以做到。

对于第一个,如果您的数据库中有外键检查,则可以执行以下操作:

// First make sure that EmployeeRoleID and EmployeeGroupID are set 
err := db.Omit("EmployeeRole", "EmployeeGroup").Create(employee).Error

如果 EmployeeGroup.ID 或 EmployeeRole.ID 不存在,则会发生外键冲突,并且您将收到错误。您可以检查错误并推断出它与外键有关,然后返回相应的 API 级错误。

您可能会发现此错误检查有点麻烦,具体取决于您使用的数据库。老实说,在保存实体之前触发一堆额外的关系验证查询是非常常见的,所以在这种情况下,不要对必须这样做感到惊讶。

另一方面,如果每次都想保存新的角色和组,则可以删除省略调用,确保每个 ID 为零,然后调用 Create。Gorm 将保存角色和组,为他们提供新 ID,然后在员工记录中保存指向这些新 ID 的链接。


编辑

我尝试运行您的代码,并发现了一些问题:

  • 首先,您的输入 JSON 具有所有 TitleCased 键名,但模型结构希望其中很多键名都是小写的。你需要决定其中一个并坚持下去,否则编组和取消marshaling将不起作用。

  • 这同样适用于 / vs /。在 JSON 中具有一个版本的名称,在 Go 结构中具有一个版本(如果删除了标记,它们也可以全部是相同的 TitleCased 版本)。EmployeeRoleIDEmployeeGroupIDgroupIDroleIDjson:"blah"

  • Gorm不喜欢为相关实体嵌入匿名结构,它不会为它们创建外键,而是在主表中创建无关的字段,不要这样做。

  • 您已将 Role 和 Group 的名称用作复合主键的一部分,但这会中断键控,因为您需要在主结构中显示两个字段才能使其正常工作,例如 和 ,这违背了拥有单独实体的目的。如果要在名称中强制实施唯一性,请改为添加唯一索引。EmployeeGroupIDEmployeeGroupName

  • 事实证明,这仅适用于多对多关系。对于属于 - 要做的是填写主结构中的字段,并省略与 的关系。Omit("Relation.*")RelationIDOmit("Relation")

下面是有效的模型的简化版本:

type Employee struct {

    PrivateGormModel

    Person  `gorm:"embedded" json:"Person"`

    // ...

    RoleID  uint

    Role    EmployeeRole `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`

}


type EmployeeRole struct {

    PrivateGormModel

    Title string `gorm:"uniqueIndex"`

}

下面是一个测试用例,它显示了它是如何工作的,假设有一个配置的:DB *gorm.DB


package main


import (

    "encoding/json"

    "testing"


    "github.com/stretchr/testify/require"

)


var inJSON = `{

    "Person": {

        "FirstName": "Test"

    },

    "RoleID": 1

}`


func TestGORM(t *testing.T) {

    require := require.New(t)

    require.NoError(DB.Migrator().DropTable(&Employee{}, &EmployeeRole{}))

    require.NoError(DB.Migrator().AutoMigrate(&Employee{}, &EmployeeRole{}))

    emp := Employee{}

    json.Unmarshal([]byte(inJSON), &emp)


    // create the role to simulate that it exists

    role := EmployeeRole{PrivateGormModel{ID: 1}, "Test"}

    require.NoError(DB.Create(&role).Error)


    // avoid re-saving emp.Role

    require.NoError(DB.Omit("Role").Create(&emp).Error)


    // if instead the RoleID doesn't exist

    emp.RoleID = 5

    require.Error(DB.Create(&emp).Error)

}


查看完整回答
反对 回复 2022-08-01
  • 1 回答
  • 0 关注
  • 129 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号