Search code examples
gogo-gorm

How to cascade (soft) delete has many relationships in GORM?


I am implementing some delete functionality using GORM. My idea is to cascade delete where when the parent row is deleted the childern are deleted aswell. I have tried the delete with select. But it only deletes the parent record.

func DeleteProject(db *gorm.DB, id uint) (Project, error) {
    var project Project
    if err := db.Select(clause.Associations).Delete(&project, id).Error; err != nil {
        return Project{}, err
    }
    return project, nil
}

I have the following model:

type Project struct {
    ID           uint           `gorm:"primary_key" json:"id"`
    CreatedAt    time.Time      `json:"created_at"`
    UpdatedAt    time.Time      `json:"updated_at"`
    DeletedAt    gorm.DeletedAt `gorm:"index" json:"deleted_at"`
    TicketNumber string         `gorm:"size:6;unique;not null" json:"ticketnumber"`
    Name         string         `json:"name"`
    MailingList  string         `json:"mailinglist"`
    Environment  string         `gorm:"not null" json:"environment"`
    UserID       uint           `gorm:"index" json:"user_id"`
    Diagrams     []Diagram      `json:"diagrams"`
}

type Diagram struct {
    ID        uint           `gorm:"primary_key" json:"id"`
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt time.Time      `json:"updated_at"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
    Data      string         `json:"data"`
    ProjectID uint           `gorm:"index" json:"project_id"`
    Instances []Instance     `json:"instances"`
}

type Instance struct {
    ID        uint           `gorm:"primary_key" json:"id"`
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt time.Time      `json:"updated_at"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
    Key       uint           `json:"key"`
    DiagramID uint           `gorm:"index" json:"diagram_id"`
}

Where a project can have multiple diagrams and a diagram can have multiple instances. But when I am running the DeleteProject function it is only deleting the Project and not the associated diagrams and instances.

How can I delete a project and it's associated diagrams and instances in this case?


Solution

  • I found an solution, but I think it is not optimal for cascade delete, it solved my problem at least. I've coded the following:

    First the DeleteProject function, where I loop over all Diagrams and call the DeleteDiagram function and finally I also clear all associations with this project before deleting the project.

    //Delete a project and it's associations.
    func DeleteProject(db *gorm.DB, id uint) (Project, error) {
        project, err := GetProject(db, id)
        if err != nil {
            return Project{}, err
        }
        if len(project.Diagrams) > 0 {
            for _, projectDiagram := range project.Diagrams {
                _, err := DeleteDiagram(db, projectDiagram.ID)
                if err != nil {
                    return Project{}, err
                }
            }
            db.Model(&project).Association("Diagrams").Clear()
        }
        if err := db.Delete(&project, id).Error; err != nil {
            return Project{}, err
        }
        return project, nil
    }
    

    The DeleteDiagram does the same, but then with the Instances

    //Delete a diagram and it's associations.
    func DeleteDiagram(db *gorm.DB, id uint) (Diagram, error) {
        var diagram, err = GetDiagram(db, id)
        if err != nil {
            return Diagram{}, err
        }
        if len(diagram.Instances) > 0 {
            db.Model(&diagram).Association("Instances").Clear()
            for _, diagramInstance := range diagram.Instances {
                _, err := DeleteInstance(db, diagramInstance.ID)
                if err != nil {
                    return Diagram{}, err
                }
            }
        }
        if err := db.Delete(&diagram).Error; err != nil {
            return Diagram{}, err
        }
        return diagram, nil
    }
    
    

    Hope it helps.