Reputation: 3783
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)
}
Upvotes: 0
Views: 2105
Reputation: 409
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
}
Upvotes: 1
Reputation: 3783
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)
Upvotes: 0