[英]How to validate a belongs-to relationship when creating record with gorm
我有以下型號
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_ ]*$`))),
)
}
我的員工組和員工角色都只是一個帶有 gorm model 和字符串類型標題的結構。 我在數據庫中有一個 ID 為 1 的角色,一個 ID 為 1 的組。這是創建員工的處理程序
func CreateEmployee(db *database.Database) fiber.Handler {
return func(c *fiber.Ctx) error {
employee := new(model.Employee)
if err := c.BodyParser(employee); err != nil {
fmt.Printf("%v", err)
return c.JSON(myResponse.ParsingError())
}
// if err := employee.Validate(); err != nil {
// return c.JSON(myResponse.ValidationError(err))
// }
if result := db.Omit("EmployeeRole.*").Omit("EmployeeGroup.*").Create(&employee); result.Error != nil {
return c.JSON(myResponse.RecordCreateError())
}
return c.JSON(myResponse.RecordCreateSuccess(employee))
}
}
我將傳入的 JSON 解析為 model 看起來像這樣,我嘗試在數據庫中創建它
{
"Person":{
"Initials":"",
"FirstName":"",
"MiddleName":"",
"LastName":"",
"DateOfBirth":"",
"Language":""
},
"Address":{
"Country":"",
"Zip":"",
"Number":"0",
"Addition":"",
"Street":"",
"State":"",
"City":""
},
"Contact":{
"Tel":"",
"Mail":"",
"URL":""
},
"BankAccount":{
"Bank":"",
"BIC":"",
"IBAN":"",
"AccountHolder":"",
"Establishment":""
},
"EmployeeRoleID":1,
"EmployeeRole":{
"Title":"Test"
},
"EmployeeGroupID":1,
"EmployeeGroup":{
"Title":"Test"
}
}
這給了我以下回應
{
"data": {
"id": 7,
"person": {
"initials": "",
"firstName": "",
"middleName": "",
"lastName": "",
"dateOfBirth": "2021-01-05T11:14:38+01:00",
"language": ""
},
"contact": {
"tel": "",
"mail": "",
"url": ""
},
"address": {
"country": "",
"zip": "",
"number": "0",
"addition": "",
"street": "",
"state": "",
"city": ""
},
"privateContact": {
"tel": "",
"mail": "",
"url": ""
},
"bankAccount": {
"bank": "",
"bic": "",
"iban": "",
"accountHolder": "",
"establishment": ""
},
"groupID": 0,
"group": {
"id": 0,
"title": ""
},
"roleID": 0,
"role": {
"id": 0,
"title": ""
}
},
"message": "record created successfully",
"status": "success"
}
即使 ID 為 1 的角色記錄和 ID 為 1 的組記錄不存在,這也會給我以下響應。 如果角色或組不存在,它應該給我一個錯誤而不是創建記錄。
您如何建立關系並不明顯,因為您似乎對 EmployeeGroup 和 EmployeeRole 都使用了匿名嵌入式結構,而且您還沒有包含這些結構的代碼。 我將假設您已正確設置,並且 gorm 很樂意處理涉及匿名嵌入式結構的關系。
我還將假設您的意思是 BelongsTo 關系,否則您會將外鍵放在哪里以將 Employee 表與 Role 或 Group 表鏈接? 很明顯,外鍵不在最后兩個中。
因此,您獲得了一個 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 級錯誤。
根據您使用的數據庫,您可能會發現此錯誤檢查有點麻煩。 老實說,在保存實體之前觸發一堆額外的關系驗證查詢是非常常見的,所以在這種情況下不必對必須這樣做感到驚訝。
另一方面,如果你想每次都保存一個新的 Role 和 Group,你可以去掉 Omit 調用,確保每個 ID 都為零,然后調用 Create。 Gorm 將保存角色和組,給他們新的 ID,然后將這些新 ID 的鏈接保存在員工記錄中。
我嘗試運行您的代碼,並發現了一些問題:
EmployeeRoleID
/ EmployeeGroupID
vs groupID
/ roleID
。 在 JSON 中有一個版本的名稱,在 Go 結構中有一個版本(如果您刪除了json:"blah"
結構,它們也可以是相同的 TitleCased 版本)。EmployeeGroupID
和EmployeeGroupName
,這違背了擁有的目的一個單獨的實體。 如果要強制名稱的唯一性,請改為添加唯一索引。Omit("Relation.*")
僅適用於多對多關系。 對於belongs-to 要做的事情是填寫主結構中的RelationID
字段,並通常用Omit("Relation")
。這是您的 model 的簡化版本,它可以工作:
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)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.