How is attr_accessible used in Rails 4?
Asked Answered
T

6

265

attr_accessible seems to no longer work within my model.

What is the way to allow mass assignment in Rails 4?

Takishatakken answered 28/6, 2013 at 18:28 Comment(0)
W
455

Rails 4 now uses strong parameters.

Protecting attributes is now done in the controller. This is an example:

class PeopleController < ApplicationController
  def create
    Person.create(person_params)
  end

  private

  def person_params
    params.require(:person).permit(:name, :age)
  end
end

No need to set attr_accessible in the model anymore.

Dealing with accepts_nested_attributes_for

In order to use accepts_nested_attribute_for with strong parameters, you will need to specify which nested attributes should be whitelisted.

class Person
  has_many :pets
  accepts_nested_attributes_for :pets
end

class PeopleController < ApplicationController
  def create
    Person.create(person_params)
  end

  # ...

  private

  def person_params
    params.require(:person).permit(:name, :age, pets_attributes: [:name, :category])
  end
end

Keywords are self-explanatory, but just in case, you can find more information about strong parameters in the Rails Action Controller guide.

Note: If you still want to use attr_accessible, you need to add protected_attributes to your Gemfile. Otherwise, you will be faced with a RuntimeError.

Wavelet answered 28/6, 2013 at 18:30 Comment(7)
The document didn't say that attr_accessible need to be removed. What will happen if we keep it?Laoag
You'll get an error if you don't make some adjustments to your Gemfile. RuntimeError in MicropostsController#index 'attr_accessible' is extracted out of Rails into a gem. Please use new recommended protection model for params(strong_parameters) or add 'protected_attributes' to your Gemfile to use old one.Cosmogony
Great explanation. It seems like in practice, though, this moves Rails away from fat model, thin controller, etc, and towards thin models, and really bloated controllers. You have to write all this stuff for every instance, it doesn't read nicely, and nesting seems to be a pain. The old attr_accessible/attr_accessor in the model system wasn't broken, and didn't need to be fixed. One blog post got too popular in this case.Jeuz
I am wondering about the advantage of strong parameter compared to the old stuff, attr_accessible method in model. As rcd said here Rails follows fat model. With this strong parameter we have to write all the attributes that permits to go, in controller. This seems odd to me. Anyone please tell me why they decided to go with strong parameters?Leishaleishmania
You don't have to handle permitted parameters in your controllers. In fact it's a violation of the single responsibility principle. Take a look at the following blog post edelpero.svbtle.com/strong-parameters-the-right-wayWavelet
So gimmiky & frequently changing apis, coupled with newfound pedantics waste many developer hours in yet another painful Rails upgrade :-(Oecd
@Pierre-LouisGottfrois Good explanation! But what about when I have two deep leves with nested attributes? I mean the first nested attributes are indicated in the principal model's controller strong_params. But what if the nested model has also nested attributes for a third model? Where do I need to specify the attributes for this third model in order to be whitelisted?Aiello
A
23

If you prefer attr_accessible, you could use it in Rails 4 too. You should install it like gem:

gem 'protected_attributes'

after that you could use attr_accessible in you models like in Rails 3

Also, and i think that is the best way- using form objects for dealing with mass assignment, and saving nested objects, and you can also use protected_attributes gem that way

class NestedForm
   include  ActiveModel::MassAssignmentSecurity
   attr_accessible :name,
                   :telephone, as: :create_params
   def create_objects(params)
      SomeModel.new(sanitized_params(params, :create_params))
   end
end
Anglicist answered 3/7, 2014 at 18:16 Comment(1)
When you use 'strong parameters' you filter parameters in controller layer, and i don't think that this is best idea for all applications. For me the best way to filter parameters is to use additional layer. And we can use 'protected_attributes' gem to write this layerAnglicist
S
6

An update for Rails 5:

gem 'protected_attributes' 

doesn't seem to work anymore. But give:

gem 'protected_attributes_continued'

a try.

Spirituous answered 17/8, 2018 at 23:58 Comment(0)
B
4

We can use

params.require(:person).permit(:name, :age)

where person is Model, you can pass this code on a method person_params & use in place of params[:person] in create method or else method

Breton answered 5/2, 2015 at 10:56 Comment(0)
I
1

1) Update Devise so that it can handle Rails 4.0 by adding this line to your application's Gemfile:

gem 'devise', '3.0.0.rc' 

Then execute:

$ bundle

2) Add the old functionality of attr_accessible again to rails 4.0

Try to use attr_accessible and don't comment this out.

Add this line to your application's Gemfile:

gem 'protected_attributes'

Then execute:

$ bundle
Inglis answered 2/5, 2015 at 3:45 Comment(0)
M
0

I had to migrate a Rails app from 3.2 to 6.1 so even gem 'protected_attributes' was not an option. I appreciate the arguments for using require().permit() in the controller, but I didn't want to retype or cut and paste all those attributes from the models, so I decided instead to use this initializer code (put in a file in config/initializers):

# fix attr_accessible in an initializer
# wrap ActionController::Parameters code in singleton method defined
# from attr_accessible so controller code can call class method
# to get permitted parameter list
# e.g. model: class A < ActiveRecord::Base,
# controller calls A.permit_attr(params)
# lots simpler than moving all attr_accessible definitions to controllers
# bug: fails if more than one attr_accessible statement

def (ActiveRecord::Base).attr_accessible *fields
  puts "attr_accessible:"+self.name+":permitted_params fields=#{fields.inspect}"
  define_singleton_method("permit_attr") { |params|
    # may have subclasses where attr_accessible is in superclass
    # thus must require by subclass name so should calculate require at runtime
    rq = self.name.downcase.to_sym
    puts "...permit_attr:self=#{rq} permit(#{fields.inspect})"
    params.require(rq).permit(fields)
  }

end

To protect against multiple attr_accessible declarations, before defining the method, add

raise "error: model can only have one attr_accessible declaration" if defined? permit_attr
Maloy answered 26/10, 2021 at 21:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.