I have tried everything I could think about to obtain a cascaded delete in GROM (deleting an object also deletes the tree of objects below). The code below is an attempt to do that and I tried with a HasMany, BelongsTo and HasMany+BelongsTo variant (by changing the types).
The current version is Hasmany+BelongsTo - the result is always the same: only the top object is soft-deleted (person) but nothing below
package main
import (
"github.com/glebarez/sqlite"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
)
type Person struct {
gorm.Model
Name string
Pills []Pill `gorm:"constraint:OnDelete:CASCADE"`
}
type Pill struct {
gorm.Model
Name string
PersonID uint
Person Person
Posologies []Posology `gorm:"constraint:OnDelete:CASCADE"`
}
type Posology struct {
gorm.Model
Name string
PillID uint
Pill Pill
}
func main() {
var err error
// setup database
db, err := gorm.Open(sqlite.Open("test-gorm.db"), &gorm.Config{})
if err != nil {
log.Fatal().Msgf("failed to open follow-your-pills.db: %v", err)
}
// migrate database
err = db.AutoMigrate(&Person{}, &Pill{}, &Posology{})
if err != nil {
log.Fatal().Msgf("failed to migrate database: %v", err)
}
log.Info().Msg("database initialized")
db.Save(&Person{Name: "John"})
db.Save(&Pill{Name: "Paracetamol", PersonID: 1})
db.Save(&Pill{Name: "Aspirin", PersonID: 1})
db.Save(&Posology{Name: "1x/day", PillID: 1})
var person Person
db.Find(&person)
db.Delete(&person)
log.Info().Msgf("person: %v", person)
}
Before giving up (something I really would like to avoid), or using a solution for a previous question (where OP ended up manually deleting all the objects) I would like to understand if I am completely missing the point of GORM (and its mechanisms) or if this is how it is and if I want to have cascaded deletes I will have to do them myself (which, as I think it now, may be the right solution after all because I see in the DB structure that DELETE CASCADE is part of the schema
CREATE TABLE `pills` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,`person_id` integer,PRIMARY KEY (`id`),CONSTRAINT `fk_people_pills` FOREIGN KEY (`person_id`) REFERENCES `people`(`id`) ON DELETE CASCADE)
EDIT Following up on the last idea, I tried a
delete from people where id=1
and it correctly deleted the whole tree
GORM does not automatically cascade deletes through associations by default. The OnDelete:CASCADE tag only adds a foreign key constraint in the database, but does not actually perform cascaded deletion at the application level.
To implement true cascaded deletes in GORM, you have to manually delete the associated records before deleting the parent record. For example:
You are right that for true cascaded deletes, you have to handle deleting child records yourself in your application code.
The constraints just allow you to enforce referential integrity at the database level, but don't cascade deletes across Go objects automatically.
So in summary:
OnDelete:CASCADE-> Database constraint for referential integrity Cascaded deletes in app logic -> Must delete children explicitly before parent Doing it manually gives you more control but requires extra code. So tradeoff is convenience vs flexibility.