简体   繁体   中英

Rails Save Hash in Model Attribute Submitted by Simple Form

I am building a Rails5 app where I have a model Training with an attribute participant_ratings , a hash which should hold the ratings of all participants assigned to the training in the way participant.id => rating value . I already managed to build the form for submitting the hash.

<%= simple_form_for @training do |f| %> 
...
  <div id="ratings-container" style="display: inline-block; vertical-align: top">
    <%= f.label t('trainings._form.ratings') %>
    <% Participant.all.order(:name).each do |p| %>
      <span class="checkbox_container">
        <% current_rating = @training.participant_ratings[p.id].nil? ? '' : @training.participant_ratings[p.id] %>
        <label id='star<%= "#{p.id}" %>' data-rating='<%= "#{current_rating}" %>' style="margin-bottom: 12px;" ></label>
        <input id="rating<%= "#{p.id}" %>" name="participant_ratings[<%= "#{p.id}" %>]" type="hidden" value="<%= "#{@training.participant_ratings[p.id]}" %>" />
      </span>
      <script>
        $('#star<%= "#{p.id}" %>').raty({
            score: function() {
                return $(this).attr('data-rating');
            },
            half  : true,
            readOnly: <%= !@training.participants.include?(p) %>,
            path: '/assets',
            click : function(score, evt) {
                $('#rating<%= "#{p.id}" %>').val(score);
            }
        });
        $('#training_participant_ids_<%= "#{p.id}" %>').change(function () {
            var star = $('#star<%= "#{p.id}" %>');
            if(document.getElementById('training_participant_ids_<%= "#{p.id}" %>').checked) {
                star.raty('readOnly', false);
            } else {
                star.raty('cancel');
                star.raty('readOnly', true);
                $('#rating<%= "#{p.id}" %>').val("");
            }
        })
      </script>
    <% end %>
  </div>   
...
<% end %>

I can see that the hash is sent correctly to the server:
"participant_ratings"=>{"3"=>"5", "1"=>"3.5", "2"=>""}

But I cannot manage this hash to be saved in the Training s attribute. I get the error: Attribute was supposed to be a Hash, but was a ActionController::Parameters. -- <ActionController::Parameters {"3"=>"5", "1"=>"3.5", "2"=>""} permitted: false> Attribute was supposed to be a Hash, but was a ActionController::Parameters. -- <ActionController::Parameters {"3"=>"5", "1"=>"3.5", "2"=>""} permitted: false>

My controller code:

class TrainingsController < BaseController
...
  def update
    @training = Training.find(params[:id])
    @training.updated_date_time = DateTime.now
    @training.participant_ratings = params[:participant_ratings]

    if @training.update(training_params)
      flash[:notice] = t('flash.training.updated')
      redirect_to @training
    else
      render 'edit'
    end
  end

  private
    def training_params
      params.require(:training).permit(:village, :topic, :user_id, :start_time, :end_time, {:participant_ids => []}, :participant_ratings)
    end
end

My Training class:

class Training < ApplicationRecord
...

  serialize :participant_ratings, Hash

...
end

What do I have to change to save the hash in the participant_ratings attribute in my Training model?

You can try

@training.participant_ratings = params[:participant_ratings].to_hash

But i think storing it as json will be even better, so:

@training.participant_ratings = params[:participant_ratings].to_json

And then to read it as a hash:

hash = JSON.parse(@training.participant_ratings)

There's a an error in the controller. update method doesn't get the ratings param, so it goes unsaved. Here's the workaround:

class TrainingsController < BaseController
...
  def update
    @training = Training.find(params[:id])
    @training.updated_date_time = DateTime.now
    @training.participant_ratings = params[:participant_ratings]
    @training.assign_attributes(training_params)

    if @training.save
      flash[:notice] = t('flash.training.updated')
      redirect_to @training
    else
      render 'edit'
    end
  end

  private
    def training_params
      params.require(:training).permit(:village, :topic, :user_id, :start_time, :end_time, {:participant_ids => []})
    end
end

Firstly, (as pointed out by Dmitry Kukhlevsky), I had to make sure that the participants_rating -hash was inside the training -hash when submitted by my form, so I had to change the name attribute in my hidden form input for the rating:

<input id="rating<%= "#{p.id}" %>" name="training[participant_ratings][<%= "#{p.id}" %>]" type="hidden" value="<%= "#{@training.participant_ratings[p.id]}" %>" />

Secondly, I had to whitelist the keys for my participant_ratings -hash so that they are permitted:

def training_params
  ratings_keys = params[:training].try(:fetch, :participant_ratings, {}).keys
  params.require(:training).permit(:village, :topic, :user_id, :start_time, :end_time, {:participant_ids => []}, participant_ratings: ratings_keys )
end

I found this solution in this rails thread

After that participant_ratings was saved just fine!

Still thanks a lot to Dmitry for all his help!!!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM