1

我遇到了与此线程中描述的完全相同的问题:

Rails 5 only_deleted with cancancan #356

我可以访问已删除的记录,如下所示:

    @area = Area.only_deleted.find(params[:id])

但如果我添加load_and_authorize_resource到我的控制器,它会尝试像这样运行查询:

    @area = Area.find(params[:id])

这将导致错误,因为它不会在 delete_at 不为空的集合上找到具有该 ID 的记录(不是删除的记录,Paranoia gem 的目的)。

如果我禁用load_and_authorize_resource控制器或该操作,它会解决错误,但它不是解决方案,因为这意味着失去授权控制。

是否有解决此问题的方法,或者是否有一个与 Rails 5 上的偏执狂配合得很好的授权宝石,我可以切换到它?

谢谢你。

4

1 回答 1

0

因此,根据有关 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

例如,当使用常规(没有ParanoiaArea.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_atis now NOT NULL)。

要访问已删除的记录,您必须使用with_deletedonly_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
于 2016-10-06T19:00:27.867 回答