7

I have the same exact schema as described here with a polymorphic join table: http://aaronvb.com/articles/a-polymorphic-join-table.html

class Location < ActiveRecord::Base
  has_many :note_joins, as: :notable
  has_many :notes, through: :note_joins
end

class Checkpoint < ActiveRecord::Base
  has_many :note_joins, as: :notable
  has_many :notes, through: :note_joins
end

class NoteJoin < ActiveRecord::Base
  belongs_to :notable, polymorphic: true
  belongs_to :note
end

class Note < ActiveRecord::Base
  attr_accessible :content, :user_id
  belongs_to :notable, polymorphic: true
  belongs_to :user

  has_many :note_joins
end

I want to be able to create and update multiple types of polymorphic associations at once, instead of having to do @note.locations << Location.first or @note.checkpoints << Checkpoint.first.

Something like @note.create note_joins_params and @note.update note_joins_params would be awesome.

The way I've been able to achieve the creation part so far is by passing an array of attributes to @note.note_joins.create, e.g. :

note_joins_params = [{"notable_id"=>"9225", "notable_type"=>"Location"}, {"notable_id"=>"9220", "notable_type"=>"Checkpoint"}]
@note.note_joins.create note_joins_params

Is there a more Rails-esque way to accomplish this, or proper attributes hash syntax similar to accepts_nested_attributes or something similar?

Also the only way I know of how to do an update is to first delete all the existing records in the join table and then re-create them, i.e.

@note.note_joins.destroy_all
new_note_joins_params = [{"notable_id"=>"9225", "notable_type"=>"Location"}, {"notable_id"=>"9220", "notable_type"=>"Checkpoint"}]
@note.note_joins.create new_note_joins_params
1
  • Rails don't have support for accept_nested_attribute for polymorphic association. Maybe you should check this thread stackoverflow.com/q/3969025/4587148 Commented Dec 27, 2016 at 11:24

1 Answer 1

6
+25

For what you want to accomplish, Rails doesn't really have this 'smart_way' of accepting nested attributes if you don't specify the source_type. Refer to this The other side of polymorphic :through associations

But you can try this:

class Location < ActiveRecord::Base
  has_many :note_joins, as: :notable
  has_many :notes, through: :note_joins

  accepts_nested_attributes_for :notes
end

class Checkpoint < ActiveRecord::Base
  has_many :note_joins, as: :notable
  has_many :notes, through: :note_joins

  accepts_nested_attributes_for :notes
end

class NoteJoin < ActiveRecord::Base
  belongs_to :notable, polymorphic: true
  belongs_to :note

  accepts_nested_attribute_for :note
end

class Note < ActiveRecord::Base
  attr_accessible :content, :user_id
  belongs_to :user

  has_many :note_joins
  # add these, otherwise you won't be able to do nested attributes
  # refer to the blog post I link above.
  has_many :locations, through: :note_joins, source: :notable, source_type: 'Location'
  has_many :checkpoints, through: :note_joins, source: :notable, source_type: 'Checkpoint'
end

Then in your form, build something like this:

# In your form

# if you want to create from the note, do this
<%= form_for @note do |f| %>
  <%= f.text_field :some_note_field %>
  <%= text_field_tag 'note[locations_attributes][][some_location_field]' %>
  <%= text_field_tag 'note[checkpoints_attributes][]some_checkpoint_field]' %>
<% end %>

# if you want to create from the location/checkpoint, do this.
<%= form_for @location do |f| %>
  <%= f.text_field :name %>
  <%= text_field_tag 'location[notes_attributes][][body]' %>
<% end %>


# In your Location/Checkpoint controllers

def create
  @location = Location.create(location_params)
  redirect_to @location
end

def location_params
  params.required(:location).permit(:name, notes_attributes: [:body])
end

# In your Note controller
def create
  @note = Note.create(note_params)
  redirect_to @note
end

def note_params
  # the goal here is to create a set of params like this
  # { note_field: 'foobar',
  #   locations_attributes: [{name: 'somewhere'}],
  #   checkpoints_attributes: [{description: 'lol'}] }
  params.required(:note).permit(:note_field, locations_attributes: [:location_field], checkpoints_attributes: [:checkpoint_field])
end
Sign up to request clarification or add additional context in comments.

4 Comments

this doesn't answer the question I asked..I'd like to create not just a Location, but different types of polymorphic models at once.
@jsurf what do you mean by all at once? do you mean creating a note along with location, checkpoint at the same time?
I phrased my question incorrectly. I originally had an STI setup where it was easy to create a list made up of different types of inherited models at once, since I only needed to reference the ID from one table. Now that i've switched to polymorphic associations, I now have ID and type. I'd like to create a list made up of both Locations and Checkpoints at once. So what I am doing is adding to the join table using array of ID and type: params = [{"notable_id"=>"9225", "notable_type"=>"Location"}, {"notable_id"=>"9220", "notable_type"=>"Checkpoint"}] @note.note_joins.create params
@jsurf please see my edit. unfortunately, it doesn't have a way to dynamically do the nesting attributes, you just need to specify them in the Note model whenever you add a new polymorphic association.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.