11

I've got a pretty simple (I think) single-table inheritance (STI) setup in my Rails app.

There is a User model with a nested resource Post. Using STI, I have it so that some Posts can be Post::Urgent objects.

I noticed that my URL helpers like <%= [@user, @post] %> needed to be hard-coded to <%= user_post_path[@user, @post] %> or else I'd end up seeing errors about Rails not finding the user_post_urgent_path method. Okay, that was reasonably easy.

(An alternate way of doing this is also <%= [@user, post: @post] %>.)

Well, for some reason I'm not able to figure out how to make the form_for method adjusted in the same way. When I write simply

<%= form_for [@user, @post] do |f| %>

, I get similar errors to the URL helper case, but in the form context: undefined method 'user_post_urgen_path'.

I fixed this by specifying:

<%= form_for [@user, @post], url: :user_post do |f| %>

But this doesn't completely work, because when I go to submit the form, I get problems in my controller, saying that strong parameters line params.require(:post) failed:

param is missing or the value is empty: post

If I inspect the params I find that the form is passing a post_urgent object and not a post object.

I could, of course, manually put in some logic that always says if !params[:post] and params[:post_urgent], then params[:post] = params[:post_urgent]. But that just seems way too hacky, especially for Rails.

Is there a better way to make a form like this "just work" regardless of what subclass of the Post model it actually happens to be?

isthmuses
  • 1,316
  • 1
  • 17
  • 27

2 Answers2

24

Not sure if you found a solution already, but I am using the following for my forms

= form_for [@user, @post.becomes(Post)] do |f|
  - f.object = @post.becomes @post.class

reference: http://thepugautomatic.com/2012/08/rails-sti-and-form-for/

AxelTheGerman
  • 986
  • 13
  • 26
0

I had some nested models initialized in the controller, empty unsaved models to work with accepts_nested_attribues_for and becomes empties them for some reason, so instead, I acted on the controller strong params, not the cleanest, I know.

  def unpermitted_model_params
    polymorphic_form_params = params.to_unsafe_hash
        .slice('sub_model1', 'sub_model2')
    form_values = polymorphic_form_params.first.to_a.second
    ActionController::Parameters.new(parent_model: form_values)
  end

  def allowed_params
    unpermitted_model_params.require(:parent_model)
                            .permit(:type, :etc, :etc)
  end
ecoologic
  • 10,202
  • 3
  • 62
  • 66