Reputation: 25
I have been learning RoR for 2 months now so pardon me if you think I am stuck in something too basic. I am sure the rails expert will solve this in a couple of minutes, but I am unable to do it for 2 days now. I have provided a detailed explanation of the situation, although I feel the problem is not half as big as my question. Please bear with me and help me out.
Problem: I have two models/tables, listings and tags, that have a many-to-many relationship, joined in the middle by a join table called taggings that contains as FKs the PK's of the two tables that it belongs to. Tag model has one attribute called tag_name, in addition to rails provided id
class Tag < ActiveRecord::Base
has_many :taggings, dependent: :destroy
has_many :listings, :through => :taggings
end
Listing model has one attribute, description (in addition to the default id field)
class Listing < ActiveRecord::Base
has_many :taggings, dependent: :destroy
has_many :tags, :through => :taggings
end
The Above two have a m-n relationship, joined by the following join table, which has two attributes, listing_id, and tag_id as foreign keys, in addition to rails provided id attribute.
class Tagging < ActiveRecord::Base
belongs_to :listing
belongs_to :tag
end
In my Listings new/create page, I have a form, where I enter a description into text field and then choose tags (that I get from Tags table) using check boxes. The following is the form.
<%= form_for @listing, :html => { :class => 'form-horizontal' } do |f| %>
<div class="control-group">
<%= f.label :description, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :description, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<% Tag.all.each do |tag| %>
<%= check_box_tag "listing[tag_ids][]", tag.id%>
<%= tag.tag_name %> <br>
<%end%>
</div>
</div>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
listings_path, :class => 'btn' %>
</div>
<% end %>
When I submit this form after selecting three tags from check boxes, I expect the following things to happen:
I think this is because the check box in form sends an array of values, and I don't know how to put the values in arrays in multiple rows in taggings table.
Here is my listings controller's create and new actions.
def new
@listing = Listing.new
end
def create
@listing = Listing.new(listing_params)
respond_to do |format|
if @listing.save
format.html { redirect_to @listing, notice: 'Listing was successfully created.' }
format.json { render action: 'show', status: :created, location: @listing }
else
format.html { render action: 'new' }
format.json { render json: @listing.errors, status: :unprocessable_entity }
end
end
end
def listing_params
params.require(:listing).permit(:description, :id)
end
I have been stuck at this for last two days, spent a lot of time before seeking help here. I would greatly appreciate if someone helps me solve this. Thanks a lot.
Upvotes: 2
Views: 2804
Reputation: 76784
Your code should work with @Wali Ali
's fix
You'll gain a better perspective by posting your request log which will probably show unpermitted params
for tag_ids
. As you're passing an array, you'll have to define it as such, hence setting it in your strong params hash:
params.require(:listing).permit(tag_ids: [])
Upvotes: 0
Reputation: 2508
permit the tag ids as an array in your controller as in this:
def listing_params
params.require(:listing).permit(:description, :id, :tag_ids => [])
end
Upvotes: 2
Reputation: 5839
I prefer doing it through rails nested forms, something like this:
class Listing < ActiveRecord::Base
has_many :taggings, dependent: :destroy
has_many :tags, :through => :taggings
accepts_nested_attributes_for :taggings, allow_destroy: true, reject_if: proc { |attributes| attributes['tag_id'].blank?}
# the reject option is necessary because we put a hidden field for list.id and it will submit a tagging object (tag_id = nil) for every tag if we don't reject ones without tag_id
end
<%= form_for @listing, :html => { :class => 'form-horizontal' } do |f| %>
<div class="control-group">
<%= f.label :description, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :description, :class => 'text_field' %>
</div>
</div>
<%= f.fields_for :taggings, Tag.all do |tags_builder| %>
<%= tags_builder.hidden_field :list_id, @list.id %>
<div class="controls">
<%= tags_builder.check_box :tag_id %>
<%= tags_builder.object.tag_name %> <br>
</div>
<%end%>
</div>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
listings_path, :class => 'btn' %>
</div>
<% end %>
You should tell your controller to allow params of the nested form
def list_params
params.require(:list).permit(:list_attr_1, :list_attr_2 :taggings_attributes =>[:tag_id, :list_id, :_destroy])
end
I haven't tested this code so it will propaply require some fixing but i wanted to give you an idea on the steps (Here are some useful links: fields_for api docs , RailsCasts on nested forms )
Upvotes: 1