I am trying to sort comments into events using a has_many :through association using checkboxes however the selected events are not saved. Here are my models:
class Comment < ActiveRecord::Base
has_many :categorizations
has_many :events, :through => :categorizations
end
class Event < ActiveRecord::Base
has_many :categorizations
has_many :comments, :through => :categorizations
end
class Categorization < ActiveRecord::Base
belongs_to :comment
belongs_to :event
end
My comments form looks like this:
<%= simple_form_for [#post, #comment] do |f| %>
<%= f.input :title %>
<%= f.association :events, :as => :check_boxes %>
<%= f.submit "Save" %>
After reading this answer, I added this to my event and comment controllers with no luck:
def comment_params
params.require(:comment).permit(:post_id, :title, :categorization_ids => [])
end
Try:
def comment_params
params.require(:comment).permit(:post_id, :title, :event_ids => [])
end
It's hard to know what's going on though without recreating it or seeing server logs, Hopefully this will solve it.
Related
I have three objects: Contact, Sector, and Contact_sector.
Contact contains an id and some other irrelevant (non-reference) columns
Sector contains an id and sector column with ~10 editable entries
Contact_sector has a contact_id reference and a sector_id reference. In my mind I imagine that every sector that applies to some contact can be found here, and if a sector is un-applied it is removed from here.
I want to have a collection of checkboxes in the contact _form formed from list of entries in :sectors, but updating the form with certain boxes ticked adds/removes entries from :contact_sectors.
Where am I going wrong?
UPDATED: Fixed strong_params to permit sectors, now I am unable to find the sectors by id ActiveRecord::RecordNotFound (Couldn't find Sector with ID=["1", ""] for Contact with ID=1)
Models:
class Contact < ActiveRecord::Base
has_many :contact_sectors
has_many :sectors, through: :contact_sectors
accepts_nested_attributes_for :contact_sectors, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :sectors, :reject_if => :all_blank, :allow_destroy => true
end
class Sector < ActiveRecord::Base
has_many :contact_sectors
has_many :contacts, through: :contact_sectors
def name_with_initial
"#{sector}"
end
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
View:
<%= f.fields_for(:sectors) do |s| %>
<%= s.collection_check_boxes :id, Sector.all,
:id, :name_with_initial,
{ prompt: true }, { class: 'form-control' } %>
<% end %>
Controller
def edit
#contact.sectors.build
end
def contact_params
#Not sure if I need something like this or not
params['contact']['sectors'] = params['contact']['sectors']['id'].split(',')
params.require(:contact).permit(:firstname, :lastname,
contact_sectors_attributes: [:id],
sectors_attributes: [:_destroy, {:id => []}])
end
Instead of creating the join model explicitly you can just declare the relationship as has_many through: and let ActiveRecord handle the join model:
class Contact < ActiveRecord::Base
has_many :contact_sectors
has_many :sectors, through: :contact_sectors
accepts_nested_attributes_for :sector,
reject_if: :all_blank, allow_destroy: true
end
class Sector < ActiveRecord::Base
has_many :contact_sectors
has_many :contacts, through: :contact_sectors
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
<%= form_for(#contact) do |f| %>
<%= f.fields_for(:sectors) do |s| %>
<%= s.collection_check_boxes :id, Sector.all,
:id, :name_with_initial,
{ prompt: true }, { class: 'form-control' } %>
<% end %>
<% end %>
models
class Contact < ActiveRecord::Base
has_many :sectors, through: :contact_sectors
has_many :contact_sectors
accepts_nested_attributes_for :sectors
end
class Sector < ActiveRecord::Base
has_many :contacts, :through => :contact_sectors
has_many :contact_sectors
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
view
<%= form_for(#contact) do |f| %>
<% Sector.all.each do |sector| %>
<%= check_box_tag "contact[sector_ids][]", sector.id, f.object.sectors.include?(sector) %>
<%= sector.sector %>
<% end %>
<% end %>
controller
def update
#To make sure it updates when no boxes are ticked
#contact.attributes = {'sector_ids' => []}.merge(params[:contact] || {})
end
def contact_params
params.require(:contact).permit(:firstname, :lastname, sector_ids: [])
end
Recommended reading:
http://millarian.com/rails/quick-tip-has_many-through-checkboxes/
Rails 4 Form: has_many through: checkboxes
I'm quite new to rails, so my question may seem noobish.
I have HABTM self association. A product can become a "COMBO" of products, having N products.
class Product < ActiveRecord::Base
has_many :combo_elements, class_name: "ComboElement", foreign_key: :combo_id
has_many :element_combos, class_name: "ComboElement", foreign_key: :element_id
has_many :combos, :through => :element_combos
has_many :elements, :through => :combo_elements
accepts_nested_attributes_for :elements
end
class ComboElement < ActiveRecord::Base
belongs_to :combo, :class_name => 'Product'
belongs_to :element, :class_name => 'Product'
end
With the above code, I can list all "elements" of a combo. Also I can list all "combos" a product is part of (the ComboElement table has a combo_id and an element_id)
Here is my form
<%= f.simple_fields_for :elements, #element do |b| %>
<%= b.input :element_id, :collection => Product.all.collect{ |t| [t.name, t.id ]}, selected: b.object.id %>
<%= b.link_to_remove "Remove this element" %>
<% end %>
I can successfully list all Products a combo has, but whenever I try to update, it simply doesn't work.
My Product controller has the following code
def product_params
params[:product].permit(:name, :price, elements_attributes: [:id, :element_id, :_destroy])
end
I appreciate any help.
Thanks in advance!
My Category model :
class Category < ActiveRecord::Base
has_many :item_categoryships
has_many :items, :through => :item_categoryships
end
My Item model :
class Item < ActiveRecord::Base
has_many :item_categoryships
has_many :categories, class_name: 'ItemCategoryship', foreign_key: 'category_id', :through => :item_categoryships
end
My ItemCategoryship model:
class ItemCategoryship < ActiveRecord::Base
belongs_to :item
belongs_to :category
end
And in views/items/edit.html.erb, I wrote simple form code like this:
<%= simple_form_for(#item) do |f| %>
<%= f.association :categories, collection: #categories, as: :check_boxes %>
<%= f.submit "Submit", class: "btn btn-large btn-primary" %>
<% end %>
The #categories above, I wrote this in controller:
#categories = current_user.categories
But I hit a problem, they can't save to database!!
I couldn't find out what the problem was. Please help me....
Thanks you all.
For the view, this SO answer should help:
https://stackoverflow.com/a/9917006/2463468
You may first need to make sure your associations make sense. Perhaps you only need a has_many on Items and belongs_to on Category.
You might be able to do without ItemCategoryship.
I'm building a recurring billing system and am having trouble with a nested form. The following code works, but makes a POST with:
"customer"=>{"service"=>{"service_id"=>"1"}}
It should be Customer.services instead of Customer.service. However, if I change the form to reference fields_for :services, it doesn't render a dropdown at all.
_form.html.haml
= form_for #customer do |f|
= f.fields_for :service do |service_fields|
= service_fields.collection_select(:service_id, Service.all, :id, :name, { :prompt => 'Select Package' })
= f.submit "Add Service", class: "btn"
models/customer.rb
class Customer < ActiveRecord::Base
has_many :subscriptions, :dependent => :destroy
has_many :services, :through => :subscriptions
accepts_nested_attributes_for :services
end
models/service.rb
class Service < ActiveRecord::Base
has_many :customers, :through => :subscriptions
has_many :subscriptions
end
models/subscription.rb
class Subscription < ActiveRecord::Base
belongs_to :customer
belongs_to :service
end
So, in this situation you want to build a subscription on the customer. Since you're pretty much on the right track, it should be as simple as changing this
_form.html.haml
= form_for #customer do |f|
= f.fields_for :subscriptions do |subscription_fields|
= subscription_fields.collection_select(:service_id, Service.all, :id, :name, { :prompt => 'Select Package' })
= f.submit "Add Service", class: "btn"
models/customer.rb
class Customer < ActiveRecord::Base
has_many :subscriptions, :dependent => :destroy
has_many :services, :through => :subscriptions
accepts_nested_attributes_for :subscriptions
end
Right now the reason why you're getting anything rendered is because you're causing the form to submit with a new "attribute" called service with then returns the data from the fields_for. With accepts_nested_attributes_for {model} you should be looking for a params with something like {model}_attributes
Also the reason why you're not getting anything rendered when you use :services in the fields_for is because Service doesn't respond to service_id.
I was able to figure it out finally (with the help of a colleague.) I made all of the above changes thanks to #Azolo's reply above with also changing the form view to:
_form.html.haml
= f.fields_for :subscriptions, #customer.subscriptions.build do |builder|
I am working on a project involving three models (recipient, award, announcer) and need to have a nested attributes when issuing an award by an announcer to multiple recipients. For an example, award form need to have the ability to do 3 things:
Can add multiple-recipients (i.e. "add recipient", "remove recipient") - nested attributes
After creating a new award, the award will be posted into recipient's profile.
Enables future polling of #recipient.awards and #announcer.awards
Really struggle in terms of how to smartly solve this problem. The following data structure kind of made sense, however can not do "accepts_nested_attributes_for :recipients" in the award form. Can you help? Many thanks in advance.
class Recipient < ActiveRecord::Base
has_many :awards
has_many :announcers, :through => :awards
end
class Announcer < ActiveRecord::Base
has_many :awards
has_many :recipients, :through => :awards
end
class Award < ActiveRecord::Base
belongs_to :announcer
belongs_to :recipient
end
You're just about there. The main issue is that you're trying to create recipient objects in the form rather than just creating a relationship between the award and another object (user). You could do something like this:
class User < ActiveRecord::Base
has_many :recipients
has_many :awards, :through => :recipients
end
# this is your relationship between an award and a user
class Recipient < ActiveRecord::Base
belongs_to :user
belongs_to :award
end
class Award < ActiveRecord::Base
has_many :recipients
has_many :users, :through => :recipients
belongs_to :announcer
accepts_nested_attributes_for :recipients, :allow_destroy => true
end
class Announcer < ActiveRecord::Base
has_many :awards
has_many :recipients, :through => :awards
end
Then you would just do a nested form that would build the recipients_attributes array:
<%= form_for #award do |f| %>
<%= f.text_field :name %>
<div id="recipients">
<% #award.recipients.each do |recipient| %>
<%= render :partial => '/recipients/new', :locals => {:recipient => recipient, :f => f} %>
<% end %>
</div>
<%= link_to_function 'add recipient', "jQuery('#recipients').append(#{render(:partial => '/recipients/new').to_json})" %>
<% end %>
And, to keep it DRY just push the nested part into a partial:
# app/views/recipients/_new.html.erb
<% recipient ||= Recipient.new %>
<%= f.fields_for 'recipients_attributes[]', recipient do |rf| %>
<%= rf.select :user_id, User.all %>
<%= fr.check_box '_delete' %>
<%= fr.label '_delete', 'remove' %>
<% end %>
Obviously the User.all call isn't ideal so maybe make that an autocomplete.