I have an association of Item & Category through Categorization:
class Item < ActiveRecord::Base
has_many :categorizations
has_many :categories, :through => :categorizations, :source => :category
end
class Category < ActiveRecord::Base
has_many :categorizations
has_many :items, :through => :categorizations, :source => :item
attr_accessible :name
end
class Categorization < ActiveRecord::Base
belongs_to :item
belongs_to :category
end
Items/new:
<div class="container">
<%= render 'shared/error_create_item_messages'%>
<br/>
<%= form_for(@item, :html => {:multipart => true} ) do |f| %>
<div class="clearfix">
<label>
<%= f.label :name %>
</label>
<div class="input">
<%= f.text_field :name %>
</div>
</div>
<div class="clearfix">
<label>
<%= f.label :category %>
</label>
<%= hidden_field_tag "product[category_ids][ ]", nil %>
<% Category.all.each do |category| %>
<div class="input">
<%= check_box_tag "item[category_ids][ ]", category.id,
@item.category_ids.include?(category.id) %>
<%= category.name %>
</div>
<% end %>
</div>
<div class="action">
<div class="btn_create_item_align">
<%= f.submit "Create item", :class=>"btn primary" %>
</div>
</div>
<% end %>
</div>
Categorizations_controller
class CategorizationsController < ApplicationController
def create
@categories = Category.all
Categorization.create(:item_id => item.id, :category_id => category.id)
Categorization.save
end
def edit
end
end
Items_controller
def create
@item = @current_user.items.build(params[:item])
@categories = Category.all
if @item.save
redirect_to @item
else
render 'new'
end
end
The problem is When i hit save (create Item), and i check the Categorization table and check the on console, The items saved still dont have category_id. So the new item and its attributes (name, description, price) is saved to DB properly, but NOT the category. It wont save to db.
Any ideas? (Newbie in Rails) Thanks
The form POSTs to ItemsController#create, and CategorizationsController#create isn't being called (you can verify this with some puts
debugging).
You can use accepts_nested_attributes_for
to have the Item's create action do all the work. The trick is to only create Category associations whose boxes are checked, and you can do that with the :reject_if
option (see Rails API doc for more info):
app/models/item.rb:
class Item < ActiveRecord::Base
has_many :categorizations
has_many :categories, :through => :categorizations, :source => :category
accepts_nested_attributes_for :categories,
:reject_if => proc{|c| c[:persist].blank?}
end
Then you can create form fields for the nested objects, one checkbox per category.
app/views/items/new.html.erb:
<%= form_for @item do |f| %>
<%# stuff to generate item fields... %>
<%= f.fields_for :categories do |cat| %>
<%= cat.check_box :persist %>
<%= cat.label :name, cat.name %>
<%- end %>
<%# submit button, etc. %>
<%- end %>
Populate the set of Categories to choose from when creating a new Item by building (but not saving) Categories associated with the Item. This is effectively moving code from your view to the controller:
app/controllers/items_controller.rb:
def new
@item = Item.new
Category.all.each {|cat| @item.categories.build(cat.attributes) }
end
It'll be educational to puts
the params
in that controller action, so you can see what the hashes being sent from the form look like.