Rails - Manipulate has_many with join model through single form - ruby-on-rails

I have the following three models:
class Site < AR::Base
has_many :installed_templates, :as => :installed_templateable, :dependent => :destroy
accepts_nested_attributes_for :installed_templates, :allow_destroy => true, :update_only => true, :reject_if => lambda { |t| t[:template_id].nil? }
end
class Template < AR::Base
has_many :installed_templates, :inverse_of => :template
end
class InstalledTemplate < AR::Base
belongs_to :template, :inverse_of => :installed_template
belongs_to :installed_templateable, :polymorphic => true
end
The business logic is that several Templates exist when I create a Site and I can associate as many as I'd like by creating an InstalledTemplate for each. A Site can only have unique Templates - I can't associate the same Template twice to a single Site.
I have the following on the form for Site:
<% Template.all.each_with_index do |template| %>
<%= hidden_field_tag "site[installed_templates_attributes][][id]", Hash[#site.installed_templates.map { |i| [i.template_id, i.id] }][template.id] %>
<%= check_box_tag "site[installed_templates_attributes][]]template_id]", template.id, (#site.installed_templates.map(&:template_id).include?(template.id) %>
<%= label_tag "template_#{template.id}", template.name %>
<% end %>
The above is the only thing that seems to work after lots of experimentation. I wasn't able to achieve this at all using the form_for and fields_for helpers.
It seems like a pretty straight forward relationship and I'm afraid I'm missing something. Anyone have advice on how to accomplish the above in a cleaner fashion?
Thanks

Try the following
<% form_for #site do |f| %>
<%f.fields_for :installed_templates do |af|%>
<%= af.text_field :name %>
<%end%>
<%end%>

I think you are trying to do two different things here.
Select a list of Templates for which you want to create Installed Template joins to the Site.
From each selected Template create an instance of an Installed Template which links to the Site.
I think I would handle this through an accessor on the model.
On the Site Model (assuming standard rails naming conventions)
attr_accessor :template_ids
def template_ids
installed_templates.collect{|i_t| i_t.template.id}
end
def template_ids=(ids, *args)
ids.each{|i| i.installed_template.build(:site_id => self.id, :template_id => i) }
end
Then the form becomes pretty simple
<% form_for #site do |f| %>
<% f.collection_select :template_ids, Template.all, :id, :name, {}, :multiple => true %>
<% end %>

Is a join model necessary here?
class Site < ActiveRecord::Base
has_and_belongs_to_many :templates
end
In the form, easiest if using simple_form:
<%= form.input :template_ids, :as => :radio_buttons, :collection => Template.order(:name), :label => 'Templates installed:' %>
If the join model is necessary, then I would have a dropdown or list of templates I could add, each with a button that submits the form and adds that template. Then I'd use the update_only nested attributes form to display the currently installed templates with their settings.
class Site < ActiveRecord::Base
...
attr_accessor :install_template_id
before_validation :check_for_newly_installed_template
def check_for_newly_installed_template
if install_template_id.present?
template = Template.find install_template_id
if template.present? and not templates.include?(template)
self.templates << template
end
end
end
end
That works kind of like Drupal, where you have to enable the theme before you can edit its settings or select it as the current theme.

Related

Rails nested form with radio buttons

I have three related models
class Opportunity < ActiveRecord::Base
has_many :proposals, :dependent => :destroy
end
class Proposal < ActiveRecord::Base
belongs_to :opportunity
belongs_to :panel
end
class Panel < ActiveRecord::Base
belongs_to :opportunity
has_many :proposals
accepts_nested_attributes_for :proposals
end
I have a single opportunity and i want to list all proposals in a form, each having four radio buttons to assign each to a one of four panels.
<%= form_for :proposal, :url => update_all_path, :html => { :method => :put } do %>
<% #proposals.each do |prop| %>
<%= fields_for "proposal[]", proposal do |proposal_fields| %>
<%= proposal.name %><br>
<%= proposal_fields.number_field :panel_id %><br>
<% end %>
<% end %>
<% end %>
This form seems to work ok at the basic info. Ideally I want a series of radio buttons instead of a number_field or select.
So I would have:
Proposal A -> 4 radio buttons**
Proposal B -> 4 radio buttons**
For now I'd be happy just getting the new panel_id into the database correctly.
Here is the returned params
"proposal"=>{"244"=>{"panel_id"=>"34"}, "245"=>{"panel_id"=>"33"}},
and my update method
def update_all
params['proposal'].keys.each do |id|
#proposal = Proposal.find(id.to_i)
#proposal.panel_id = params['proposal'][:id][panel_id]
end
redirect_to...
end
and without further ado... the error
NameError at /proposals/all
undefined local variable or method `panel_id' for #
<ProposalsController:0x007ff3cf714420>
Did you mean? panel_url
How do i save the panel_id for each proposal ?
Thanks in advance for any help.

How to insert nested form values into DB

I am using Rails 4.
In my project, include nested form for has_many relationship. From UI point of view, I got it. But nested form values are not inserting into database.
class Newspaper < ActiveRecord::Base
has_to :newspaper_categories, :dependent_destroy => true
accepts_nested_attributes_for :newspaper_categories, :allow_destroy => true, :reject_if => :all_blank
end
class NewspaperCategory < ActiveRecord::Base
belongs_to :newspaper
end
Newspaper form contents like,
<%= nested_form_for(#newspaper) do |f| %>
# Newspaper form fields
# Include `Newspaper category` form from the file.
<%= f.fields_for :newspaper_categories do |nc|%>
<%= render "newspaper_category" %>
<% end %>
# For add new form using JS
<%= f.link_to_add "Add New", :newspaper_categories %>
<%= f.submit %>
<% end %>
In my Newspaper Controller,
# add build in new method,
def new
#newspaper = Newspaper.new
#newspaper.newspaper_categoried.build
end
# In params set task_attributes,
def newspaper_params
params.require(:newspaper).permit(:name, :logo, task_attributes[:cat_link, :_destroy])
end
Where I goes wrong, still i'm confusing to insert
Update this
params.require(:newspaper).permit(:name, :logo, {newspaper_categories_attributes: [ :_destroy, :category_id, :rss_link, :image_url]})

Rails - Destroy children for nested attributes

I'm banging my head trying to figure this one out. I have the following form, which works great!
<%= bootstrap_form_for #template do |f| %>
<%= f.text_field :prompt, :class => :span6, :placeholder => "Which one is running?", :autocomplete => :off %>
<%= f.select 'group_id', options_from_collection_for_select(#groups, 'id', 'name') %>
<% 4.times do |num| %>
<%= f.fields_for :template_assignments do |builder| %>
<%= builder.hidden_field :choice_id %>
<%= builder.check_box :correct %>
<% end %>
<% end %>
<% end %>
Then I have my model:
class Template < ActiveRecord::Base
belongs_to :group
has_many :template_assignments
has_many :choices, :through => :template_assignments
accepts_nested_attributes_for :template_assignments, :reject_if => lambda { |a| a[:choice_id].blank? }, allow_destroy: true
end
When the form is submitted, the nested attributes are added beautifully. However, when I want to delete a Template, I also want it to delete all the children "template_assignments" as well, which is what I assumed "allow_destroy => true"
So, I am trying this from the Rails Console (which could be my issue??) I'll do something like:
>> t = Template.find(1)
#Which then finds the correct template, and I can even type:
>> t.template_assignments
#Which then displays the nested attributes no problem, but then I try:
>> t.destroy
#And it only destroys the main parent, and none of those nested columns in the TemplateAssignment Model.
Any ideas what I am doing wrong here? Is it because you can't do it in the Rails Console? Do I need to do it in a form instead? If so, how do I achieve this in a form?
Any help would be great!
Specify that child template_assignments should be destroyed upon the destruction of the parent Template:
# app/models/template.rb
has_many :template_assignments, dependent: :destroy
The following depicts the steps that Rails takes to destroy an object's dependent associations:
# from the Rails console
>> t = Template.find(1)
#=> #<Template id:1>
>> t.template_assignments.first
#=> #<TemplateAssignment id:1>
>> t.destroy
#=> SELECT "template_assignments".* FROM "template_assignments" WHERE "template_assignments"."template_id" = 1
#=> DELETE FROM "template_assignments" WHERE "template_assignments"."id" = ? [["id", 1]]
>> TemplateAssignment.find(1)
#=> ActiveRecord::RecordNotFound: Couldn't find TemplateAssignment with id=1
The :dependent option can be specified on either side of the association, so it can alternatively be declared on the child association, rather than the parent:
# app/models/template_assignment.rb
belongs_to :template, dependent: :destroy
From the Rails docs:
has_many, has_one and belongs_to associations support the :dependent option. This allows you to specify that associated records should be deleted when the owner is deleted

Rails 4 nested form issue

Hi I'm using the nested form plugin and trying to make it work for rails 4 instead of rails 3. Basically my model looks like this:
has_many :item, :dependent => :destroy
accepts_nested_attributes_for :item, :reject_if => lambda { |a| a[:item].blank? }, :allow_destroy => true
and my view looks like this:
<%= nested_form_for(#store) do |f| %>
<%= f.fields_for :item do |item_form| %>
<%= item_form.text_field :name %>
<%= item_form.link_to_remove "Remove this item" %>
<% end %>
<% end %>
this works (in terms of presentation - you can add and delete fields like you should be able to) but doesn't save the item names.
I tried this in my controller (these are the protected attributes/params):
def store_params
params.require(:store).permit(:name, :owner, :description,:url, :user, item_attributes)
end
but it still comes up with:
Unpermitted parameters: item_attributes
Thanks for all help!
You're going to have to permit the fields of item (like name) to be allowed as well.
So try this:
def store_params
params.require(:store).permit(:name, :owner, :description,:url, :user, item_attributes: [:name])
end
sometimes you have to specify the :id like this:
def store_params
params.require(:store).permit(:name, :owner, :description,:url, :user, item_attributes: [:id, :name])
end
in a similar case I had last week, not specifying the :id made Rails 4 create an new entity instead of updating the existing one.

Assigning a nested attribute with Formtastic

I've been trying to figure this one out for a while but still no luck. I have a company_relationships table that joins Companies and People, storing an extra field to describe the nature of the relationship called 'corp_credit_id'. I can get the forms working fine to add company_relationships for a Person, but I can't seem to figure out how to set that modifier field when doing so. Any ideas?
More about my project: People have many companies through company_relationships. With that extra field in there I am using it to group all of the specific relationships together. So I can group a person's Doctors, Contractors, etc.
My models:
Company.rb (abridged)
class Company < ActiveRecord::Base
include ApplicationHelper
has_many :company_relationships
has_many :people, :through => :company_relationships
Person.rb (abridged)
class Person < ActiveRecord::Base
include ApplicationHelper
has_many :company_relationships
has_many :companies, :through => :company_relationships
accepts_nested_attributes_for :company_relationships
company_relationship.rb
class CompanyRelationship < ActiveRecord::Base
attr_accessible :company_id, :person_id, :corp_credits_id
belongs_to :company
belongs_to :person
belongs_to :corp_credits
end
My form partial, using formtastic.
<% semantic_form_for #person do |f| %>
<%= f.error_messages %>
<% f.inputs do %>
...
<%= f.input :companies, :as => :check_boxes, :label => "Favorite Coffee Shops", :label_method => :name, :collection => Company.find(:all, :conditions => {:coffee_shop => 't'}, :order => "name ASC"), :required => false %>
So what I would like to do is something like :corp_credit_id => '1' in that input to assign that attribute for Coffee Shop. But formtastic doesn't appear to allow this assignment to happen.
Any ideas on how to do this?
Are you looking for something like
<% semantic_form_for #person do |form| %>
<% form.semantic_fields_for :company_relationships do |cr_f| %>
<%= cr_f.input :corp_credit_id %>
<% end %>
It is in the documentation

Resources