nested attributes and adding attributes through the controller?
-
21-09-2019 - |
Question
As this question is hard to describe, that's the best title i could come up with, so here is some code.
Given three Models Parent, Child && Grandchild.
Parent < ActiveRecord::Base
has_many :children
has_many :grandchildren
accepts_nested_attributes_for :child
end
Child < ActiveRecord::Base
belongs_to :parent
has_many :kids, :as => :grandchildren #this is just an example
accepts_nested_attributes_for :grandchild
end
Grandchild < ActiveRecord::Base
belongs_to :parent
belongs_to :child
end
I'd like to add the current_user.id to both the child record and Grandchild record that gets created in during Parent#new. I've used hidden fields for now, because i couldn't find a good way to add them.
Maybe someone can help by creating a callback to add the current_user.id on create? I've never had much luck getting that into a model anyways, but you are smart.
Thoughts?
Solution
Well, for one thing, I'd recommend a has_many :through
relationship from parent to grandchild (through child), and vice versa. See the "Association Join Models" section in the ActiveRecord Association Class Methods API for more details.
As to your main question, like you say, a callback is probably what you want. I think something like this should do it (though this is untested code):
class Parent
# ...somewhere at the top...
before_create :set_current_user_on_descendants
# ...somewhere in the main class body...
# (I assume parent['current_user'] is passed in as a typical
# parameter, and thus self.current_user is already set.)
def set_current_user_on_descendants
children.each { |c| c.current_user = self.current_user }
grandchildren.each { |gc| gc.current_user = self.current_user }
end
end
There are a few stylistic points that could be done differently. You could define a "descendants" method that returned children + grandchildren and iterate over that, for example, or you could implement the callback on the child and grandchild classes (in which case you might want to pull it out in to a module for maximum DRYness, though for a one-line method in only two classes that might be overkill). Depending on exactly when you want to update current_user, you might want to use before_save
or some other callback instead of before_create
- you can find a complete list of the available callbacks in the ActiveRecord callbacks API.
OTHER TIPS
I suppose its also possible to override the default save!
method
class Parent < ActiveRecord::Base
def save!
children.each { |c| c.current_user = @current_user }
grandchildren.each { |gc| gc.current_user = @current_user }
super
end
end
Untested as well. Not really sure this would work...