开发者

mass_assignment_authorizer and nested attributes

I'm using dynamic attr_accessible as per this article:

http://asciicasts.com/episodes/237-dynamic-attr-accessible

It works fine. But I haven't found an elegant way to make it work with nested attributes. Here's some simplified code:

class Company < ActiveRecord::Base
  has_many :employees

  accepts_nested_attributes_for :employees
end

class Employee < ActiveRecord::Base
  belongs_to :company

  attr_protected :salary

  attr_accessor :accessible

  def mass_assignment_authorizer  
    if accessible == :all
      ActiveModel::MassAssignmentSecurity::BlackList.new
    else
      super + (accessible || [])
    end
  end 
end

Let's say I have an admin interface with a RESTful form for a Company. On this form, I have fields for employees_attributes, including blank fields to create new Employees. I can't find a way to call Employee#accessible= in this context. Browsing through the ActiveRecord source code, it seems that this might be impossible: in the remotest part of a very deep call stack, nested associations just result in Employee.new being called with the attributes.

I'd thought about creating a special attribute that could be passed in through mass assignment. If the attribute's value were the right code, the Employee instance would set @accessible to :all. But I don't think there's a way to guar开发者_如何转开发antee that this attribute gets set before the protected attributes.

Is there any way to make dynamic protected attributes work with nested attributes?


I'm new to rails, and have had boatloads of trouble trying to get nested attributes to work myself, but I found that I had to add the nested attributes to my accessible list.

class Company < ActiveRecord::Base
  has_many :employees

  accepts_nested_attributes_for :employees

  attr_accessible :employees_attributes
end

My understanding is that accepts_nested_attributes_for creates that special employees_attributes, but when you default all attributes to non-accessible (which I believe the asciicast does) you won't be able to use it.

I hope that helps.


This seems to me like something that could be set directly from your controller code on the class for this request. E.g.

Employee.accessible = :all
Company.create(params[:company])
Employee.accessible = nil

Which could be extracted to a block like

def with_accessible(*types)
  types.flatten!
  types.each{|type| type.accessible = :all}
  yield
  types.each{|type| type.accessible = nil}
end

So your final controller code is

with_accessible(Employee, OtherClass, YetAnotherClass) do
  Company.create(params[:company])
end

Pretty expressive of what's going on for the case of all attributes

For the case of only certain attributes, I might modify it to the following

def with_accessible(*types, &block)
  types.flatten!
  return with_accessible_hash(types.first, &block) if types.first.is_a?(Hash)
  types.each{|type| type.accessible = :all}
  ret = yield
  types.each{|type| type.accessible = nil}
  ret
end

def with_accessible_hash(hash, &block)
  hash.each_pair do |klass, accessible|
    Object.const_get(klass).accessible = accessible
  end
  ret = yield
  hash.keys.each{|type| type.accessible = nil}
  ret
end

Which gives you

with_accessible(:Employee => [:a, :b, :c], :OtherClass => [:a, :b]) do
  Company.create(params[:company])
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜