对 ActiveReord 进行软删除 (Soft Deletion) 时,可透过自行实作 (ex: table 增加一栏,判断是否被软删除),或直接用现成的 Gem 来处理
需要增加软删除的 table 要加 deleted_at:datetime
,并在该 model 中加入 acts_as_paranoid
即,可参考此 commit
推荐至 GitHub 看文件,写得很清楚,且有提供范例
在 rails console --sandbox
中演练示范
$ rails c -s
[1] pry(main)> Shop.count
TRANSACTION (1.1ms) BEGIN
(16.6ms) SELECT COUNT(*) FROM "shops" WHERE "shops"."deleted_at" IS NULL
0
[2] pry(main)> Shop.create(name: 'riverye', email: '[email protected]');
TRANSACTION (0.3ms) SAVEPOINT active_record_1
Shop Exists? (0.7ms) SELECT 1 AS one FROM "shops" WHERE "shops"."name" = $1 AND "shops"."deleted_at" IS NULL LIMIT $2 [["name", "riverye"], ["LIMIT", 1]]
Shop Create (1.8ms) INSERT INTO "shops" ("name", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "riverye"], ["email", "[email protected]"], ["created_at", "2021-07-18 06:07:26.190110"], ["updated_at", "2021-07-18 06:07:26.190110"]]
TRANSACTION (0.2ms) RELEASE SAVEPOINT active_record_1
[3] pry(main)> Shop.count
(0.6ms) SELECT COUNT(*) FROM "shops" WHERE "shops"."deleted_at" IS NULL
1
[4] pry(main)> Shop.first.destroy
Shop Load (0.5ms) SELECT "shops".* FROM "shops" WHERE "shops"."deleted_at" IS NULL ORDER BY "shops"."id" ASC LIMIT $1 [["LIMIT", 1]]
TRANSACTION (0.3ms) SAVEPOINT active_record_1
Shop Update (0.7ms) UPDATE "shops" SET "deleted_at" = $1, "updated_at" = $2 WHERE "shops"."id" = $3 [["deleted_at", "2021-07-18 06:07:43.934539"], ["updated_at", "2021-07-18 06:07:43.934564"], ["id", 1]]
TRANSACTION (0.2ms) RELEASE SAVEPOINT active_record_1
#<Shop:0x00007fa2ec4d0468> {
:id => 1,
:name => "riverye",
:email => "[email protected]",
:note => nil,
:created_at => Sun, 18 Jul 2021 14:07:26.190110000 CST +08:00,
:updated_at => Sun, 18 Jul 2021 14:07:43.934564000 CST +08:00,
:deleted_at => Sun, 18 Jul 2021 14:07:43.934539400 CST +08:00
}
[5] pry(main)> Shop.count
(0.6ms) SELECT COUNT(*) FROM "shops" WHERE "shops"."deleted_at" IS NULL
0
[6] pry(main)> Shop.with_deleted.count
(0.4ms) SELECT COUNT(*) FROM "shops"
1
# 上述步骤说明:
# 1. 一开始 Shop.count # 0
# 2. 建立一个 Shop
# 3. Shop.count # 1
# 4. 删除建立的 Shop
# 5. Shop.count # 0
# 6. Shop.with_deleted.count # 1
当 table 有 unique key 时,软删除的资料并没有真的被删除,此时会有问题
$ rails c -s
[1] pry(main)> Shop.create!(name: 'riverye', email: '[email protected]');
TRANSACTION (0.3ms) BEGIN
TRANSACTION (0.5ms) SAVEPOINT active_record_1
Shop Exists? (16.5ms) SELECT 1 AS one FROM "shops" WHERE "shops"."name" = $1 AND "shops"."deleted_at" IS NULL LIMIT $2 [["name", "riverye"], ["LIMIT", 1]]
Shop Create (1.6ms) INSERT INTO "shops" ("name", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "riverye"], ["email", "[email protected]"], ["created_at", "2021-07-18 06:35:26.540395"], ["updated_at", "2021-07-18 06:35:26.540395"]]
TRANSACTION (0.3ms) RELEASE SAVEPOINT active_record_1
[2] pry(main)> Shop.first.destroy;
Shop Load (0.8ms) SELECT "shops".* FROM "shops" WHERE "shops"."deleted_at" IS NULL ORDER BY "shops"."id" ASC LIMIT $1 [["LIMIT", 1]]
TRANSACTION (0.3ms) SAVEPOINT active_record_1
Shop Update (0.9ms) UPDATE "shops" SET "deleted_at" = $1, "updated_at" = $2 WHERE "shops"."id" = $3 [["deleted_at", "2021-07-18 06:35:40.312456"], ["updated_at", "2021-07-18 06:35:40.312482"], ["id", 1]]
TRANSACTION (0.3ms) RELEASE SAVEPOINT active_record_1
[3] pry(main)> Shop.create!(name: 'riverye', email: '[email protected]');
TRANSACTION (0.3ms) SAVEPOINT active_record_1
Shop Exists? (0.4ms) SELECT 1 AS one FROM "shops" WHERE "shops"."name" = $1 AND "shops"."deleted_at" IS NULL LIMIT $2 [["name", "riverye"], ["LIMIT", 1]]
Shop Create (1.9ms) INSERT INTO "shops" ("name", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "riverye"], ["email", "[email protected]"], ["created_at", "2021-07-18 06:35:59.189116"], ["updated_at", "2021-07-18 06:35:59.189116"]]
TRANSACTION (0.2ms) ROLLBACK TO SAVEPOINT active_record_1
# ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_shops_on_name"
# DETAIL: Key (name)=(riverye) already exists.
# from /xxx/yyy/.rvm/gems/ruby-3.0.1/gems/rack-mini-profiler-2.3.2/lib/patches/db/pg.rb:69:in `exec_params'
# Caused by PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_shops_on_name"
# DETAIL: Key (name)=(riverye) already exists.
# from /xxx/yyy/.rvm/gems/ruby-3.0.1/gems/rack-mini-profiler-2.3.2/lib/patches/db/pg.rb:69:in `exec_params'
[4] pry(main)> Shop.with_deleted.first.really_destroy!;
Shop Load (0.4ms) SELECT "shops".* FROM "shops" ORDER BY "shops"."id" ASC LIMIT $1 [["LIMIT", 1]]
TRANSACTION (0.2ms) SAVEPOINT active_record_1
Shop Update (0.4ms) UPDATE "shops" SET "deleted_at" = $1, "updated_at" = $2 WHERE "shops"."id" = $3 [["deleted_at", "2021-07-18 06:36:08.980456"], ["updated_at", "2021-07-18 06:36:08.980466"], ["id", 1]]
Shop Destroy (0.5ms) DELETE FROM "shops" WHERE "shops"."id" = $1 [["id", 1]]
TRANSACTION (0.4ms) RELEASE SAVEPOINT active_record_1
[5] pry(main)> Shop.create!(name: 'riverye', email: '[email protected]');
TRANSACTION (0.4ms) SAVEPOINT active_record_1
Shop Exists? (0.5ms) SELECT 1 AS one FROM "shops" WHERE "shops"."name" = $1 AND "shops"."deleted_at" IS NULL LIMIT $2 [["name", "riverye"], ["LIMIT", 1]]
Shop Create (0.4ms) INSERT INTO "shops" ("name", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "riverye"], ["email", "[email protected]"], ["created_at", "2021-07-18 06:36:48.398434"], ["updated_at", "2021-07-18 06:36:48.398434"]]
TRANSACTION (0.3ms) RELEASE SAVEPOINT active_record_1
# 上述步骤说明:
# 1. 建立一个 Shop
# 2. 软删除建立的 Shop
# 3. 建立一个 Shop (与第一个 name 一样) # 建立失败
# 4. 真的删除软删除的 Shop
# 5. 建立一个 Shop (与第一个 name 一样) # 建立成功
实务上,常用的 Gem 之一,简易好上手
铁人赛文章连结:https://ithelp.ithome.com.tw/articles/10264573
medium 文章连结:https://link.medium.com/ay1JSdj2Mjb
本文同步发布於 小菜的 Blog https://riverye.com/
备注:之後文章修改更新,以个人部落格为主
<<: 【Day 03】- Python 基础操作与常见资料型态(整数、浮点数、布林值、字串、串列、元组、字典)
除了上⾯提到的局部样版,View Helper 也是⽤来整理程序码很常⽤的⼿法。其实平 常⼤家在写的...
在service planner team规划设计产品服务时,亦负责拟定规划官网及app等四种隐私相...
Flappy Bird 教学原文参考:Flappy Bird 这篇文章会介绍,如何在 Scratch...
标题听起来很厉害(?),不过今天只需要认识一个 Web API - Element.getBound...
终於来到 Vue2.x 介绍的尾声了,藉由完成今天自己的旅游小帮手范例一起收尾吧,GO~ 加入 Yo...