Mike Campbell
Mike Campbell

Reputation: 7978

Nested models/forms with associations

I have the following three models with relationships:

# category.rb
class Category < ActiveRecord::Base
  has_many :category_marks
  accepts_nested_attributes_for :category_marks
end

# category_mark.rb
class CategoryMark < ActiveRecord::Base
  attr_accessible: :add, :stu, :inc

  validates_presence_of :user_group
  validates_presence_of :category_id
  belongs_to :category
  belongs_to :user_group
end

# user_group.rb
class UserGroup < ActiveRecord::Base
  has_many :category_marks
end

category_marks describe the access permissions of different user groups to different categories. I'm trying to create the category marks when I create a new category, hence the accepts_nested_attributes_for. I'm able to edit categories and update category_marks at the same time, but not when I'm creating new category. This is my categories_controller.rb (irrelevant bits removed):

class Admin::CategoriesController < ApplicationController

    def new
      @category = Category.new
      @category.category_marks.build(UserGroup.all.map{|ug| { user_group: ug }})

      respond_to do |format|
        format.html # new.html.erb
        format.json { render json: @category }
      end
    end

    def edit
      @category = Category.find(params[:id])
    end

    def create
      @category = Category.new
      @category.category_marks.build(UserGroup.all.map{|ug| { user_group: ug }})
      @category.attributes = params[:category]

      respond_to do |format|
        if @category.save
          format.html { redirect_to [:admin, :categories], notice: 'Category was successfully created.' }
          format.json { render json: @category, status: :created, location: @category }
        else
          format.html { render action: "new" }
          format.json { render json: @category.errors, status: :unprocessable_entity }
        end
      end
    end

    def update
      @category = Category.find(params[:id])

      respond_to do |format|
        if @category.update_attributes(params[:category])
          format.html { redirect_to [:admin, :categories], notice: 'Category was successfully updated.' }
          format.json { head :no_content }
        else
          format.html { render action: "edit" }
          format.json { render json: @category.errors, status: :unprocessable_entity }
        end
      end
    end
  end

Every category needs a category mark for every user group, so in the new action I'm building a category marks object and association for every user group onto my new @category object. This works fine and enables me to display the form fields for each category mark using a nested form. The difficulty I'm now having is getting those category marks to save in the create action.

params comes into create like:

{"utf8"=>"✓",
"authenticity_token"=>"0rNpYy7OB+DPuzmu8HoxX2MvTnkjUyU+Vej+a4RMh7s=",
"category"=>{
  "name"=>"test category",
  "color"=>"#00f",
  "parent_id"=>"3",
  "category_marks_attributes"=>{
    "0"=>{
      "stu"=>"1",
      "inc"=>"0",
      "add"=>"1"},
    "1"=>{
      "stu"=>"0",
      "inc"=>"1",
      "add"=>"1"}}},
"button"=>"",
"action"=>"create",
"controller"=>"admin/categories"}

View code for the form:

<%= form_for([:admin, @category]) do |category_form| %>
  <ul>
    <li><%= category_form.label :name %> <%= category_form.text_field :name %></li>
    <li><%= category_form.label :color %> <%= category_form.text_field :color, class: :add_colour_picker %></li>
    <li><%= category_form.label :parent_id, "Parent category" %> <%= category_form.select :parent_id, Category.roots.delete_if{|c| c == @category}.map{|c| [c.name, c.id]}, include_blank: true %></li>
    <br />
    <h2>User Group Permissions</h2>
    <table>
      <tr>
        <th>User Group</th>
        <th>View Students</th>
        <th>View Incidents</th>
        <th>Add Incidents</th>
      </tr>
      <%= category_form.fields_for :category_marks do |category_marks_form| %>
      <%= category_marks_form.hidden_field :user_group_id, value: category_marks_form.object.user_group_id %>
        <tr>
          <td><%= category_marks_form.object.user_group.name %></td>
          <td><%= category_marks_form.check_box :stu %></td>
          <td><%= category_marks_form.check_box :inc %></td>
          <td><%= category_marks_form.check_box :add %></td>
        </tr>
      <% end %>
    </table>

    <li><%= button_tag "Save Category" %></li>
  </ul>
<% end %>

Upvotes: 1

Views: 169

Answers (1)

Geoff
Geoff

Reputation: 2228

Your category_marks_attributes in params are missing the user_group_id and therefore would not validate or save based on the user input.

You probably need to add something like the following to your view:

= category_marks_builder.hidden_field :group_id, value: category_marks_builder.object.group_id

Also, I'm pretty sure it works as you have it, but I would get rid of the @category.attributes= line and call @category.update_attributes params[:category] instead of @category.save.

Lastly, I'm not sure why you're doing the @category.category_marks.build call again in the create controller. I think this is going to give you two sets of category_marks. One with the user inputs and one with default values.

I hope it helps.

Upvotes: 2

Related Questions