Describe the feature

Model A has many Model B, Model B has many Model C

If Model A was deleted, Model B and Model C both should be able to be deleted by cascade

Motivation

Related Issues

6346, #6344, go-orm/playground PR

Comment From: pplmx

FYI.

Here is a demo implemented by Spring JPA, which implements the above requirement.

Comment From: Shahriar-Sazid

You can achieve this by after delete hook.

type ModelA struct {
    ID         uint
    ModelBList []ModelB
}

type ModelB struct {
    ID         uint
    ModelAID   uint
    ModelCList []ModelC
}

type ModelC struct {
    ID       uint
    ModelBID uint
}

func (m *ModelA) AfterDelete(tx *gorm.DB) (err error) {
    tx.Clauses(clause.Returning{}).Where("model_a_id = ?", m.ID).Delete(&ModelB{})
    return
}

func (m *ModelB) AfterDelete(tx *gorm.DB) (err error) {
    tx.Clauses(clause.Returning{}).Where("model_b_id = ?", m.ID).Delete(&ModelC{})
    return
}

Comment From: pplmx

Thanks a lot. :)

Comment From: pplmx

If you wanna delete it, not only just removing the reference, you should do the following:

  1. Since the foreign key constraint, use the BeforeDelete instead of AfterDelete
package model

import (
    "fmt"
    "gorm.io/gorm"
)

type ModelA struct {
    ID         uint
    ModelBList []ModelB
}

type ModelB struct {
    ID         uint
    ModelAID   uint
    ModelCList []ModelC
}

type ModelC struct {
    ID       uint
    ModelBID uint
}

// BeforeDelete for ModelA
func (a *ModelA) BeforeDelete(tx *gorm.DB) (err error) {
    // 1. if ID is 0, return
    if a.ID == 0 {
        return
    }

    // 2. delete its ModelBList
    // DO NOT USE
    //if err = tx.Clauses(clause.Returning{}).Where("model_a_id = ?", a.ID).Delete(&ModelB{}); err != nil {
    //  return fmt.Errorf("failed to delete model b list: %w", err)
    //}

    // NOTES: please use this way to delete model b list,
    // which aims to get model b id when running ModelB BeforeDelete hook
    // USE IT
    if err = tx.Model(a).Association("ModelBList").Find(&a.ModelBList); err != nil {
        return fmt.Errorf("failed to find model b list when deleting model a: %w", err)
    }
    for _, b := range a.ModelBList {
        if err = tx.Delete(b).Error; err != nil {
            return fmt.Errorf("failed to delete model b[%d] in model a[%d]: %w", b.ID, a.ID, err)
        }
    }

    return
}

// BeforeDelete for ModelB
func (b *ModelB) BeforeDelete(tx *gorm.DB) (err error) {
    // 1. if ID is 0, return
    if b.ID == 0 {
        return
    }

    // 2. delete its ModelCList
    if err = tx.Model(b).Association("ModelCList").Find(&b.ModelCList); err != nil {
        return fmt.Errorf("failed to find model c list when deleting model b: %w", err)
    }
    for _, c := range b.ModelCList {
        if err = tx.Delete(c).Error; err != nil {
            return fmt.Errorf("failed to delete model c[%d] in model b[%d]: %w", c.ID, b.ID, err)
        }
    }

    return
}