views:

115

answers:

2

I have used RESTful techniques to generate a model (in fact, I am using Devise gem, which does that for me), and I have added new fields called first_name and last_name to the model. Migration went fine. I added attr_accessor :first_name, :last_name to the model and expected it would just work. But when I try to mass-assign new instances with Doctor.create({:first_name=>"MyName"}) etc., I am getting errors saying I can't mass-assign protected attributes.

I thought the whole point of using attr_accessor was to get around the protectedness of the fields of a model. Can you help me make sense of this message?

Edit: oh, and by the way the records do not get created either. I thought they should be since this is just a warning, but they are not on the database.

Edit2: here is my model

class Doctor < User
  has_many :patients
  has_many :prescriptions, :through=> :patients

  validates_presence_of :invitations, :on => :create, :message => "can't be blank"

  attr_accessor :invitations
end

and the schema, which doesn't have the first_name and last_name because they are created in the users table, which is the ancestor of doctors. I used single table inheritance.

create_table :doctors do |t|
  t.integer :invitations

  t.timestamps
end

and this is the migration to change the users table

add_column :users, :first_name, :string
add_column :users, :last_name, :string
add_column :users, :type, :string

EDIT: here is the seed file. I am not including the truncate_db_table method, but it works.

%w{doctors patients}.each do |m|
  truncate_db_table(m)  
end  

Doctor.create(:invitations=>5, :email=>"[email protected]", :first_name=>"Name", :last_name=>"LastName")
Patient.create(:doctor_id=>1, :gender=>"male", :date_of_birth=>"1991-02-24")
+1  A: 

Don't use attr_accessor here. ActiveRecord creates those automatically on the model. Also, ActiveRecord will not create a record if a validation or mass-assignment error is thrown.

EDIT: You don't need a doctors table, you need a users table with a type column to handle Rails Single Table Inheritance. The invitations will be on the users table. Ah, I see in your added code sample you do have type on users. Get rid of the doctors table, move invitations over to users, and I think you should be ok. Also get rid of the attr_accessor. Not needed.

Keep in mind that rails STI uses the same table for all classes and subclasses of a particular model. All of your Doctor records will be rows in the users table with a type of 'doctor'

EDIT: Also, are you sure you only want to validate presence of invitations on creation and not updates?

Dave Sims
When I don't use it, though, Rails tells me "method not found" as I am raking the seed file. What should I do in that case?
picardo
Can you post your model code and rake task? Also the migration that created the model. Make sure those columns are created in the db.
Dave Sims
ok, I added those to the post.
picardo
I didn't realize STI used only one table for all subclasses!
picardo
+3  A: 

Don't confuse attr_accessor with attr_accessible. Accessor is built into Ruby and defines a getter method - model_instance.foo # returns something - and a setter method - model_instance.foo = 'bar'.

Accessible is defined by Rails and makes the attribute mass-assignable (does the opposite of attr_protected).

If first_name is a field in your model's database table, then Rails has already defined getters and setters for that attribute. All you need to do is add attr_accessible :first_name.

rspeicher
oh, man. Is that right? Let me try this.
picardo
Now I'm getting "unknown attribute" error on invitations when I rake the seed file. I know I have this field in the database, though; it's in the migration file...
picardo
It's in the migration file, but did you run the migrations? Post your seeds file.
rspeicher
ok, I edited the post to include the seeds file.
picardo
Did you update the other stuff? You've still got attr_accessor in your model even though that's wrong.
rspeicher
This could be related to the fact that STI uses only one table to for all subclasses, which Dave Simms pointed out. I have a doctors table, which has invitations field, but it should be in the users table instead. d'oh!
picardo
If you're subclassing the User table, have you checked for attr_accessible in User itself? Or attr_protected, but I suspect it's the former. You can add fields to be accessible in subclasses by calling #attr_accessible in the subclass.
François Beausoleil