目前我为餐馆设置了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
答案 0 :(得分: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
def has_ownership?(restaurant)
self.restaurants.find_by_id(restaurant.id).present?
end
答案 1 :(得分:1)
首先,我负责检查餐厅的所有权,而不是检查所有者,特别是因为您在RestaurantsController中实施此检查。
此外,您似乎过度设计了所有权检查。实际上,您只需要检查restaurant.owner == current_owner
。
<强> restaurant.rb 强>
def owned_by?(current_owner)
owner == current_owner
end
如果您认为您将在其他地方重复使用此方法,则只需要进入模型(而不是在过滤器之前作为控制器中的单行生存)。
或者,或者,如果您的所有者要管理许多不同类型的对象,您可以在权限模型中保留权限,并使其更灵活。
<强> owner.rb 强>
def manages?(object)
object.respond_to?(:owner) && object.owner == self
end
您使用的方法,find和find_by_id在ActiveRecord查询中是脆弱的,非首选。具体来说,find_by_ *会在没有结果时抛出异常。另一方面,使用where(:id =&gt; id)将返回空数组,如果没有结果,则返回nil。
请查看这些内容以获得更多见解和最佳实践 http://tenmiles.com/blog/2011/07/activerecord-finders-returns-nil-or-throws-exception/ http://guides.rubyonrails.org/active_record_querying.html