29674

“Cannot modify association because the source reflection class is associated via :has_many”

I've looked at other questions with a similar title and issue, but their solutions do not seem to apply to me. Sorry for not being more descriptive, I was already reaching the 150 limit.

<hr>

I have the following Models:

class Event < ActiveRecord::Base has_many :weeks, dependent: :destroy has_many :tests, through: :weeks accepts_nested_attribues_for :weeks, allow_destroy: true accepts_nested_attribues_for :tests, allow_destroy: true end class Week < ActiveRecord::Base belongs_to :event has_many :tests, dependent: :destroy end class Test < ActiveRecord::Base belongs_to :week end

And this view:

=form_for @event do |e| %h3 Event =render 'form', e: e # This partial contains the general data of the Event. %h3 Weeks %table %thead %th Start %th End %th X %tbody =e.fields_for :weeks do |w| =render 'week_fields', w: w =link_to_add_fields 'Add Week', e, :weeks # Custom helper that adds the fields of the week to the end of the table via JavaScript. %h3 Tests -@weeks.each do |week| %h5=week.start # Prints the 'start' of the Week. %table %thead %th Start %th End %th X %tbody = e.fields_for :tests, week.tests do |t| =render 'test_fields', t: t, s: week.id =link_to_add_fields 'Add Test', e, :tests, parent: week.id

This works fine for adding Events and Weeks, but when I try to add a new Test I get an error that says:

Cannot modify association 'Event#tests' because the source reflection class 'Test' is associated to 'Week' via :has_many.

I understand this is due to the way I got the associations in the view, but I'm not too sure how I should use them.

This is the code from the link_to_add_fields helper I'm using above:

def link_to_add_fields(name, f, association, parent: 0) new_object = f.object.send(association).klass.new id = new_object.object_id fields = f.fields_for(association, new_object, child_index: id) do |builder| render(association.to_s.singularize + "_fields", f: builder, s: parent) end link_to(name, '#', class: 'btn btn-default add_fields', id: "add_#{association}", data: {id: id, association: association, fields: fields.gsub("\n", "")}) end

The helper generates code that's later introduced onto the form via JavaScript. It's a modified version of the one used on a Railcasts episode.

This is what I'm using on my controller to limit the parameters:

def event_params params.require(:event).permit(:name, :start, :end, :place, tests_attributes: [:id, :name, :date, :time, :week_id], weeks_attributes: [:id, :start, :end, :_destroy]) end

This works perfectly for editing the data of an already existing Test, but not for new ones generated by the helper. I was reading that (in my case) tests_attributes should be within week_attributes, since the Test I'm trying to modify belongs to Week. like this:

def event_params params.require(:event).permit(:name, :start, :end, :place, weeks_attributes: [:id, :start, :end, :_destroy, test_attributes: [:id, :name, :date, :time, :week_id]]) end

I do not know how to make the form to follow this association (or if this is actually a solution). I know that Test cannot be modified cause it's being pulled as read-only, but I'm not sure how to arrange the association within the view, so that it can be saved when I update Event.

Any pointers are greatly appreciated.

Answer1:

This got a bit tricky, but I got it. It was a matter of modifying what was within what.

I got it working by using something like this:

%h3 Tests -@weeks.each do |week| =e.fields_for :weeks, week do |w| %h5=week.start # Prints the 'start' of the Week. %table %thead %th Start %th End %th X %tbody = w.fields_for :tests, week.tests do |t| =render 'test_fields', t: t, s: week.id =link_to_add_fields 'Add Test', w, :tests, parent: week.id

The trick was to make the fields generated for Test to be within the fields generated for Week. So first I added e.fields_for :weeks, week do |w| which would indicate that the following fields belong to Weeks, then I limited the data to the current selected Week by using the previous each loop.

Next I used the new w association in the fields_for for Test. This would make every field generated by the render to be in the format of "event[weeks_attributes][week_id][tests_attributes][test_id]" which is what I was expecting for the parameters in my controller.

The same is done in link_to_add_fields where I change the e association that belonged to Event, for w which belongs to Weeks. This would also change the format of the new records generated via JavaScript, using the format I previously described.

Finally, I had to change the format expected in the controller. I was using the singular form test, it had to be changed to plural, as it is possible to add multiple records in the form. So I ended up with something like this:

def event_params params.require(:event).permit(:name, :start, :end, :place, weeks_attributes: [:id, :start, :end, :_destroy, tests_attributes: [:id, :name, :date, :time, :week_id]]) end

And it works!

Answer2:

I think you should use complex nested form structure here to add test with week. e.fields_for :tests should be inside the e.fields_for :weeks block for proper associations.

You can get idea from here Deep Nested Rails 4 Form

Recommend

  • MySql: how to make subquery and count all rows where id is the same in two tables
  • Inplace substitution from ConfigParser
  • Return specific text from MySQL using LOCATE and SUBSTRING
  • Raw SQL to SQLAlchemy
  • Python sort a list with external key
  • Displaying MongoDB Documents with HTML
  • Auto regex - fix needed
  • document.execCommand doesn't work in angularJS
  • using class interface as a parameter in wcf service
  • WPF Drag-to-scroll doesn't work correctly
  • jquery accordion - remember active area when clicking back from an external link
  • Recursion Control flow
  • In-place sed command not working
  • How do I include a SWC in an AS2 Flash project?
  • How to add a focus style to an editable ComboBox in WPF
  • Web.config system.webserver errors
  • Force show.bind execution
  • How do I superscript characters in a UIButton?
  • Appending Character to Character Array In C
  • Disable Enter in editText android
  • Meteor helpers not available in Angular template
  • Is my CUDA kernel really runs on device or is being mistekenly executed by host in emulation?
  • Sony Xperia Z Tablet not found by adb
  • How to recover from a Spring Social ExpiredAuthorizationException
  • ILMerge & Keep Assembly Name
  • Large data - storage and query
  • How can I estimate amount of memory left with calling System.gc()?
  • WOWZA + RTMP + HTML5 Playback?
  • How to pass list parameters for each object using Spring MVC?
  • using conditional logic : check if record exists; if it does, update it, if not, create it
  • How to delete a row from a dynamic generate table using jquery?
  • json Serialization in asp
  • Rails 2: use form_for to build a form covering multiple objects of the same class
  • python regex in pyparsing
  • Hits per day in Google Big Query
  • How to stop GridView from loading again when I press back button?
  • Android Google Maps API OnLocationChanged only called once
  • Linking SubReports Without LinkChild/LinkMaster
  • Easiest way to encapsulate a HTML5 webpage into an android app?
  • How can I use threading to 'tick' a timer to be accessed by other threads?