Creating multiple polymorphic records at once rails
Asked Answered
F

1

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
Flier answered 22/12, 2016 at 22:2 Comment(1)
Rails don't have support for accept_nested_attribute for polymorphic association. Maybe you should check this thread https://mcmap.net/q/328114/-accepts_nested_attributes_for-with-belongs_to-polymorphic/4587148Unplumbed
I
6

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
Imply answered 27/12, 2016 at 19:50 Comment(4)
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.Flier
@Flier what do you mean by all at once? do you mean creating a note along with location, checkpoint at the same time?Imply
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 paramsFlier
@Flier 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.Imply

© 2022 - 2024 — McMap. All rights reserved.