iamse7en iamse7en - 16 days ago 8
JSON Question

Rails: form_for with json: undefined method to_model for Hash

Converted my site to use redis so a JSON array is sent to my views instead of an ActiveRecord array. Made all the necessary conversions like

model.attribute
to
model['attribute']
.

However, can't figure out getting my form_for to work. User can make a prediction on a game. The same
_form.html.erb
is used to create or update the prediction. The error I get,
undefined method 'to_model' for #<Hash:0x007ff28d5f19c8>
occurs on both of these lines:

_form.html.erb
<%= form_for [game, (prediction || Prediction.new)], remote: true do |f| %>
...
<%= link_to "Delete", [game, prediction], method: :delete, remote: true, class: 'btn btn-xs btn-danger' if prediction.present? %>


Any ideas? If you need more info, read below:

The link and partial:

# index.html.erb
@games.each do |game|
...
<%= prediction_form_link(@predictions, game) %>
<%= render partial: 'predictions/form', locals: { game: game, prediction: prediction_for(@predictions, game) } %>


Basically, load partial with form and pass in
game
and
prediction
, but also determine whether
prediction
already exists to be updated instead of created:

def prediction_for(predictions, game)
predictions["#{game['id']}"].first if predictions["#{game['id']}"].present?
end
def prediction_form_link(predictions, game)
if prediction = prediction_for(predictions, game)
... # code that displays what this button looks like. Partial is loaded in a modal.


Thank you.

Answer

Okay, I finally figured it out after spending lots of time in pry and old SO posts. Since I'm now passing JSON, I have to modify form_for to use OpenStruct, and be very specific about what I want it to do. Basically had to modify the partial and my js.erbs.

# _form.html.erb
form_for(OpenStruct.new(prediction || {}), as: :prediction, remote: true, url: (prediction ? game_prediction_path([game['id']], [prediction['id']]) : game_predictions_path(game['id'], Prediction.new)), method: (prediction ? :put : :post) ) do |f| %>
...
<%= link_to "Delete", game_prediction_path([game['id']], [prediction['id']]), method: :delete, remote: true, class: 'btn btn-xs btn-danger' if prediction.present? %>

New OpenStruct to either grab the existing prediction or {} for a new one. I'm not even sure about which of these are absolutely necessary, but it's working. Classify as a :prediction and then specify the paths, parameters, and methods in the two situations (#create or #update), since I'm using the same partial for both.

Then I had to slightly modify my create.js.erb, destroy.js.erb, and update.js.erb files to send the data back to the views as_json.

# create.js.erb for example
$('#game-<%= @game.id %>').modal("hide");
$('.modal-backdrop').remove();
$('body').removeClass('modal-open');
$('#user-prediction-<%= @game.id %>').html('<%= j prediction_form_link(@predictions.as_json, @game.as_json) %>');
$('#user-prediction-<%= @game.id %>').append('<%= j render partial: "predictions/form", locals: { game: @game.as_json, prediction: prediction_for(@predictions.as_json, @game.as_json) } %>')