Using attr_accessible in a join model with has_many :through relationship
I 开发者_如何学Chave a USER that creates a COMPANY and become an EMPLOYEE in the process. The employees table has an :user_id
and a :company_id
.
class User
has_many :employees
has_many :companies, :through => :employees
class Employee
belongs_to :user
belongs_to :company
attr_accessible :active
class Company
has_many :employees
has_many :users, :through => employees
Pretty basic. But here's the thing, the resource EMPLOYEE has other attributes than its foreign keys, like the boolean :active
. I would like to use attr_accessible
, but this causes some problems. The attribute :user_id
is set right, but :company_id
is nil.
@user.companies << Company.new(...)
Employee id:1 user_id:1 company_id:nil
So my question is: if :user_id
is set right, despite it is not an attr_accessible
, why :company_id
isn't set right just the same? It shouldn't be an attr_accessible
.
I'm using Rails 3.0.8, and have also tested with 3.0.7.
There are a lot of bits working together here.
You definitely want to use attr_accessible on all models. (Google "hack rails mass assignment" and read the Rails Guide on mass assignment.)
Once you add attr_accessible to a model, all assignments from hashes (mass assignments) are disabled except those you explicitly allow. However, you can still assign values directly, one at a time.
Foreign keys seem like a good thing to exclude from mass assignment, so don't list them in attr_accessible.
The .create and .build methods are not using mass assignment so they can set the value of one foreign key association. If there are several associations, as best I can tell, you'll have to set all but the first separately.
Finally, the actual IDs for the foreign keys are created by the database, not by ActiveRecord. So you'll either have to create parent and child records simultaneously, or you'll have to save the child first before you can assign the foreign key in the parent. Otherwise there is no ID available for the assignment.
It's not clear to me from your example how Employee is getting instantiated. But since the Employee belongs to both User and Company, I think something like this might work, assuming @user already exists:
company = @user.companies.create(..) # fills in company.user_id and saves to DB
employee = @user.employees.build(..) # fills in employee.user_id but does NOT save yet
employee.company = company # fills in employee.company_id
employee.save # now save to DB
The company_id is nil simply because the Company hasn't been saved to the database yet - Company.new simply creates the object in memory without saving it yet.
If you do:
@user.companies << Company.create(..)
or
@user.companies << Company.first
They should both work. There's even a shorter method which I think should work too:
@user.companies.create(..)
It all depends at which point you want to save the association. In some cases, it may be better not to save the employee and company models straight away, and instead wait for when the parent model (User) is saved. In which case you can use:
@user.companies.build(..)
(which is similar to the code in your example).
In terms of your active
boolean attribute on the Employee model, if this is a column in the database, you don't need to explicitly declare attr_accessible
for it - it'll be accessible by default.
精彩评论