GORM 中的复杂更新

A complex update in GORM

我在 GORM 中有一个模型,使用 Postgres 作为数据库。模型是这样的

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:"-"`
}

当我从模型创建新记录时,我调用创建函数

db.Create(&menu)

现在,我正在尝试更新模型,但遇到了一些问题。 如果我调用这个

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

我有一个错误,模型没有在数据库中更新 更新: 错误是


(C:/source/go/gorm/country.go:100) 
[2020-06-06 02:37:59]  sql: converting argument  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 ] 

如果我运行

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

地区和城市模型已在数据库中更新,但国家/地区模型未更新。 此外,对于区域模型,在更新期间删除元素的 CountryID 为空,但城市模型不会将其 RegionID 更新为取消引用。

如何更新像这样的完整模型?

更新:

我不知道如何更新完整的国家/地区模型。 这是执行的完整查询。

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 ]


要更新现有数据,您可以获取第一个预加载子项

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

然后您可以更新数据并添加新数据,例如

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"})

现在将更新后的数据保存到数据库中

db.Save(&country)

如果你只想添加新的子数据,你也可以避免预加载。

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

默认情况下 gorm association_autoupdate 标志设置为 true,因此它是自动保存关联。

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

Replace 仅将关联方式替换为其他。如果您不提供任何内容,它只会在此处删除当前与 Country 的关联。只有Regions不是子城

Gorm 不支持更新时的任何删除操作。它仅用于添加或更新现有数据。 Gorm 模型默认使用软删除。如果您以这种方式删除区域,它将更新 deleted_at 字段数据。并且在查询时它总是过滤掉已删除的数据。

db.Delete(county.Regions)

而且不会软删除城市,你必须这样做。

db.Delete(region.Cities)

工作代码示例here