Parano和CanCanCan之间的Rails 5兼容性受损?
我遇到与此主题中描述的完全相同的问题:
Rails 5 only_deleted with cancancan#356
我可以访问已删除的记录,如下所示:
@area = Area.only_deleted.find(params[:id])
但如果我将load_and_authorize_resource
添加到我的控制器,它将尝试运行这样的查询:
@area = Area.find(params[:id])
这将导致错误,因为它不会在lost_at不为null的集合中找到具有该id的记录(未删除记录,Paranoia gem的目的)。
如果我为控制器禁用load_and_authorize_resource
或执行该操作,它会解决错误,但它不是解决方案,因为这意味着失去授权控制。
有没有解决这个问题,或者是否有一个授权gem与Rails 5上的Paranoia很好用,我可以切换到?
谢谢。
因此,根据有关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方法包含一个条件来检查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
现在为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
UPDATE
您可以将方法调用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