英文:
Go-gorm BeforeDelete callback doesn't get called if delete is started in an other callback
问题
我在数据库中有一个层次模型(一个团队有客户,每个客户可以有备注)。我的目标是在删除团队时能够清理数据库:
-> 删除团队 -> 删除所有客户 -> 删除每个客户的所有备注
我的计划是使用BeforeDelete回调来实现,但是在团队回调之后,客户的BeforeDelete不再被正确调用。
在数据库中,团队及其客户被删除,但是客户的备注没有被删除。日志行也没有被打印出来。
你知道是否可以链接这些回调,或者第二个回调不执行是设计上的问题吗?
package main
import (
"errors"
"log"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
var DB *gorm.DB
type Team struct {
gorm.Model
Name string
Customers []Customer
}
type Note struct {
gorm.Model
Content string
OwnerID uint
OwnerType string
}
type Customer struct {
gorm.Model
Name string
TeamID uint
Notes []Note `gorm:"polymorphic:Owner;"`
}
func (team *Team) BeforeDelete(tx *gorm.DB) (err error) {
log.Println("------- delete team ---------")
tx.Where("team_id = ?", team.ID).Delete(&Customer{})
return
}
func (customer *Customer) BeforeDelete(tx *gorm.DB) (err error) {
log.Println("------- delete customer ---------")
tx.Where("owner_type = ? AND owner_id = ?", "customers", customer.ID).Delete(&Note{})
return
}
func (note *Note) BeforeDelete(tx *gorm.DB) (err error) {
log.Println("------- delete note ---------")
return
}
func init() {
var err error
DB, err = gorm.Open("sqlite3", "data.DB")
if err != nil {
log.Printf("Error from gorm.Open: %s\n", err)
}
log.Println("You connected to your database.")
if DB.HasTable(&Team{}) {
DB.DropTable(&Team{})
DB.DropTable(&Customer{})
DB.DropTable(&Note{})
}
if !DB.HasTable(&Team{}) {
DB.CreateTable(&Team{})
}
if !DB.HasTable(&Customer{}) {
DB.CreateTable(&Customer{})
}
if !DB.HasTable(&Note{}) {
DB.CreateTable(&Note{})
}
}
func createTeam(name string) Team {
team := Team{Name: name}
DB.Create(&team)
return team
}
func addCustomer(teamID uint, name string) Customer {
customer1 := Customer{Name: name}
customer1.TeamID = teamID
customer1.Notes = []Note{}
DB.Create(&customer1)
return customer1
}
func addNoteToCustomer(customerID uint, note Note) (customer Customer, err error) {
if DB.Preload("Notes").First(&customer, customerID).RecordNotFound() {
return customer, errors.New("customer doesn't exists")
}
customer.Notes = append(customer.Notes, note)
DB.Save(&customer)
return customer, err
}
func main() {
team := createTeam("Team 1")
team2 := createTeam("Team 2")
// Create customers
customer1 := addCustomer(team.ID, "TestC 1")
customer2 := addCustomer(team.ID, "TestC 2")
customer3 := addCustomer(team2.ID, "TestC 3")
customer4 := addCustomer(team2.ID, "TestC 4")
note1 := Note{Content: "testcontent"}
addNoteToCustomer(customer1.ID, note1)
note2 := Note{Content: "testcontent 2"}
addNoteToCustomer(customer2.ID, note2)
note3 := Note{Content: "testcontent 3"}
addNoteToCustomer(customer3.ID, note3)
note4 := Note{Content: "testcontent 4"}
addNoteToCustomer(customer4.ID, note4)
DB.Delete(&team)
}
英文:
I have a hierarchical model in my database (a team has customers and each customer can have notes). My goal would to be able to clean up the database if a team is deleted:
-> delete team -> delete all customers -> delete all notes for each customer
My plan was to do it with the BeforeDelete callback, but after the team callback, the BeforeDelete for Customers is not called any more properly.
In the DB, the Team is deleted as well as its Customers, but the notes for the customers aren't. The log line is not printed neither.
Do you know if it's possible to chain these callbacks, or is it by design that the second callback is not performed.
package main
import (
"errors"
"log"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
var DB *gorm.DB
type Team struct {
gorm.Model
Name string
Customers []Customer
}
type Note struct {
gorm.Model
Content string
OwnerID uint
OwnerType string
}
type Customer struct {
gorm.Model
Name string
TeamID uint
Notes []Note `gorm:"polymorphic:Owner;"`
}
func (team *Team) BeforeDelete(tx *gorm.DB) (err error) {
log.Println("------- delete team ---------")
tx.Where("team_id = ?", team.ID).Delete(&Customer{})
return
}
func (customer *Customer) BeforeDelete(tx *gorm.DB) (err error) {
log.Println("------- delete customer ---------")
tx.Where("owner_type = ? AND owner_id = ?", "customers", customer.ID).Delete(&Note{})
return
}
func (note *Note) BeforeDelete(tx *gorm.DB) (err error) {
log.Println("------- delete note ---------")
return
}
func init() {
var err error
DB, err = gorm.Open("sqlite3", "data.DB")
if err != nil {
log.Printf("Error from gorm.Open: %s\n", err)
}
log.Println("You connected to your database.")
if DB.HasTable(&Team{}) {
DB.DropTable(&Team{})
DB.DropTable(&Customer{})
DB.DropTable(&Note{})
}
if !DB.HasTable(&Team{}) {
DB.CreateTable(&Team{})
}
if !DB.HasTable(&Customer{}) {
DB.CreateTable(&Customer{})
}
if !DB.HasTable(&Note{}) {
DB.CreateTable(&Note{})
}
}
func createTeam(name string) Team {
team := Team{Name: name}
DB.Create(&team)
return team
}
func addCustomer(teamID uint, name string) Customer {
customer1 := Customer{Name: name}
customer1.TeamID = teamID
customer1.Notes = []Note{}
DB.Create(&customer1)
return customer1
}
func addNoteToCustomer(customerID uint, note Note) (customer Customer, err error) {
if DB.Preload("Notes").First(&customer, customerID).RecordNotFound() {
return customer, errors.New("customer doesn't exists")
}
customer.Notes = append(customer.Notes, note)
DB.Save(&customer)
return customer, err
}
func main() {
team := createTeam("Team 1")
team2 := createTeam("Team 2")
// Create customers
customer1 := addCustomer(team.ID, "TestC 1")
customer2 := addCustomer(team.ID, "TestC 2")
customer3 := addCustomer(team2.ID, "TestC 3")
customer4 := addCustomer(team2.ID, "TestC 4")
note1 := Note{Content: "testcontent"}
addNoteToCustomer(customer1.ID, note1)
note2 := Note{Content: "testcontent 2"}
addNoteToCustomer(customer2.ID, note2)
note3 := Note{Content: "testcontent 3"}
addNoteToCustomer(customer3.ID, note3)
note4 := Note{Content: "testcontent 4"}
addNoteToCustomer(customer4.ID, note4)
DB.Delete(&team)
}
答案1
得分: 1
我认为这是因为在Customer模型结构的指针上添加了BeforeDelete函数。
在你的第一个示例中,你只是传入了Customer{}
,它不是模型结构的指针。你可以尝试使用下面的示例:
var customer Customer
func (team *Team) BeforeDelete(tx *gorm.DB) (err error) {
tx.Where("team_id = ?", team.ID).Delete(&customer)
return
}
英文:
I think it's because the BeforeDelete function is added to the pointer of the Customer model struct.
You're just passing in Customer{}
in your first example, which isn't a pointer to the model struct. Try the below example instead?
var customer Customer
func (team *Team) BeforeDelete(tx *gorm.DB) (err error) {
tx.Where("team_id = ?", team.ID).Delete(&customer)
return
}
答案2
得分: 0
在尝试了很多方法后,我找到了一个解决方案:
func (team *Team) BeforeDelete(tx *gorm.DB) (err error) {
//tx.Where("team_id = ?", team.ID).Delete(Customer{})
var customers []Customer
tx.Model(&team).Related(&customers)
for _, customer := range customers {
tx.Delete(&customer)
}
return
}
其他模型的处理方式类似。如果有更好的建议,我很乐意听取(不知为何,我不太喜欢这个解决方案——代码太多了)。
英文:
After trying around a lot, I've found a solution:
func (team *Team) BeforeDelete(tx *gorm.DB) (err error) {
//tx.Where("team_id = ?", team.ID).Delete(Customer{})
var customers []Customer
tx.Model(&team).Related(&customers)
for _, customer := range customers {
tx.Delete(&customer)
}
return
}
And similarly for the other models. If anybody has a better suggestion, I'm happy to read it (somehow I don't like this one - too much code for it)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论