Search code examples
postgresqlgogo-gormgoland

How can I perform nested deletes gorm Golang?


I have a table Customers with Cars, and Cars has Services. When I delete the Car I also want to delete the Services from the database And when I delete the Customer I want to be able to delete Customer, its Cars, Its Services.

I tried the following to delete Car and Services related to the Car, but it only deletes the Car from the database, how do I delete both Cars and its Services?

type Customer struct {
    gorm.Model

    FirstName string
    LastName  string
    Phone     string `gorm:"typevarchar(100);unique_index"`
    Cars      []Car  `gorm:"constraint:OnDelete:CASCADE;"`
}

type Car struct {
    gorm.Model

    Make       string
    Modelo     string
    Color      string
    VinNumber  string     `gorm:"typevarchar(100);unique_index"`
    Services   []*Service `gorm:"constraint:OnDelete:CASCADE;"`
    CustomerId int
}

type Service struct {
    gorm.Model

    Comment string
    Miles   string
    CarId   int
}

//endpoints
//delete customer
func deleteCustomer(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)

    setupResponse(&w, r)
    if (*r).Method == "OPTIONS" {
        return
    }

    var customer Customer
    db.First(&customer, params["id"])
    db.Unscoped().Delete(&customer)

    json.NewEncoder(w).Encode(&customer)
}

func deleteCar(w http.ResponseWriter, r *http.Request) {
    setupResponse(&w, r)
    if (*r).Method == "OPTIONS" {
        return
    }

    params := mux.Vars(r)

    var car Car
    db.First(&car, params["id"])
    db.Select("Services").Unscoped().Delete(&car)
    json.NewEncoder(w).Encode(&car)
}

I already tried most of what I seen in StackOverflow and the docs but nothing seems to work so far.


Solution

  • After reading the docs and a bunch of StackOverflow answers nothing really worked for me. The docs and/or StackOverflow suggested I add constraints when I migrated the models, or/and add the constraints to the structs gorm:"constraint:OnDelete:CASCADE;" , I also tried db.Select("cars").Delete(&customers) with no success. This is how I went about it, not sure if it's the best practice but it deletes its related records from the database. I updated the deleteCustomer and deleteCar endpoint like this:

    //delete customer
    func deleteCustomer(w http.ResponseWriter, r *http.Request) {
        params := mux.Vars(r)
    
        setupResponse(&w, r)
        if (*r).Method == "OPTIONS" {
            return
        }
    
        var customer Customer
        var cars []Car
    
        db.First(&customer, params["id"])
        db.Model(&customer).Related(&cars)
    
        deleteServicesSqlStatement := `
            DELETE FROM services
            WHERE car_id = $1;`
    
        for i, car := range cars {
            fmt.Println(i, car.ID)
            err := db.Exec(deleteServicesSqlStatement, car.ID).Error
            if err != nil {
                fmt.Println(err)
            }
    
        }
    
        DeleteCarsSqlStatement := `
            DELETE FROM cars
            WHERE customer_id = $1;`
    
        err := db.Exec(DeleteCarsSqlStatement, params["id"]).Error
        if err != nil {
            fmt.Println(err)
        }
    
        db.Debug().Unscoped().Delete(&customer)
        json.NewEncoder(w).Encode(&customer)
    
    }
    
    
    //delete car
    func deleteCar(w http.ResponseWriter, r *http.Request) {
        //handle CORS
        setupResponse(&w, r)
        if (*r).Method == "OPTIONS" {
            return
        }
    
        params := mux.Vars(r)
    
        var car Car
        db.First(&car, params["id"])
    
        sqlStatement := `
            DELETE FROM services
            WHERE car_id = $1;`
    
        err := db.Exec(sqlStatement, params["id"]).Error
    
        if err != nil {
            fmt.Println(err)
        }
    
        db.Debug().Unscoped().Delete(&car)
        json.NewEncoder(w).Encode(&car)
    }