【问题标题】:Rails-4, CanCan returning undefined method for nil:NilClass, for a user model attribute that existsRails-4,CanCan 为存在的用户模型属性返回 nil:NilClass 的未定义方法
【发布时间】:2013-09-10 20:50:36
【问题描述】:

我在我的应用程序中使用了 devise 和 cancan。我的 Product model 有一个 belong_to :user 并且 User 表 有一个列 seller:boolean。因此,在我的能力类中,我有这一行 product.try(:user).seller 如粘贴在问题中的能力类所示,该行在 ProductsController#create, undefined method 中抛出 NoMethodError每当我尝试创建新产品时,nil:NilClass 的`seller'。但是在 controllers create action 中,user objectnot nil 因为当我在错误后检查日志时,我看到 SELECT "users".* FROM "users" WHERE "users"."id" = 11.

另外,如果我这样做,在 Rails 控制台中:

   a = Product.find(4)    
   a.user.seller  will return  => true   

我的能力等级

 class Ability
   include CanCan::Ability

   def initialize(user)
     user ||= User.new 

     can :manage, Product do | product |
      product.try(:user_id) == user.id
      product.try(:user).seller == true 
     end
   end
 end   

产品控制器:

class ProductsController < ApplicationController
  before_filter :authenticate_user!, except: [:index, :show]
  load_and_authorize_resource only: [:create, :edit, :destroy]
  respond_to :html, :json

  def index
    @products = Product.all
    respond_with @products
  end

  def new
    @product = Product.new    
  end

  def create 
    @user = User.find_by(id: current_user.id)
    @product = @user.products.build(product_params)
    @product.save 
  end

end  

为了使 CanCan 与 rails 4 一起工作,我的应用程序控制器具有

class ApplicationController < ActionController::Base
  #temprary work-around for cancan gem to work with rails-4
  #source https://github.com/ryanb/cancan/issues/835#issuecomment-18663815
  before_filter do
    resource = controller_path.singularize.gsub('/', '_').to_sym
    method = "#{resource}_params"
    params[resource] &&= send(method) if respond_to?(method, true)
  end
end

为了让下面的截图更清晰,这里是 products/index.html.erb 的缩短版本

<% @products.each do |product| %>     
  <% if user_signed_in? %>      
    <% if can? :update, product %>
      <span class="bottomcentre"><%= link_to 'edit', edit_product_path(product), class: "btn btn-primary"  %></span>
    <% end %>

    <% if can? :destroy, product %>
      <span class="bottomright"><%= link_to "Delete", product, data: {confirm: 'Are u sure?'}, method: :delete, class: "btn btn-danger" %></span>
    <% end %>

   <% end %><!-- closes user_signed_in -->

  <% end %>

  <br/>
  <% if user_signed_in? %> 
    <% if can? :create, Product %>
      <%= link_to 'Create a new Product', new_product_path %>
    <% end %>
  <% end %>

另一个效果是 editdestroy 链接显示给卖家的产品不是他们的,除非我注释掉引发错误的行,即这个 product.try(:user).seller == true 在前面显示的 CanCan's Ability Class 中。因此,当被注释掉时,我得到这个截图 1,其中隐藏了不属于卖家的产品的编辑链接,当取消注释时,你得到截图 2,所有产品编辑链接都显示给卖家,即使产品不是他们的。

屏幕截图 1,product.try(:user).seller == trueCanCan Ability Class 中被注释掉,它显示了编辑链接 仅适用于属于已签名卖家的前两个产品

product.try(:user).seller == true 的屏幕截图 2 在 CanCan Ability Class 中保持原样。看到 edit links 显示为 shirtcufflinks 的较低产品,它们不属于已登录卖家。

【问题讨论】:

    标签: ruby-on-rails devise ruby-on-rails-4 cancan


    【解决方案1】:

    您的can :manage 块定义略有错误。块的返回值决定了用户是否有能力。在您的块中,您有两个语句,其中只有第二个语句与用户是否有能力有关。您需要使用&amp;&amp; 加入您的陈述。

    此外,您的错误似乎是产品没有用户,而不是当前用户为零。在product.try(:user) 的返回上也需要使用try,因为如果没有产品,它将为零。

    所以,总而言之,我认为你的块需要是:

    can :manage, Product do | product |
      product.try(:user_id) == user.id &&
      product.try(:user).try(:seller) == true 
    end
    

    【讨论】:

    • 非常感谢@shadwell 解决这个问题。你知道为什么我不能对非卖家但已登录的普通用户隐藏 创建新产品 链接吗?代码放置在 products/index.html.erb 的底部,在循环每个产品的代码块之外,这就是我使用 Product 的原因可以吗? 而不是 product 否则会出错。这是代码: 我在上面的问题中将此代码添加到 index.html.erb 模板中
    • 不,抱歉,没有什么明显的。不过,我原以为会是相反的情况-您将无法向他们展示它,因为 :manage 逻辑取决于具有 user_id 的产品。
    • 好的,谢谢。正如您上次回复中所述,它应该是相反的方式,但不幸的是,目前它不是那样工作的。感谢您的宝贵时间。
    猜你喜欢
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 2017-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多