简体   繁体   中英

Go-gorm BeforeDelete callback doesn't get called if delete is started in an other callback

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)
}

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
}

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM