Reputation: 8189
I have two models connected by a join table:
class Publication < ActiveRecord::Base
attr_accessible :title, :author_attributes, :translator_attributes
has_many :publication_contributors
has_many :authors, :through => :publication_contributors, :source => :contributor,
:conditions => {:publication_contributors => {:contributor_type => "Author"}}
has_many :translators, :through => :publication_contributors, :source => :contributor,
:conditions => {:publication_contributors => {:contributor_type => "Translator"}}
accepts_nested_attributes_for :authors, :translators
end
class Contributor < ActiveRecord::Base
attr_accessible :name
has_many :publications, :through => :publication_contributors
has_many :publication_contributors
end
class PublicationContributor < ActiveRecord::Base
attr_accessible :contributor_type
belongs_to :publication
belongs_to :contributor
end
Note that the join table has a third attribute (beside publication_id and contributor_id), which is called contributor_type. This attribute might contain a role such as "Author", "Translator", "Editor", or something else. In my Publication model, I created a pair of associations for two of the more common contributor_types, "Author" and "Translator". These associations work well at retrieving the relevant data, such as with @publication.authors
. It sputters when creating these associations through a nested form, however.
My form looks something like this:
<%= form_for @publication, :remote => true do |f| %>
<%= f.label :title %>:
<%= f.text_field :title %>
<%= f.fields_for :authors do |builder| %>
<%= builder.label :name, "Author" %>:
<%= builder.text_field :name %>
<% end %>
<%= f.fields_for :translators do |builder| %>
<%= builder.label :name, "Translator" %>:
<%= builder.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
In my controller:
def create
publication = Publication.create(params[:publication])
end
The form renders as expected, but it sputters during the create action. I had hoped that Rails would know to magically assign the proper contributor_type based on the conditions in the Publication associations, such as:
has_many :authors, :through => :publication_contributors, :source => :contributor,
:conditions => {:publication_contributors => {:contributor_type => "Author"}}
Unfortunately, it does not. I get this error during creation:
Mysql2::Error: Column 'contributor_type' cannot be null
This makes me think that my only recourse is to manually assign the contributor_type in a before_create
callback, but if that's the case, how do I determine which type to assign? The parameters have the proper type in their name, for instance:
publication[authors_attributes][0][name]
Is there some way to access that information in the model layer?
UPDATE:
My new
action:
def new
@publication = Publication.new
publication_contributor = @publication.publication_contributors.build
contributor = publication_contributor.contributor.build
end
It throws this error on the final line (the one setting contributor
):
undefined method `build' for nil:NilClass
Upvotes: 4
Views: 3970
Reputation: 29599
Can you try the following?
class Publication < ActiveRecord::Base
attr_accessible :title, :publication_contributors_attributes
has_many :publication_contributors
has_many :authors, through: :publication_contributors, source: :contributor, conditions: { publication_contributors: { contributor_type: 'Author' } }
has_many :translators, through: :publication_contributors, source: :contributor, conditions: { publication_contributors: { contributor_type: 'Translator' } }
accepts_nested_attributes_for :publication_contributors
end
class Contributor < ActiveRecord::Base
attr_accessible :name
has_many :publications, through: :publication_contributors
has_many :publication_contributors
end
class PublicationContributor < ActiveRecord::Base
attr_accessible :contributor_type
belongs_to :publication
belongs_to :contributor
accepts_nested_attributes_for :contributor
end
<%= form_for @publication, :remote => true do |f| %>
<%= f.label :title %>:
<%= f.text_field :title %>
<%= f.fields_for :publication_contributors do |pc_form| %>
<%= pc_form.hidden_field :contributor_type %>
<%= pc_form.fields_for :contributor do |c_form| %>
<%= c_form.label :name, "Author" %>:
<%= c_form.text_field :name %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
UPDATE: controller code
@publication.publication_contributors.build
@publication.publication_contributors.each do |pc|
pc.build_contributor
end
Upvotes: 7