A complex update in GORM

非 Y 不嫁゛ 提交于 2021-02-11 14:33:01

问题


I have a model in GORM with Postgres as database. The model is like this

type Country struct {
    gorm.Model
    Name   string
    Population int64
    Regions []Region
}

type Region struct {
    gorm.Model
    Name   string
    Cities []City
    CountryID uint `sql:"type:bigint REFERENCES countries(id) ON DELETE CASCADE" json:"-"`
}

type City struct {
    gorm.Model
    Name        string
    Comment string
    RegionID   uint `sql:"type:bigint REFERENCES regions(id) ON DELETE CASCADE" json:"-"`
}

When I create a new record from model, I invoke create function

db.Create(&menu)

Now, I'm trying to update the model and I have some issues. If I invoke this

err = db.Debug().Where("id = ?", countryId).Updates(&country).Error

I have an error an the model is not updated in database UPDATED: The error is


(C:/source/go/gorm/country.go:100) 
[2020-06-06 02:37:59]  sql: converting argument $4 type: unsupported type []main.Region, a slice of struct 

(C:/source/go/gorm/country.go:100) 
[2020-06-06 02:37:59]  [0.00ms]  UPDATE "" SET "created_at" = '2020-06-06 00:37:50', "id" = 1, "name" = 'New Name', "regions" = '[{{1 2020-06-06 00:37:50.450497 +0000 UTC 2020-06-06 00:37:50.450497 +0000 UTC <nil>} Region 1 [{{1 2020-06-06 00:37:50.465029 +0000 UTC 2020-06-06 00:37:50.465029 +0000 UTC <nil>} City 1  1}] 1} {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC <nil>} Region 2 updated [{{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC <nil>} City 2 updated  0}] 0}]', "updated_at" = '2020-06-06 00:37:50'  WHERE (id = 1)  
[0 rows affected or returned ] 

If I run

err = db.Debug().Model(&country).Association("Regions").Replace(country.Regions).Error

The regions and cities models are updated in database but country model is not updated. Added to this, for regions model during update delete elements has null for CountryID but cities model doesn't update its RegionID to dereference.

How can I update a complete model like this?

UPDATE:

I don't know how to update a complete Country model. This is the full queries executed.

Create Tables

(C:/source/go/gorm/country.go:35) 
[2020-06-06 01:33:29]  [36;1m[20.02ms]  CREATE TABLE "countries" ("id" serial,"created_at" timestamp with time zone,"updated_at" timestamp with time zone,"deleted_at" timestamp with time zone,"name" text,"population" bigint , PRIMARY KEY ("id"))  
[36;31m[0 rows affected or returned ]

(C:/source/go/gorm/country.go:35) 
[2020-06-06 01:33:29]  [36;1m[16.00ms]  CREATE INDEX idx_countries_deleted_at ON "countries"(deleted_at)   
[36;31m[0 rows affected or returned ]

(C:/source/go/gorm/country.go:36) 
[2020-06-06 01:33:29]  [36;1m[28.99ms]  CREATE TABLE "regions" ("id" serial,"created_at" timestamp with time zone,"updated_at" timestamp with time zone,"deleted_at" timestamp with time zone,"name" text,"country_id" bigint REFERENCES countries(id) ON DELETE CASCADE , PRIMARY KEY ("id"))  
[36;31m[0 rows affected or returned ]

(C:/source/go/gorm/country.go:36) 
[2020-06-06 01:33:29]  [36;1m[11.99ms]  CREATE INDEX idx_regions_deleted_at ON "regions"(deleted_at)   
[36;31m[0 rows affected or returned ]

(C:/source/go/gorm/country.go:37) 
[2020-06-06 01:33:29]  [36;1m[18.99ms]  CREATE TABLE "cities" ("id" serial,"created_at" timestamp with time zone,"updated_at" timestamp with time zone,"deleted_at" timestamp with time zone,"name" text,"comment" text,"region_id" bigint REFERENCES regions(id) ON DELETE CASCADE , PRIMARY KEY ("id"))  
[36;31m[0 rows affected or returned ]

(C:/source/go/gorm/country.go:37) 
[2020-06-06 01:33:29]  [36;1m[32.00ms]  CREATE INDEX idx_cities_deleted_at ON "cities"(deleted_at)   
[36;31m[0 rows affected or returned ] 
Add Country Model

(C:/source/go/gorm/country.go:64) 
[2020-06-06 01:33:32]  [36;1m[3.99ms]  INSERT INTO "countries" ("created_at","updated_at","deleted_at","name","population") VALUES ('2020-06-06 01:33:32','2020-06-06 01:33:32',NULL,'Country 1',0) RETURNING "countries"."id"  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:64) 
[2020-06-06 01:33:32]  [36;1m[4.00ms]  INSERT INTO "regions" ("created_at","updated_at","deleted_at","name","country_id") VALUES ('2020-06-06 01:33:32','2020-06-06 01:33:32',NULL,'Region 1',1) RETURNING "regions"."id"  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:64) 
[2020-06-06 01:33:32]  [36;1m[3.00ms]  INSERT INTO "cities" ("created_at","updated_at","deleted_at","name","comment","region_id") VALUES ('2020-06-06 01:33:32','2020-06-06 01:33:32',NULL,'City 1','',1) RETURNING "cities"."id"  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:64) 
[2020-06-06 01:33:32]  [36;1m[4.00ms]  INSERT INTO "regions" ("created_at","updated_at","deleted_at","name","country_id") VALUES ('2020-06-06 01:33:32','2020-06-06 01:33:32',NULL,'Region 2',1) RETURNING "regions"."id"  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:64) 
[2020-06-06 01:33:32]  [36;1m[3.83ms]  INSERT INTO "cities" ("created_at","updated_at","deleted_at","name","comment","region_id") VALUES ('2020-06-06 01:33:32','2020-06-06 01:33:32',NULL,'City 2','',2) RETURNING "cities"."id"  
[36;31m[1 rows affected or returned ]
Update Country Model

(C:/source/go/gorm/country.go:75) 
[2020-06-06 01:33:39]  [36;1m[3.99ms]  SELECT * FROM "countries"  WHERE "countries"."deleted_at" IS NULL AND ((id = 1))  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:75) 
[2020-06-06 01:33:39]  [36;1m[4.00ms]  SELECT * FROM "regions"  WHERE "regions"."deleted_at" IS NULL AND (("country_id" IN (1)))  
[36;31m[2 rows affected or returned ]

(C:/source/go/gorm/country.go:75) 
[2020-06-06 01:33:39]  [36;1m[3.00ms]  SELECT * FROM "cities"  WHERE "cities"."deleted_at" IS NULL AND (("region_id" IN (1,2)))  
[36;31m[2 rows affected or returned ]
{{1 2020-06-05 23:33:32.660156 +0000 UTC 2020-06-05 23:33:32.660156 +0000 UTC <nil>} Country 1 0 [{{1 2020-06-05 23:33:32.664151 +0000 UTC 2020-06-05 23:33:32.664151 +0000 UTC <nil>} Region 1 [{{1 2020-06-05 23:33:32.668156 +0000 UTC 2020-06-05 23:33:32.668156 +0000 UTC <nil>} City 1  1}] 1} {{2 2020-06-05 23:33:32.672155 +0000 UTC 2020-06-05 23:33:32.672155 +0000 UTC <nil>} Region 2 [{{2 2020-06-05 23:33:32.676156 +0000 UTC 2020-06-05 23:33:32.676156 +0000 UTC <nil>} City 2  2}] 1}]}

(C:/source/go/gorm/country.go:99) 
[2020-06-06 01:33:39]  [36;1m[3.00ms]  UPDATE "regions" SET "created_at" = '2020-06-05 23:33:32', "updated_at" = '2020-06-06 01:33:39', "deleted_at" = NULL, "name" = 'Region 1', "country_id" = 1  WHERE "regions"."deleted_at" IS NULL AND "regions"."id" = 1  
[36;31m[1 rows affected or returned ]

() 
[2020-06-06 01:33:39]  [36;1m[6.00ms]  UPDATE "cities" SET "created_at" = '2020-06-05 23:33:32', "updated_at" = '2020-06-06 01:33:39', "deleted_at" = NULL, "name" = 'City 1', "comment" = '', "region_id" = 1  WHERE "cities"."deleted_at" IS NULL AND "cities"."id" = 1  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:99) 
[2020-06-06 01:33:39]  [36;1m[4.00ms]  SELECT * FROM "countries"  WHERE "countries"."deleted_at" IS NULL AND "countries"."id" = 1 ORDER BY "countries"."id" ASC LIMIT 1  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:99) 
[2020-06-06 01:33:39]  [36;1m[3.00ms]  UPDATE "regions" SET "created_at" = '2020-06-05 23:33:32', "updated_at" = '2020-06-06 01:33:39', "deleted_at" = NULL, "name" = 'Region 1', "country_id" = 1  WHERE "regions"."deleted_at" IS NULL AND "regions"."id" = 1  
[36;31m[1 rows affected or returned ]

() 
[2020-06-06 01:33:39]  [36;1m[4.99ms]  UPDATE "cities" SET "created_at" = '2020-06-05 23:33:32', "updated_at" = '2020-06-06 01:33:39', "deleted_at" = NULL, "name" = 'City 1', "comment" = '', "region_id" = 1  WHERE "cities"."deleted_at" IS NULL AND "cities"."id" = 1  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:99) 
[2020-06-06 01:33:39]  [36;1m[4.00ms]  INSERT INTO "regions" ("created_at","updated_at","deleted_at","name","country_id") VALUES ('2020-06-06 01:33:39','2020-06-06 01:33:39',NULL,'Region 2 updated',1) RETURNING "regions"."id"  
[36;31m[1 rows affected or returned ]

() 
[2020-06-06 01:33:39]  [36;1m[3.99ms]  INSERT INTO "cities" ("created_at","updated_at","deleted_at","name","comment","region_id") VALUES ('2020-06-06 01:33:39','2020-06-06 01:33:39',NULL,'City 2 updated','',3) RETURNING "cities"."id"  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:99) 
[2020-06-06 01:33:39]  [36;1m[3.00ms]  SELECT * FROM "countries"  WHERE "countries"."deleted_at" IS NULL AND "countries"."id" = 1 ORDER BY "countries"."id" ASC LIMIT 1  
[36;31m[1 rows affected or returned ]

(C:/source/go/gorm/country.go:99) 
[2020-06-06 01:33:39]  [36;1m[3.99ms]  UPDATE "regions" SET "country_id" = NULL  WHERE "regions"."deleted_at" IS NULL AND (("id" NOT IN (1,3)) AND ("country_id" = 1))  
[36;31m[1 rows affected or returned ]



回答1:


For update existing data you can fetch first preloading child

var country Country
db.Preload("Regions").Preload("Regions.Cities").First(&country, 1)

Then you can update the data and add new data like

country.Regions[0].Cities[0].Name = "Dhaka City 1"
country.Regions[0].Name = "Dhaka Region 1"
country.Regions[1].Cities = append(country.Regions[1].Cities, City{Name: "Dhaka City 2"})

Now save the updated data in database

db.Save(&country)

If you want to add new child data only you can avoid Preload also.

db.First(&country, 8)
country.Regions = append(country.Regions, Region{Name: "Dhaka Region 3"})
db.Save(&country)

By default gorm association_autoupdate flag set as true, so it's auto save association.

err = db.Debug().Model(&country).Association("Regions").Replace(country.Regions).Error

Replace only replace the association means with other. If you don't give any it will just remove current association with Country here. And only Regions not it's child city.

Gorm doesn't support any delete operation on update. It just use for add or update existing data. Gorm model uses soft delete by default. If you delete Regions this way, it will update the deleted_at field data. And when query it always filter out deleted data.

db.Delete(county.Regions)

And it won't soft delete City, you have to do this way.

db.Delete(region.Cities)

A working code sample here



来源:https://stackoverflow.com/questions/62220553/a-complex-update-in-gorm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!