40

Original question

Two resources: users and animals. When creating a user, the client selects check boxes to say how many animals they have.

When the user form is submitted, it should not only create a new user record, but it should also create a bunch of records in the animal_users rich join table to represent each of the check boxes the client selected.

The issue I think is that I am not specifying something correctly for the checkbox part within the form. I have looked at the checkbox_tag API, the Rails Guides on Forms, and many websites and stackOverflow posts.

Thanks in advance, code below:

Original code (answer code further down):

models:

#models/user.rb
class User < ActiveRecord::Base
  has_many :animals, through: :animal_users
  has_many :animal_users
  accepts_nested_attributes_for :animal_users, allow_destroy: true
end

#models/animal.rb
class Animal < ActiveRecord::Base
  has_many :users, through: :animal_users
  has_many :animal_users
end

#models/animal_user.rb
class AnimalUser < ActiveRecord::Base
  belongs_to :animal
  belongs_to :user
end

The user form:

#views/users/_form.html.erb
    <%= form_for(@user) do |f| %>
  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>

  <div>
    <% Animal.all.each do |animal| %>
    <label>
        <%= check_box_tag "user[animal_ids][]", animal.id, f.object.animals.include?(animal) %>
        <%= animal.animal_name %>
    <% end %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Strong params within the users_controller.rb

def user_params
      params.require(:user).permit(:name, animal_users_attributes: [:_destroy, :id, :user_id, :animal_id])
end

Answer code here:

models:

#models/user.rb
class User < ActiveRecord::Base
  has_many :animals, through: :animal_users
  has_many :animal_users
end

#models/animal.rb
class Animal < ActiveRecord::Base
  has_many :users, through: :animal_users
  has_many :animal_users
end

#models/animal_user.rb
class AnimalUser < ActiveRecord::Base
  belongs_to :animal
  belongs_to :user
end

The user form:

#views/users/_form.html.erb
<%= form_for(@user) do |f| %>
  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>

  # Checkbox part of the form that now works!
    <div>
      <% Animal.all.each do |animal| %>
        <%= check_box_tag "user[animal_ids][]", animal.id, f.object.animals.include?(animal) %>
        <%= animal.animal_name %>
      <% end %>
    </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Strong params within the users_controller.rb

  def user_params
    params.require(:user).permit(:name, animal_ids: [])
  end

And just for the sake of completeness, here is what is passed into the server upon form submission:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"xyz=", "user"=>{"name"=>"Neil", "animal_ids"=>["1", "3"]}, "commit"=>"Create User"}
 Animal Load (0.2ms)  SELECT "animals".* FROM "animals"  WHERE "animals"."id" IN (1, 3)
  (0.1ms)  begin transaction
 SQL (0.2ms)  INSERT INTO "users" ("created_at", "name", "updated_at") VALUES (?, ?, ?)  [["created_at", "2015-03-26 15:29:00.478767"], ["name", "Neil"], ["updated_at", "2015-03-26 15:29:00.478767"]]
 SQL (0.1ms)  INSERT INTO "animal_users" ("animal_id", "created_at", "updated_at") VALUES (?, ?, ?)  [["animal_id", 1], ["created_at", "2015-03-26 15:29:00.479833"], ["updated_at", "2015-03-26 15:29:00.479833"]]
 SQL (0.0ms)  INSERT INTO "animal_users" ("animal_id", "created_at", "updated_at") VALUES (?, ?, ?)  [["animal_id", 3], ["created_at", "2015-03-26 15:29:00.480644"], ["updated_at", "2015-03-26 15:29:00.480644"]]
 SQL (0.1ms)  UPDATE "animal_users" SET "updated_at" = ?, "user_id" = ? WHERE "animal_users"."id" = 6  [["updated_at", "2015-03-26 15:29:00.481362"], ["user_id", 8]]
 SQL (0.1ms)  UPDATE "animal_users" SET "updated_at" = ?, "user_id" = ? WHERE "animal_users"."id" = 7  [["updated_at", "2015-03-26 15:29:00.482062"], ["user_id", 8]]
  (2.4ms)  commit transaction
Neil
  • 4,578
  • 14
  • 70
  • 155
  • 1
    In `User` model try using `accepts_nested_attributes_for :animal_users`, have a look at this post http://stackoverflow.com/questions/21983131/rails-4-nested-attributes-and-has-many-through-associaton-in-a-form – Sonalkumar sute Mar 26 '15 at 14:14
  • @Sontya Thank you for your response. I think I am getting closer, though not quite there yet. – Neil Mar 26 '15 at 14:34
  • 2
    change your view `<% Animal.all.each do |animal| %> <%= f.fields_for :animal_users do |ff| %> <%= ff.check_box :animal_id, {}, animal.id %> <%= animal.name %> <% end %>` – Sonalkumar sute Mar 26 '15 at 14:38
  • @Sontya what you posted makes sense. It feels like I'm getting close but am still doing something wrong. – Neil Mar 26 '15 at 14:55
  • tell me where you stuck now – Sonalkumar sute Mar 26 '15 at 14:57
  • @Sontya I updated the code above. The form currently isn't showing the check boxes or the labels for each checkbox. – Neil Mar 26 '15 at 14:59
  • @Sontya I attempted your syntax but it wasn't working so I changed it to what I have above. Neither were working for me. I do have animals in the animals table – Neil Mar 26 '15 at 15:06
  • this question is so fucking weird but has so many upvotes, not even `AnimalUser` mentioned in controller at all, sigh `magic` – buncis Jun 25 '23 at 08:16
  • @buncis `AnimalUser` is mentioned in the controller via the strong params: `animal_users_attributes`. This strategy leverages `accepts_nested_attributes_for`. – Neil Jul 10 '23 at 12:48

3 Answers3

49

collection_check_boxes is a better option:

<%= f.collection_check_boxes(:animal_ids, Animal.all, :id, :name) do |animal| %>
  <%= animal.label { animal.check_box } %>
<% end %>
webster
  • 3,902
  • 6
  • 37
  • 59
37
<% Animal.all.each do |animal| %>
   <label>
      <%= check_box_tag "user[animal_ids][]", animal.id, f.object.animals.include?(animal) %>
      <%= animal.name %>
   </label>
<% end %>

and you have to accept a hash with an array of ids on your controller params, something like

{  animal_ids: [] }

maybe reading this can help: http://millarian.com/programming/ruby-on-rails/quick-tip-has_many-through-checkboxes/

Santiago Suárez
  • 586
  • 7
  • 12
3

Another example of using collection_check_boxex that is more understandable for me:

<%= f.collection_check_boxes(:animal_ids, Animal.all, :id, :name) do |animal| %>
  <div class="form-check">
    <%= animal.check_box(class: "form-check-input") %>
    <%= animal.label(class: "form-check-label") %>
  </div>
<% end %>

It works in Rails 6

fguillen
  • 36,125
  • 23
  • 149
  • 210