Rails’ form_for conveniently uses “record identification” to figure out the correct URL based on the model:
1 2 | |
It’s also used to figure out the HTTP verb, the field name prefixes, HTML classes and HTML id.
So this form would POST to admin_items_path for a new record, or PUT to admin_item_path(@item) for an existing record.
But it can break when you use STI (Single Table Inheritance).
1 2 3 | |
1 2 3 | |
You want this form to PUT to admin_item_path(@item) but Rails will PUT to the non-existent admin_special_item_path(@item).
The Rails form helper guide says you can’t rely on record identification with STI.
You can, though, with some fiddling. This is what I just did:
1 2 3 4 | |
#becomes changes the SpecialItem to an Item for the benefit of the record identification.
But then the form will have a plain Item, so our type dropdown won’t pre-select “SpecialItem”. So to fix that, we restore the form object on line 2.
Because #becomes mutates the attributes inside the original @item, we must use #becomes again on line 2.
My first attempt was to do @item.dup.becomes(Item) on line 1 so the @item is untouched. But that breaks the form’s new_record? check…
Whatever you go with, make sure to test your forms, as this is fragile territory.