jquery ui autocomplete,rails3和CanCan模型访问问题

时间:2011-06-05 19:04:29

标签: jquery ruby-on-rails-3 cancan

我在我的rails应用程序中有CanCan定义的各种角色。我最近实现了jQuery UI自动完成功能,效果很好。问题是,当我提交表单时,模型中出现的find_by_name可以找到不属于current_user的记录。我认为我有以下内容:

<strong><%= f.label :inventory_name, "Material" %></strong>
<%= f.text_field :inventory_name, :class => "inputbox" %><br>

我的jQuery看起来像:

jQuery("input[id$=_inventory_name]").autocomplete({
  source: '/ajax/inventory',
  minLength: 2
});

然后我有一个做正确事情的ajax控制器:

def inventory
  inventory = Inventory.accessible_by(current_ability)
  if params[:term]
    like= "%".concat(params[:term].concat("%"))
    names = inventory.where("name LIKE ?", like)
  else
    names = inventory
  end

  list = names.map {|u| Hash[ :id => u.id, :label => u.name, :name => u.name]}
  render :json => list
end

但我的模型没有:

def inventory_name=(name)
  inventory = Inventory.find_by_name(name)
  if inventory
    self.inventory_id = inventory.id
  else
    errors[:inventory_name] << "Invalid name entered"
  end
end
def inventory_name
  Inventory.find(inventory_id).name if inventory_id
end

find_by_name将返回它找到的第一个匹配项,无论谁拥有它。理想情况下,我想改变:

inventory = Inventory.find_by_name(name)

inventory = Inventory.accessible_by(current_ability).find_by_name(name)

但这违反了MVC的原则,更不用说模型无法访问current_ability,current_user等。所以我的问题是,如何将这个逻辑移动到我可以访问这些东西的控制器中?我似乎无法绕过它:(

3 个答案:

答案 0 :(得分:1)

我认为您需要做的就是在控制器中创建和更新操作,然后检查Inventory.accessible_by(current_ability)

答案 1 :(得分:1)

我最终需要一个前后过滤器:

after_filter :save_new_project_inventory, :only => [:create]
before_filter :update_project_inventory, :only => [:create, :update]

...

private
def save_new_project_inventory
  # do this in an after filter so that
  # we will have a project.id
  @project_inventories.each { |key, value|
    value[:project_id] = @project.id
    ProjectInventory.create!(value)
  }
end

def update_project_inventory
  @project_inventories = {}
  params[:project][:project_inventories_attributes].each { |key, value|
    if value[:_destroy] != "false"
      project_inventory = ProjectInventory.find(value[:id])
      project_inventory.delete
    else
      if value[:id]
        project_inventory = ProjectInventory.find(value[:id])
        project_inventory.inventory_id = inventory_name(value[:inventory_name])
        project_inventory.save
      else
        if @project.nil?
          # can't save here because we need a project.id that we
          # won't have until after we save, so finish up in the
          # after filter
          @project_inventories[key] = {
            :inventory_id => inventory_name(value[:inventory_name])
          }
        else
          project_inventory = ProjectInventory.new
          project_inventory.inventory_id = inventory_name(value[:inventory_name])
          project_inventory.project_id = params[:id]
          project_inventory.save
        end
      end
    end
  }
  params[:project].delete(:project_inventories_attributes)
end

def inventory_name(name)
  inventories = Inventory.accessible_by(current_ability)
  inventory = inventories.find_by_name(name)
  if inventory
    inventory.id.to_s
  end
end

我不确定这是否是最佳方式,但它确实有效。

答案 2 :(得分:0)

我知道这已经老了,但我遇到了同样的问题,直到现在才难倒。对于其他任何人来说,我最终得到了一个更简单的解决方案。

在我的示例中,current_user创建一个作业,该作业需要分配给它的客户(一个也属于当前用户)。 jQuery UI自动完成功能用于搜索客户,当然我只希望属于当前用户的客户可以分配给正在创建的作业,这与原始问题非常相似。我的最终解决方案只是将user_id的隐藏字段添加到新作业表单,在调用新操作时填充user_id。

JobsController
def new
  @job = current_user.jobs.new(user_id: current_user.id)
end

这种方式在模型中,我们可以使用self.user_id获取user_id(获取隐藏字段中的值),然后将其用作查找搜索条件的一部分。我已将self.user_id分配给变量&#39; user&#39;为了便于阅读。

JobModel
def customer_name=(fullname)
  user = self.user_id
  self.customer = Customer.find_by(fullname: fullname, user_id: user) 
end

如此简单,除非搜索术语(在本例中为:fullname)和user_id在Customer模型中匹配,否则不会返回任何结果,并且要求用户再次尝试,即使是客户模型中存在相同的全名,并且具有不同的user_id,不会返回。

希望这会有所帮助。

编辑:请记住将user_id添加到允许的参数中。