Simple_Form Association with has_many :through extra field - ruby-on-rails

I have two models, Developers and Tasks,
class Developer < ActiveRecord::Base
attr_accessible :address, :comment, :email, :name, :nit, :phone, :web
has_many :assignments
has_many :tasks, :through => :assignments
end
class Task < ActiveRecord::Base
attr_accessible :description, :name, :sprint_id, :developer_ids
has_many :assignments
has_many :developers, :through => :assignments
end
class Assignment < ActiveRecord::Base
attr_accessible :accomplished_time, :developer_id, :estimated_time, :status, :task_id
belongs_to :task
belongs_to :developer
end
im taking care of the relation by adding an Assignment table, so i can add many developers to one task in particular, now i would also like to be able to manipulate the other fields i added to the joining table like the 'estimated_time', 'accomplished_time'... etc... what i got on my Simple_form is
`
<%= simple_form_for [#sprint,#task], :html => { :class => 'form-horizontal' } do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<%= f.association :developers, :as => :check_boxes %>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
project_sprint_path(#sprint.project_id,#sprint), :class => 'btn' %>
</div>
<% end %>`
This only allows me to select the developers, i want to be able to modify the estimated_time field right there.
Any Suggestions?

I love how simple-form has the association helpers, making it really easy in some cases. Unfortunately, what you want you cannot solve with just simple-form.
You will have to create assignments for this to work.
There are two possible approaches.
For both you will have to add the following to your model:
class Task
accepts_nested_attributes_for :assignments
end
Note that if you are using attr_accesible, you should also add assignments_attributes to it.
The easy approach
Suppose you know how many assignments, maximally, a task would have. Suppose 1 for simplicity.
In your controller, write
def new
#task = Task.build
#task.assignments.build
end
This will make sure there is one new assignment.
In your view write:
= simple_form_for [#sprint,#task], :html => { :class => 'form-horizontal' } do |f|
= f.input :name
= f.input :description
= f.simple_fields_for :assignments do |assignment|
= assignment.association :developer, :as => :select
= assignment.estimated_time
.form-actions
= f.button :submit, :class => 'btn-primary'
= link_to t('.cancel', :default => t("helpers.links.cancel")),
project_sprint_path(#sprint.project_id,#sprint), :class => 'btn'
The problem with this approach: what if you want more than 1, 2 or 3?
Use cocoon
Cocoon is a gem that allows you to create dynamic nested forms.
Your view would become:
= simple_form_for [#sprint,#task], :html => { :class => 'form-horizontal' } do |f|
= f.input :name
= f.input :description
= f.simple_fields_for :assignments do |assignment|
= render `assignment_fields`, :f => assignment
.links
= link_to_add_association 'add assignment', f, :assignments
.form-actions
= f.button :submit, :class => 'btn-primary'
= link_to t('.cancel', :default => t("helpers.links.cancel")),
project_sprint_path(#sprint.project_id,#sprint), :class => 'btn'
And define a partial _assignment_fields.html.haml :
.nested_fields
= f.association :developer, :as => :select
= f.estimated_time
= link_to_remove_association 'remove assignment', f
Hope this helps.

The thing is that by using this:
<%= f.association :developers, :as => :check_boxes %>
You're actually only setting the developer_ids attribute, which will automatically build the assignments for you as it's a has many :through. For this I believe you should probably be using nested attributes, for each of the assignments, and each record would have a select box or similar to choose the related developer for that particular assignment in this task. It's quite similar to what Cojones has answered, but you should not be using check boxes for this association, since you're going to be dealing with a single assignment which contains a single developer. And with nested attributes, you should be able to create as many assignments you want.
That I believe is the easiest way to start with.

I think it should look somehow like this:
= f.simple_fields_for :assignments do |fa|
= fa.association :developer, as: :check_boxes
= fa.input :estimated_time
...

Related

How to manage many to many relation for nested fields in Ruby on Rails

class Resume < ActiveRecord::Base
has_many :user_skills, :dependent => :destroy
accepts_nested_attributes_for :user_skills, :allow_destroy => true, :reject_if => :all_blank
end
class UserSkill < ActiveRecord::Base
belongs_to :resume
has_and_belongs_to_many :technologies
end
class Technology < ActiveRecord::Base
has_and_belongs_to_many :user_skills
end
<%= nested_form_for([:student, #resume], validate: true, :html => { :multipart => true, class: "full-width" }) do |f| %>
------------------------------
Resume fields
------------------------------
<h5>User Skills</h5>
<%= f.fields_for :user_skills do |us| %>
<%= us.label :academic_years, "Academic Years" %>
<%= us.text_field :academic_years %>
<%= us.label :professional_years, "Professional Years" %>
<%= us.text_field :professional_years %>
<%= us.fields_for :technologies do |tech| %>
<%= tech.collection_select :name, Technology.all, :id, :name, { prompt: "Select Technology"}, { :multiple => true, :size => 10} %>
<% end %>
<%= us.link_to_remove "Remove", class: "btn btn-small red right" %>
Now I don't know how I manage this record in controller for create and update, And also I don't know how will I show this records.... If you understand my problem then pleasr provide me controller code for update and create of resume controller and also help me to show the resume data.
I think you use old nested_form gem by Ryan Bates. You should use newest for example simple_form or others from ruby-toolbox.com

Rails - Parameters not being saved when using nested forms in Cocoon

I've followed Cocoon's instructions for creating a nested form for a has_many association but there's one thing that I can't seem to figure out. I'm able to add an existing genre to my project but when it comes to creating a completely new one the parameters for my genres aren't being permitted. When I refer to my rails console to debug the issue this is the message I receive upon update:
Unpermitted parameters: genre_attributes
What's strange is I've permitted both my join table genreships and my genre model along with having my movie model accept their nested attributes but still no luck. There has to be something I'm not doing in my movie_params method but not able to find what. I've literally tried to permit every possible attribute within this method and completely out of ideas on what could be the solution
Models
class Movie < ActiveRecord::Base
has_many :genreships
has_many :genres, through: :genreships
accepts_nested_attributes_for :genres, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :genreships, :reject_if => :all_blank, :allow_destroy => true
extend FriendlyId
friendly_id :title, use: :slugged
end
class Genreship < ActiveRecord::Base
belongs_to :movie
belongs_to :genre
accepts_nested_attributes_for :genre, :reject_if => :all_blank
end
class Genre < ActiveRecord::Base
has_many :genreships
has_many :movies, through: :genreships
extend FriendlyId
friendly_id :name, use: :slugged
end
Movies Controller
def movie_params
params.require(:movie).permit(:title, :release_date, :summary, genreships_attributes: [:id, :genre_id, :_destroy], genres_attributes: [:id, :_destroy, :name])
end
Form
= simple_form_for #movie do |f|
.field
= f.input :title
.field
= f.input :release_date, label: 'Release Date', order: [:month, :day, :year], start_year: 1901
.field
= f.input :summary, as: :text
#genres
= f.simple_fields_for :genreships do |genreship|
= render 'genreship_fields', :f => genreship
= link_to_add_association 'add a genre', f, :genreships
.actions = f.submit 'Save', class: 'btn btn-default'
Genreship Partial
.nested-fields
#genre_from_list
= f.association :genre, :collection => Genre.order(:name), :prompt => 'Choose an existing genre'
= link_to_add_association 'or create a new genre', f, :genre
= link_to_remove_association "remove genre", f
Genre Partial
.nested-fields
= f.input :name
Here's what's shown in the rails console right after posting a new movie:
Started POST "/movies" for 127.0.0.1 at 2014-11-19 08:17:42 -0500
Processing by MoviesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2/wqXhn/x73AdOOrSo49Q/OPAmrcVk0rLViJJNpIhps=", "movie"=>{"title"=>"", "release_date(2i)"=>"11", "release_date(3i)"=>"19", "release_date(1i)"=>"2014", "summary"=>"", "genreships_attributes"=>{"1416403056574"=>{"genre_attributes"=>{"name"=>"Comedy"}, "genre_id"=>"", "_destroy"=>"false"}}}, "commit"=>"Save"}
Unpermitted parameters: genre_attributes
You use genre_attributes while genres_attributes (plural form) is defined in your controller. Change it to:
def movie_params
params.require(:movie).permit(:title, :release_date, :summary, genreships_attributes: [:id, :genre_id, :_destroy], genre_attributes: [:id, :_destroy, :name])
end
I created a solution that in a sense deviates from Nathan's example code somewhat but gets the job done nonetheless. Simply moving this line from genreship_fields.html.slim to _form.html.slim did the trick:
link_to_add_association 'create a new genre', f, :genres
My partials now look like this:
Form
= simple_form_for #movie do |f|
.field
= f.input :title
.field
= f.input :release_date, label: 'Release Date', order: [:month, :day, :year], start_year: 1901
.field
= f.input :summary, as: :text
#genres
= f.simple_fields_for :genreships do |genreship|
= render 'genreship_fields', :f => genreship
| #{link_to_add_association 'add a genre', f, :genreships} | #{link_to_add_association 'create a new genre', f, :genres}
.actions = f.submit 'Save', class: 'btn btn-default'
Genreship Partial
.nested-fields
#genre_from_list
= f.association :genre, :collection => Genre.order(:name), :prompt => 'Choose an existing genre'
= link_to_remove_association "remove genre", f
Genre Partial
.nested-fields
= f.input :name
= link_to_remove_association "remove form", f

Cannot get simple_nested_form to submit data

I am trying to build a simple_nested_form in my Ruby on Rails app. When I submit my form I am getting some unknown error because it is just redirecting back to the form to input again. Here is the output in the rails server console for when I submit the form. It looks like there is some random "0" => thrown in there.
Parameters: {"machine"=>{"name"=>"2134", "ip_adress"=>"2", "machine_employees_attributes"=>{"0"=>{"machine_id"=>"1", "employee_id"=>"2"}}}, "commit"=>"Create Machine"}
I have a machine model which has_many :machine_employees
and a machineemployee model which belongs_to :machine
Do you have any idea why this 0 => could be appearing because I think it is what is giving me the issues.
Here is the code for my models.
Machine
class Machine < ActiveRecord::Base
# Relationships
has_many :machine_employees
has_many :employees, :through => :machine_employees
accepts_nested_attributes_for :machine_employees, :reject_if => lambda{ |me| me[:employee_id].blank? }
attr_accessible :ip_adress, :name, :machine_employees_attributes
# Validations
validates_presence_of :name, :ip_adress
end
MachineEmployee
class MachineEmployee < ActiveRecord::Base
before_validation :set_default
# Relationships
belongs_to :machine
belongs_to :employee
attr_accessible :employee_id, :machine_id, :end_date, :start_date
# Validations
validates_presence_of :employee_id, :machine_id, :start_date
private
# Callback Methods
def set_default
self.start_date = Date.today
self.end_date = nil
end
end
New Machine Form
<div class="row-fluid">
<div class="span3">
<h1>Add a Machine</h1>
<br />
<%= simple_nested_form_for #machine do |f| %>
<%= render "machine_fields", :f => f %>
<%= f.button :submit %>
<%= link_to 'Back', machines_path %>
</div>
<div class="span4">
<h4>Assign an Employee to This Machine</h4>
<%= f.simple_fields_for :machine_employees do |me_form| %>
<!-- render nested machine_employee fields-->
<%= render "machine_employee_fields", :f => me_form %>
<% end %>
</div>
<% end %>
</div>
Machine Employee Fields Partial
<%= f.input :machine_id, :as => :hidden, :input_html => { :value => #machine.id } %>
<%= f.input :employee_id, collection: #employees, :id => :name, :prompt => "Select ..." %>
The 0 is thrown in there because the machine model has_many machine_employees. When you use nested forms, it passes a pseudo-array for has_many relations. So, if you tried to submit 2 machine employees, your hash would look like this:
Parameters: {"machine"=>{"name"=>"2134", "ip_adress"=>"2", "machine_employees_attributes"=>{
"0"=>{"machine_id"=>"1", "employee_id"=>"2"},
"1"=>{"machine_id"=>"1", "employee_id"=>"3"}
}
}, "commit"=>"Create Machine"}
This way you can access the machine_employees passed from the form by doing params[:machine][:machine_employees_attributes][0] or params[:machine][:machine_employees_attributes][1]. Note that if this was a has_one relationship, then the machine_employees_attributes key would be changed to machine_employee_attributes and there would be no numerical index.
I suspect the problem is that your machine model must accept_nested_attributes_for :machine_employees and must also have attr_accessible :machine_employees_attributes.

How do I create a parent from child in Rails?

In my app I'm receiving this error.
Couldn't find Vendor with ID=1 for InventoryItem with ID=
InventoryItem.rb
belongs_to :vendor
accepts_nested_attributes_for :vendor
Vendor.rb
has_many :inventory_items
_form.html.erb
<%= simple_nested_form_for #inventory_item, :html => {:class => 'form-inline' } do |f| %>
<h2>Inventory Data</h2>
<%= f.input :name, :input_html => {:autocomplete => :off, :placeholder => 'Item Name' }%>
<%= f.input :id, :as => :hidden %>
<%= f.simple_fields_for :vendor do |v| %>
<%= v.input :name, :label => 'Vendor name', :input_html => {:autocomplete => :off, :placeholder => 'Vendor Name' } %>
<%= v.input :id, :as => :hidden %>
<% end %>
<% end %>
----snip----
My parameters hash comes out accordingly
{"utf8"=>"✓",
"authenticity_token"=>"ZY9fum4XGStTMNbpRQxrzmP7PT3A6BUU+wOymV0fZ/c=",
"inventory_item"=>{"name"=>"testing",
"id"=>"7678",
"vendor_attributes"=>{"name"=>"test",
"id"=>"1"},
"item_instances_attributes"=>{"0"=>{"barcode"=>"",
"funding_source"=>"",
"serial"=>"",
"version"=>"",
"website_id"=>"",
"item_type"=>"Retail",
"type_of_at"=>"Vision",
"os"=>"Mac",
"registration_key"=>"",
"dealer_price"=>"",
"retail_price"=>"",
"reuse_price"=>"",
"estimated_current_purchase_price"=>"",
"cost_to_consumer_for_loan"=>"",
"repair_status"=>"Working",
"date_reviewed"=>"10-15-2012",
"qr_url"=>"",
"location"=>"",
"restrictions"=>"",
"notes"=>""}}},
"commit"=>"Create Inventory item"}
inventory_items_controller.rb
def create
params[:inventory_item].delete(:estimated_dealer_price)
#inventory_item = InventoryItem.create(params[:inventory_item])
#inventory_item.name = inventory_item.name.downcase
if inventory_item.save
redirect_to(inventory_items_path, :notice => "Item created.")
else
render 'new'
end
end
The controller is receiving the id and attempting to find the right vendor (which exists), has issues when left to the built-in rails methods for finding the vendor and building the relationship.
The input for vendor name is an autocomplete which assigns the id to the hidden id field.
possible solutions:
Handle manually in the controller, fetching the id and building the relationship
change the form so that the inventory_item.vendor.name autocompletes inventory_item.vendor_id and strip the name if an id is provided
fix something I'm missing?
Sounds like you have it in reverse, normally child should not be creating parent records and you should check if its posible to make it in more standard approach of parent child relationship.
That being said you can do something like this
InventoryItem << ActiveRecord::Base
belongs_to :vendor
def vendor_attributes=(params)
self.vendor = Vendor.find(params[:id]) || Vendor.create_by_name!(params[:name])
end
end

Rails nested model with formtastic is skipping one field?

I have this structure models
class Tournament < ActiveRecord::Base
AGES = ["5u", "6u", "7u", "8u"]
has_many :courts, :dependent => :destroy
accepts_nested_attributes_for :courts, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
class Court < ActiveRecord::Base
belongs_to :tournament, :autosave => true
has_many :ages, :dependent => :destroy
accepts_nested_attributes_for :ages, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
class Age < ActiveRecord::Base
belongs_to :court
Now my forms look like this
_form.html.erb
<%= semantic_form_for #tournament do |f| %>
<%= f.inputs do %>
<%= f.input :name, :hint => "What is the name of the Tournament?" %>
<%= f.semantic_fields_for :courts do |builder| %>
<%= render :partial => "court_fields", :locals => { :f => builder } %>
<% end %>
_court_fields.html.erb
<div class="nested_fields">
<%= f.input :name, :input_html => {:class => "name"} %>
<%= f.semantic_fields_for :ages do |builder| %>
<%= render :partial => "age_fields", :locals => { :f => builder } %>
<% end %>
_age_fields.html.erb
Testing ...am I getting in here
<%= f.input :name, :as => :check_boxes, :collection => Tournament::AGES, :input_html => {:class => "age_limits"} %>
everything seems to work well except nothing shows up in the ages_fields partial...not the checkboxes and not even the dummy text Testing ...am I getting in here is not displaying....any ideas what could be causing the issue
The obvious reason I can think of: are you sure your Court has ages ?
[EDIT] That the Court has the relation was indeed clear to me.
But your code will only show an age for a court if it already exists.
From your output in the comments: the court has no actual ages so no ages are shown.
If you do this in your controller:
def new
#tournament = Tournament.new
#tournament.courts.build
#tournament.courts[0].ages.build
end
This will make sure that you have at least one (empty) court and one (empty) age.
Otherwise you could also consider using a gem like cocoon to dynamically add new elements if needed.
Hope this helps.

Resources