1

目前,我为餐厅设置了 CRUD 资源。一个所有者拥有_许多餐厅和一个餐厅属于_一个所有者。任何用户都可以访问餐厅#index 和#show 视图。然而,为了创建一个新的餐厅,业主必须登录。我实施了设计,这工作正常。我的问题是确保 current_owner 在能够编辑、更新或销毁餐厅之前拥有餐厅。

我在创建 before_filter 时遇到问题,它将检查登录的所有者 (current_owner) 是否是登录的所有者试图查看的那家餐厅的所有者。

在设置 before_filter 之前,我快速创建了一个 #check_if_owner 方法并将其放在编辑操作中。如果所有者不拥有餐厅,则应将其重定向到上一页。但是,由于某种原因,我收到以下错误:

ActiveRecord::RecordNotFound in RestaurantsController#edit

Couldn't find Restaurant with id=4 [WHERE "restaurants"."owner_id" = 1]

我不确定为什么会发生这种情况,因为当我在控制台中运行 #check_ownership 方法时它返回 false,如果 current_owner 不拥有餐厅,这正是我希望该方法执行的操作。如果它在控制台中返回 false,用户不应该被重定向到上一页而不是接收 RecordNotFound 错误吗?我不确定为什么会这样。

发布的是其余的代码...

class RestaurantsController < ApplicationController
  before_filter :authenticate_owner!, except: [:index, :show]
  # before_filter :check_if_owner, only: [:edit, :update, :destroy]
  def index
    @restaurants = Restaurant.all
  end

  def show
    @restaurant = Restaurant.find(params[:id])
  end

  def new
    @restaurant = current_owner.restaurants.new
  end

  def create
    @restaurant = current_owner.restaurants.build(params[:restaurant])
    if @restaurant.save
      redirect_to restaurants_path
    else
      flash[:error] = "<ul>" + @restaurant.errors.full_messages.map{|o| "<li>" + o + "</li>" }.join("") + "</ul>"
      redirect_to new_restaurant_path
    end
  end

  def edit
    check_if_owner(Restaurant.find(params[:id]))
    @restaurant = current_owner.restaurants.find(params[:id])
  end

  def update
    @restaurant = current_owner.restaurants.find(params[:id])
    @restaurant.update_attributes(params[:restaurant])
    redirect_to restaurant_path(@restaurant)
  end

  def destroy
    @restaurant = current_owner.restaurants.find(params[:id])
    @restaurant.destroy
    redirect_to restaurants_path
  end

  private

    def check_if_owner(restaurant)
      debugger
      if current_owner.check_ownership(restaurant)
        return
      else
        redirect_to :back
      end
    end

end


class Owner < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable,
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me, :name
  # attr_accessible :title, :body

  has_many :restaurants

  validates :name, presence: true
  validates :email, presence: true

  def check_ownership(restaurant)
    !self.restaurants.find_by_id(restaurant.id).nil?
  end

end
4

2 回答 2

2

用以下方法解决了它:

class RestaurantsController < ApplicationController
  before_filter :authenticate_owner!, except: [:index, :show]
  before_filter :check_if_owner, only: [:edit, :update, :destroy]

  private

  def check_if_owner
    if current_owner.has_ownership?(Restaurant.find(params[:id]))
      return
    else
      flash[:error]= "You do not have permission to do that." 
      redirect_to :back
    end
  end
end

所有者.rb

  def has_ownership?(restaurant)
    self.restaurants.find_by_id(restaurant.id).present?
  end
于 2013-06-24T19:29:15.727 回答
1

首先,我将负责检查餐厅的所有权,而不是所有者,特别是因为您正在餐厅控制器中实施此检查。

而且,此外,您似乎过度设计了所有权检查。真的,您只需要检查restaurant.owner == current_owner.

餐厅.rb

def owned_by?(current_owner)
  owner == current_owner
end

如果您认为您将在其他地方重用此方法,则只需将其放入模型中(而不是在过滤器之前在控制器中作为单行存在)。

或者,或者,如果您的所有者要管理许多不同类型的对象,您可以将权限检查留在所有者模型中并使其更加灵活。

所有者.rb

def manages?(object)
  object.respond_to?(:owner) && object.owner == self
end

您使用 find 和 find_by_id 的方法很脆弱,在 ActiveRecord 查询中不受欢迎。具体来说, find_by_* 在没有结果时会引发异常。另一方面,使用 where(:id => id) 将返回一个空数组,如果没有结果则返回 nil。

查看这些以获得更多见解和最佳实践。
http://tenmiles.com/blog/2011/07/activerecord-finders-returns-nil-or-throws-exception/ http://guides.rubyonrails.org/active_record_querying.html

于 2013-06-24T20:28:10.627 回答