Go-gorm的BeforeDelete回调在另一个回调中启动删除时不会被调用。

huangapple go评论83阅读模式
英文:

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)

huangapple
  • 本文由 发表于 2017年9月15日 19:39:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/46238692.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定