Search code examples
ruby-on-railsmodelssimple-form

Rails Mass Assignment


I have a site that keeps track of SAT tutoring sessions. The curriculum that the students learn is a collection of rules. I have a model for each tutoring session called "Sittings" and the rules model is called "Rules". I want the site admin to be able to enter a Sitting by date, and then use checkboxes to select which "rules" the student got wrong in that sitting. I'm a little confused as to how I can create the form to pull out specific rules without adding attributes to my Sitting model of rule1, rule2, etc. I'm using simple_form to create my forms.

My Sitting model:

class Sitting < ActiveRecord::Base

attr_accessible :date, :comment, :rule_id, :user_id

validates :date, presence: true

belongs_to :user

has_many :combos
has_many :rules, :through => :combos

end

My Rules model:

class Rule < ActiveRecord::Base

attr_accessible :name, :subject, :session_id, :hint_id, :question_id, :trigger_id

validates :name, presence: true
validates :subject, presence: true

has_many :questions
has_many :triggers
has_many :hints
has_many :combos
has_many :sittings, :through => :combos

end

My Combo model:

class Combo < ActiveRecord::Base

belongs_to :sitting
belongs_to :rule

end

Edit:

Here's what I have tried for the form. It does create the checkbox form, but my DB isn't updating the rule_id. (shows as nil when I create a Sitting)

form:

<%= simple_form_for(@sitting, html: { class: "form-horizontal"}) do |f| %>
  <%= f.error_notification %>

  <% Rule.all.each do |rule| %>
        <%= check_box_tag "sitting[rule_ids][]", rule.id, @sitting.rule_ids.include?(rule.id) %> <%= rule.id %>
  <% end %>

  <div class="form-group">
    <%= f.input :comment, as: :text, input_html: { rows: "2", :class => "form-control" }, label: "Comments:" %>
  </div>
  <div class="form-group"> 
    <%= f.date_select :date, as: :date, label: "Taken Date:" %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

I updated my strong params to allow an array:

def create
@sitting = Sitting.new(sitting_params)

respond_to do |format|
  if @sitting.save
    format.html { redirect_to @sitting, notice: 'Sitting was successfully created.' }
    format.json { render action: 'show', status: :created, location: @sitting }
  else
    format.html { render action: 'new' }
    format.json { render json: @sitting.errors, status: :unprocessable_entity }
  end
end
end

def sitting_params
  params.require(:sitting).permit(:comment, :date, :user_id, :rule_id => [])
end

Am I missing something in order to update the Sitting.rule_id properly? I get the following error in my logs:

WARNING: Can't mass-assign protected attributes for Sitting: rule_ids
app/controllers/sittings_controller.rb:27:in `create'

Solution

  • Just to summarize what we got through over the chat.

    Firstly, you do not need both attr_accessible and strong_params at the same time. I've posted another answer some time ago explaining how those two approaches differs from each other.

    You're running rails 4, so you should take advantage of strong params and not use protected_attributes gem. In short, remove this gem from you Gemfile as well as all attr_accessible calls.

    As Marian noticed, you have a typo in your strong params method, you need to permit rule_ids, not rule_id. rule_id column is obsolete, as sitting has_many :rules :through rather than sitting belongs_to :rule - most likely it is an artifact of old association code.

    As soon as rule_ids are assigned within your model, it will create new join models in your join table, creating an association between given sitting and passed rules.