[ruby-on-rails] Difference between attr_accessor and attr_accessible

In Rails, what is the difference between attr_accessor and attr_accessible? From my understanding, using attr_accessor is used to create getter and setter methods for that variable, so that we can access the variable like Object.variable or Object.variable = some_value.

I read that attr_accessible makes that specific variable accessible to the outside world. Can someone please tell me whats the difference

This question is related to ruby-on-rails ruby

The answer is


attr_accessor is a Ruby method that makes a getter and a setter. attr_accessible is a Rails method that allows you to pass in values to a mass assignment: new(attrs) or update_attributes(attrs).

Here's a mass assignment:

Order.new({ :type => 'Corn', :quantity => 6 })

You can imagine that the order might also have a discount code, say :price_off. If you don't tag :price_off as attr_accessible you stop malicious code from being able to do like so:

Order.new({ :type => 'Corn', :quantity => 6, :price_off => 30 })

Even if your form doesn't have a field for :price_off, if it's in your model it's available by default. This means a crafted POST could still set it. Using attr_accessible white lists those things that can be mass assigned.


attr_accessor is ruby code and is used when you do not have a column in your database, but still want to show a field in your forms. The only way to allow this is to attr_accessor :fieldname and you can use this field in your View, or model, if you wanted, but mostly in your View.

Let's consider the following example

class Address
    attr_reader :street
    attr_writer :street  
    def initialize
        @street = ""
    end
end

Here we have used attr_reader (readable attribute) and attr_writer (writable attribute) for accessing purpose. But we can achieve the same functionality using attr_accessor. In short, attr_accessor provides access to both getter and setter methods.

So modified code is as below

class Address
    attr_accessor :street  
    def initialize
        @street = ""
    end
end

attr_accessible allows you to list all the columns you want to allow Mass Assignment. The opposite of this is attr_protected which means this field I do NOT want anyone to be allowed to Mass Assign to. More than likely it is going to be a field in your database that you don't want anyone monkeying around with. Like a status field, or the like.


A quick & concise difference overview :

attr_accessor is an easy way to create read and write accessors in your class. It is used when you do not have a column in your database, but still want to show a field in your forms. This field is a “virtual attribute” in a Rails model.

virtual attribute – an attribute not corresponding to a column in the database.

attr_accessible is used to identify attributes that are accessible by your controller methods makes a property available for mass-assignment.. It will only allow access to the attributes that you specify, denying the rest.


Many people on this thread and on google explain very well that attr_accessible specifies a whitelist of attributes that are allowed to be updated in bulk (all the attributes of an object model together at the same time) This is mainly (and only) to protect your application from "Mass assignment" pirate exploit.

This is explained here on the official Rails doc : Mass Assignment

attr_accessor is a ruby code to (quickly) create setter and getter methods in a Class. That's all.

Now, what is missing as an explanation is that when you create somehow a link between a (Rails) model with a database table, you NEVER, NEVER, NEVER need attr_accessor in your model to create setters and getters in order to be able to modify your table's records.

This is because your model inherits all methods from the ActiveRecord::Base Class, which already defines basic CRUD accessors (Create, Read, Update, Delete) for you. This is explained on the offical doc here Rails Model and here Overwriting default accessor (scroll down to the chapter "Overwrite default accessor")

Say for instance that: we have a database table called "users" that contains three columns "firstname", "lastname" and "role" :

SQL instructions :

CREATE TABLE users (
  firstname string,
  lastname string
  role string
);

I assumed that you set the option config.active_record.whitelist_attributes = true in your config/environment/production.rb to protect your application from Mass assignment exploit. This is explained here : Mass Assignment

Your Rails model will perfectly work with the Model here below :

class User < ActiveRecord::Base

end

However you will need to update each attribute of user separately in your controller for your form's View to work :

def update
    @user = User.find_by_id(params[:id])
    @user.firstname = params[:user][:firstname]
    @user.lastname = params[:user][:lastname]

    if @user.save
        # Use of I18 internationalization t method for the flash message
        flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
    end

    respond_with(@user)
end

Now to ease your life, you don't want to make a complicated controller for your User model. So you will use the attr_accessible special method in your Class model :

class User < ActiveRecord::Base

  attr_accessible :firstname, :lastname

end

So you can use the "highway" (mass assignment) to update :

def update
    @user = User.find_by_id(params[:id])

    if @user.update_attributes(params[:user])
        # Use of I18 internationlization t method for the flash message
        flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
    end

    respond_with(@user)
end

You didn't add the "role" attributes to the attr_accessible list because you don't let your users set their role by themselves (like admin). You do this yourself on another special admin View.

Though your user view doesn't show a "role" field, a pirate could easily send a HTTP POST request that include "role" in the params hash. The missing "role" attribute on the attr_accessible is to protect your application from that.

You can still modify your user.role attribute on its own like below, but not with all attributes together.

@user.role = DEFAULT_ROLE

Why the hell would you use the attr_accessor?

Well, this would be in the case that your user-form shows a field that doesn't exist in your users table as a column.

For instance, say your user view shows a "please-tell-the-admin-that-I'm-in-here" field. You don't want to store this info in your table. You just want that Rails send you an e-mail warning you that one "crazy" ;-) user has subscribed.

To be able to make use of this info you need to store it temporarily somewhere. What more easy than recover it in a user.peekaboo attribute ?

So you add this field to your model :

class User < ActiveRecord::Base

  attr_accessible :firstname, :lastname
  attr_accessor :peekaboo

end

So you will be able to make an educated use of the user.peekaboo attribute somewhere in your controller to send an e-mail or do whatever you want.

ActiveRecord will not save the "peekaboo" attribute in your table when you do a user.save because she don't see any column matching this name in her model.


In two words:

attr_accessor is getter, setter method. whereas attr_accessible is to say that particular attribute is accessible or not. that's it.


I wish to add we should use Strong parameter instead of attr_accessible to protect from mass asignment.

Cheers!


attr_accessor is a Ruby method that gives you setter and getter methods to an instance variable of the same name. So it is equivalent to

class MyModel
  def my_variable
    @my_variable
  end
  def my_variable=(value)
    @my_variable = value
  end
end

attr_accessible is a Rails method that determines what variables can be set in a mass assignment.

When you submit a form, and you have something like MyModel.new params[:my_model] then you want to have a little bit more control, so that people can't submit things that you don't want them to.

You might do attr_accessible :email so that when someone updates their account, they can change their email address. But you wouldn't do attr_accessible :email, :salary because then a person could set their salary through a form submission. In other words, they could hack their way to a raise.

That kind of information needs to be explicitly handled. Just removing it from the form isn't enough. Someone could go in with firebug and add the element into the form to submit a salary field. They could use the built in curl to submit a new salary to the controller update method, they could create a script that submits a post with that information.

So attr_accessor is about creating methods to store variables, and attr_accessible is about the security of mass assignments.