Reputation: 30045
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
Upvotes: 5
Views: 8286
Reputation: 1052
I had a very similar case.
My parent table had a soft deletion, but all the associated tables had a hard deletion.
This was the solution for my case: Delete Associations
if err := tx.Select(clause.Associations).Delete(&YOUR_MODEL).Error; err != nil {
return err
}
So, the parent table is soft deleted, and all the associations are hard deleted.
Upvotes: 0
Reputation: 625
gorm:"constraint:OnDelete:CASCADE"
is used for hard delete. But gorm doesn't do hard delete by default if you don't enforce by clause.Unscoped()
. It does soft delete by updating the deleted_at field. You can achieve soft delete cascade by after delete hook. Related github issue.
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
}
Upvotes: 4