问题
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