因此,根据有关 load_and_authorize_resource 的文档,该方法将尝试加载一个实例变量以防尚未设置,并且如果存在设置的实例变量则不会这样做,这正是应用程序崩溃的原因:
class AreasController < ApplicationController
load_and_authorize_resource
before_action :set_area, only: [:show, :edit, :update, :destroy]
...
def set_area
if session[:show_obsolete_records] == true
@area = Area.only_deleted.find(params[:id])
else
@area = Area.find(params[:id])
end
end
end
load_and_authorize_resource
首先在列表中运行,并且由于在调用之前没有设置实例变量,因此它是@area = Area.find(params[:id])
自行执行的,这显然会导致错误,因为Paranoia重写了 finder 方法以包含一个条件来检查 is 是否deleted_at
存在NULL
。
例如,当使用常规(没有Paranoia)Area.find(17)
时,您会在控制台上收到如下查询:
Area Load (0.2ms) SELECT "areas".* FROM "areas" WHERE "areas"."id" = ? LIMIT ? [["id", 17], ["LIMIT", 1]]
使用Paranoia时,您会收到以下查询:
Area Load (0.2ms) SELECT "areas".* FROM "areas" WHERE ("areas"."deleted_at" IS NULL) AND "areas"."id" = ? LIMIT ? [["id", 17], ["LIMIT", 1]]
这样,已删除的记录将不会在常见查询中找到,因为它们将deleted_at
设置时间戳 ( deleted_at
is now NOT NULL
)。
要访问已删除的记录,您必须使用with_deleted
或only_deleted
,例如
@area = Area.only_deleted.find(params[:id])
否则它将找不到已删除的记录,因此我收到错误消息
ActiveRecord::RecordNotFound - Couldn't find Area with 'id'=16 [WHERE "areas"."deleted_at" IS NULL]:
该方法load_and_authorize_resource
已加载@area = Area.find(params[:id])
并已跳过set_area
,因此您可以删除该方法,即使代码不存在,它仍会设置该区域。
解决方案是简单地将load_and_authorize_resource
方法移动到回调列表下方:
class AreasController < ApplicationController
before_action :set_area, only: [:show, :edit, :update, :destroy]
load_and_authorize_resource
...
def set_area
if session[:show_obsolete_records] == true
@area = Area.only_deleted.find(params[:id])
else
@area = Area.find(params[:id])
end
end
end
更新
根据此线程,您可以将方法调用留load_and_authorize_resource
在堆栈顶部,但将其更改为authorize_resource
不尝试调用。@area = Area.find(params[:id])
class AreasController < ApplicationController
authorize_resource
before_action :set_area, only: [:show, :edit, :update, :destroy]
...
def set_area
if session[:show_obsolete_records] == true
@area = Area.only_deleted.find(params[:id])
else
@area = Area.find(params[:id])
end
end
end