A Rails newbie here. I believe I am rather confused as to how fields_for operates within a rails form. I have a has many through relationship formed with Projects, Organizations, and Affiliations in the middle. A Project can have many Organizations, and an Organization can have many Projects. Affiliations is in the middle with a :role attribute that is meant to be edited to show what role an Organization plays within a Project. I was able to get the functionality of adding many Organizations to a Project working just fine. I utilized chosen-rails to implement a neat UI to select multiple, search, etc. I can add and remove Organizations to a Project just fine with that. However, upon trying to implement some way to manipulate the role each Organization plays in a Project through altering the role attribute with a fields_for form, submissions of the original form break. I have tried many variations of the fields_form, including one from a guide here.
In project.rb:
def affiliations_attributes=(affiliations_attributes)
affiliations_attributes.each do |i, affiliation_attributes|
if affiliation_attributes[:role].length > 0
self.affiliations.build(affiliation_attributes)
end
end
end
In the project form:
<%= f.fields_for :affiliations do |affiliation_builder|%>
<%= affiliation_builder.label :role %>
<%= affiliation_builder.text_field :role %>
<% end %>
With this setup, on submission of the form, I receive an error stating that 'Affiliations organization must exist.' I don't understand why, since I'm not even editing the Affiliations or Organization here. Is there something I am missing?
Prior to this, I tried doing this for a fields_for form:
<%= f.fields_for :affiliations do |affiliation|%>
<%= affiliation.label :role, ("Role in Project") %>
<%= affiliation.text_field :role, autofocus: true, autocomplete: 'off', placeholder: 'e.g. Donor, Founder', class: 'form-control' %>
<% end %>
Projects Controller:
def project_params
params.require(:project).permit(:organization, {organization_ids: []},
:stream_name, :implementation_date, :narrative,
:length, :primary_contact,:number_of_structures, :structure_description,
:name, affiliations_attributes: [:id, :role], photos: [])
end
Now, surprisingly, this works when updating the role attribute in Affiliations. I can also add Organizations to the project with no issue, and add subsequent roles to the created Affiliation. However, the problem arises when I try to remove an Organization. Rails flashes an ActiveRecord::RecordNotFound in ProjectsController#update error stating "Couldn't find Affiliation with ID= for Project with ID=". I have no idea what is going on here and have been banging my head against the wall for a couple days now trying to fix this. The only thing I can think is somehow there's an ID matching issue, or some parameter not being properly passed. One thing I did notice when toying around with the project_params on the second method above and removing :id from affiliations_attributes is that I get flashed with the same error message about an 'Affiliations organization' being required. Perhaps there's some way to pass an organization ID with the affiliation_builder method? Any help or guidance would be greatly appreciated here. Thank you.
class Affiliation < ApplicationRecord
belongs_to :project
belongs_to :organization
end
class Organization < ApplicationRecord
has_many :affiliations
has_many :projects, through: :affiliations
end
class Project < ApplicationRecord
has_many :affiliations
has_many :organizations, through: :affiliations
accepts_nested_attributes_for :affiliations
def affiliations_attributes=(affiliations_attributes)
affiliations_attributes.each do |i, affiliation_attributes|
if affiliation_attributes[:role].length > 0
self.affiliations.build(affiliation_attributes)
end
end
end
end
CodePudding user response:
Why |i, affiliation_attributes| ? use |affiliation_attributes|
CodePudding user response:
I believe I have found a solution, though I am not sure if it is technically correct. I looked at the order of the attributes being pushed when Project was updated, and I noticed that organization_ids was being pushed in the Hash before affiliations_attributes. So I think it was trying to update something that just didn't exist, and was giving some misleading information (at least to me). I was able to fix this issue by forcing the affiliations_attributes to be pushed first in a separate update by itself, and then pushing the rest of the project attributes in the main update.
def project_organization_params
params.require(:project).permit(:id, {affiliations_attributes: [:id,
:role]})
end
And then update with:
@project = Project.find(params[:id])
@project.update(project_organization_params)
if @project.update(project_params)
...
Everything seems to be working just fine now.