Rails 3 has_many :through form with dropdown menus? - ruby-on-rails

I'm trying to implement a has_many :through join in Rails 3 (with Formtastic) and I'm a little stuck. I have the models set up as follows:
Models:
class Project < ActiveRecord::Base
has_many :employees, :through => :teams
has_many :teams
class Employee < ActiveRecord::Base
has_many :projects, :through => :teams
has_many :teams
class Team < ActiveRecord::Base
belongs_to :project
belongs_to :employee
And this line gives me a multi-select box in the projects view that allows employees to be selected:
View:
<%= f.input :employees, :as => :select %>
So far this gets the job done, but what I'd LIKE to have is a separate dropdown box to select each employee's name, then their role in the project. I can't figure out the form code to get me there...
EDIT:
As suggested, I've gotten the code from Railscast 197: Nested Model Forms working and it's part-way there. This is what I have in the view:
<%= f.semantic_fields_for :employees do |builder| %>
<%= render 'employee_fields', :f => builder %>
<% end %>
<%= link_to_add_fields "add employee", f, :employees %>
and the 'employee_fields' partial:
<p class="fields">
<%= f.input :name, :as => :select, :collection => Employee.find(:all) %>
<%= f.hidden_field :_destroy %>
<%= link_to_remove_fields "remove", f %>
</p>
But right now this creates a new employee rather than a new team (project-employee join record), so I think it's acting as a has_many rather than a has_many :through. How can I edit this so that the :name input above adds a record to project[employee_ids][]?

Oh my god, I finally got this thing to work. Here's the relevant code, minus the bits added to make the form add and remove fields dynamically:
_form.html.erb
<%= semantic_form_for #project do |f| %>
.
.
<%= f.semantic_fields_for :teams do |builder| %>
<input id="project_teams_none" name="team[employee_ids][]" type="hidden" value="" />
<%= render 'team_fields', :f => builder %>
<% end %>
_team_fields.html.erb
<div class="input">
<%= f.collection_select(:employee_id, Employee.all, :id, :name, :include_blank => true ) %>
</div>
The key was adding the <input id="project_teams_none" name="team[employee_ids][]" type="hidden" value="" /> line in manually, because for whatever reason this wasn't generated as part of the form. This got the form to actually start updating things, and then I just had to make the nested form refer to the join model (team) rather than to employees so that the updates were going to the right place.
Looks so simple now!

Related

Unable to add rows to join table in rails 5 app

I have three tables - result, feedback_qs, and feedback_qs_results (join table). Given an existing result and feedback_qs, I want to populate the join table with the result_id, feedback_q_id and the subsequent answer (text_area).
I believe the relationships are correct....
# result.rb
has_many :feedback_q_results
has_many :feedback_qs, :through => :feedback_q_results
# feedback_q.rb
has_many :feedback_q_results
has_many :results, :through => :feedback_q_results
# feedback_q_result.rb
belongs_to :result
belongs_to :feedback_q
I have access to the result_id (result/result.id/feedback) via config/routes.rb below...
resources :results do
member do
get 'feedback'
post 'feedback_create'
end
end
What would this form look like to populate the join table? Something similar to this (I realize this isn't correct)...? How do I take in the data through the post_controller to populate the join table? Or should it go through another controller?
<%= form_for :result, url: feedback_create_result_path(result), method: :post do |form| %>
<% for q in FeedbackQ.all %>
<div class="col field">
<p>
<%= form.label q.question_text %>
</p>
<%= text_area_tag id: q.id %>
</div>
<% end %>
<div class="actions">
<%= form.submit "Submit", class: "btn btn-primary" %>
</div>
<% end %>
What would my feedback_create method look like? While I've seen several questions regarding rails' many to many relationships and populating join tables, none of those use cases seems to suit my need. Thanks in advance.
Update - My use case is this: user takes an exam (called a result), and they then review the proctor by answering feedback questions. I want to record the feedback in the join table
If your has_many associations are working correctly, then in the rails console you should be able to do:
> result.feedback_qs
=> []
and
> feedback_q.results
=> []
Where result and feedback_q are are Result and FeedbackQ objects, respectively.
To associate a feedback_q to a result, simply:
> result.feedback_qs << feedback_q
This should create a record in the feedback_q_result table.
Now, when you do
> result.feedback_qs
It should return the feedback_q record that you just added.
I altered my original models. One issue I ran into was simple naming convention in rails, so I simplified the table names to eliminate that from the equation. New models:
# result.rb
has_many :feedbacks
has_many :questions, :through => :feedbacks
# question.rb
has_many :feedbacks
has_many :results, :through => :feedbacks
# feedback.rb
belongs_to :question
belongs_to :result
validates_uniqueness_of :question_id, scope: :result_id
form in my view:
<%= form_for :result, url: feedback_create_result_path(result), method: :post do |form| %>
<% Question.all.each do |question| %>
<div class="col field">
<p>
<%= form.label :question, question.question_text %>
</p>
<%= hidden_field_tag 'question[][id]', question.id %>
<%= text_area_tag 'question[][answer]', "", id: "question_" + question.id.to_s, class: "stretch_textarea" %>
</div>
<% end %>
<div class="actions">
<%= form.submit "Submit", class: "btn btn-primary" %>
</div>
This SO page helped quite a bit as well.

Rails - Display Nested Attributes in an "Update" Form

I am having a bit of difficulty getting my "update form" to display the nested attributes. Specifically, images (e.g., "choices") to display. All other data fields are showing. Just not this is my form:
<%= bootstrap_form_for #template, :url => {:action => 'update', :id => #template.id } do |f| %>
<fieldset>
<legend>Update Your Template</legend>
<%= 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', selected: #template.group.id) %>
<div class="row-fluid">
<ul class="thumbnails">
<%= f.fields_for :template_assignments do |builder| %>
<li class="span3" id="">
<div class="thumbnail">
<%= builder.text_field :choice_id %>
<%= image_tag #template.template_assignments.builder.choice.image %>
</div>
</li>
<% end %>
</ul>
</div>
<% end %>
The main line I am having trouble with is:
<%= image_tag #template.template_assignments.builder.choice.image %>
I cannot get it to iterate through each of the 4 nested attributes for the image. It iterates through the 4 nested attributes pertaining to :choice_id, which displays correctly in the text_field.
If i change it to:
<%= image_tag #template.template_assignments.first.choice.image %>, it displays the first image no problem.
However, I need it to iterate and display the "first", "second", "third", and "fourth" images.
Any help on how to display these images, just as the image_id's are being displayed?
EDIT:
Here are my models
# app/models/template.rb
class Template < ActiveRecord::Base
belongs_to :group
has_many :template_assignments, dependent: :destroy
has_many :choices, :through => :template_assignments
accepts_nested_attributes_for :template_assignments, allow_destroy: true
end
# app/models/template_assignment.rb
class TemplateAssignment < ActiveRecord::Base
belongs_to :template
belongs_to :choice
end
# app/models/choice.rb
class Choice < ActiveRecord::Base
has_many :template_assignments
has_many :templates, :through => :template_assignments
end
You'll probably want to just use builder directly, just like you're doing in the text_field.
<%= image_tag builder.choice.image %>
[UPDATE] after some trial and error the correct form would be :
<%= image_tag builder.object.choice.image %>
What's happening is that when f.fields_for :template_assignments do |builder| is used to render the nested items, the builder object that is yielded to the block is not the object itself (in this case a TemplateAssignment), but is a FormBuilder object, which is what supplies the convenience methods like builder.text_field. (If you tried to do template_assignment.text_field you'd get an error.) The builder stores the object that it is representing in the form as object, so you can get a hold of your template_assignment object by using builder.object. From there you can deal the the template_assignment like normal. I hope that helps.

collection_select not filtering (in nested_form w/nested resources)

I've got an application that uses nested resources (see routes.rb below) to completely segregate users. It works great until I use collection_select to allow users to select objects from other models. For example, if I visit the store index view as user A, I only see stores created by user A. However, if I visit the store_group view and try to select a store to add to the group from the collection_select menu under the fields_for :store_group_details, I see all stores created by all users.
As far as I can tell, the problem might happen because there is no filter for stores in the store_group controller. store_group_details doesn't have a controller, but from what I've read, that seems to be correct since the model can only be accessed through a nested form in the store_group view. I have another situation where another view for another resource has several collection_select menus for selecting objects from other models, and all of those have the same problem (they display all objects in that model, regardless of which user created them).
How can I filter the objects shown in the collection_select menus? Is it a problem with what I'm passing into collection_select, or is it because the controllers don't do anything to filter the other models before those models' objects are displayed? I've looked at the docs for collection_select, and couldn't make it work based on that.
Thanks for your help, I've spent quite a bit of time trying to get this to work.
user.rb
class User < ActiveRecord::Base
has_many :store_groups
has_many :stores
has_many :store_group_details
end
store.rb
class Store < ActiveRecord::Base
belongs_to :user
has_many :store_group_details
has_many :store_groups, :through => :store_group_details
end
store_group.rb
class StoreGroup < ActiveRecord::Base
belongs_to :user
has_many :store_group_details, :inverse_of => :store_group
has_many :stores, :through => :store_group_details
accepts_nested_attributes_for :store_group_details
attr_accessible :store_group_details_attributes
end
store_group_detail.rb
class StoreGroupDetail < ActiveRecord::Base
belongs_to :store
belongs_to :store_group
belongs_to :user
attr_accessible :store_id
delegate :store_name, :to => :store
end
_store_group_form.html.erb
<div class="container">
<div class="span8">
<%= nested_form_for([#user, #store_group]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label "Store Group Name (required)" %>
<%= f.text_field :store_group_name %>
<%= f.label "Store Group Description" %>
<%= f.text_area :store_group_description %>
<%= f.fields_for :store_group_details %>
<p><%= f.link_to_add "Add store to group", :store_group_details %></p>
<br>
<%= f.submit "Submit", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
_store_group_detail_fields.html.erb
<p>
<%= f.label "Select Store:" %>
<%= f.collection_select :store_id, Store.order(:store_name),
:id, :store_name, include_blank: true %>
<%= f.link_to_remove "remove" %>
</p>
routes.rb
resources :users do
resources :stores
resources :store_groups
resources :store_group_details
end
There must be a problem with your controller. Did you ever solve this issue?
Look at this answer as it might lead to a solution. Or add you stores_controller.rb.

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.

Searchlogic and :has_many, :through =>

I'm using Searchlogic to search on many fields in a database. One of those fields is a :has_may, :through => relationship, and I can't get it to work.
Here are the relevant parts of the models:
Source.rb:
class Source < ActiveRecord::Base
has_many :authorships
has_many :authors, :through => :authorships
end
Authorship.rb:
class Authorship < ActiveRecord::Base
belongs_to :source
belongs_to :author
end
Author.rb:
class Author < ActiveRecord::Base
has_many :authorships
has_many :sources, :through => :authorships
end
Then, in my view, I have:
<% form_for #search do |f| %>
<fieldset><legend>Search the Online Medieval Sources Bibliography</legend>
<% f.fields_for #search.conditions do |sources| %>
<%= sources.hidden_field :live_not_null %>
<p>
Text Name:
<%= sources.text_field :text_name_like, :size => 95 %> <br />
<% sources.fields_for :author do |authors| %>
Medieval Author:
<%= authors.text_field :name_like, :size => 90 %>
<% end %> <br />
Modern Editor/Translator:
<%= sources.text_field :editor_like, :size => 80 %> <br />
</p>
<% end %>
</fieldset>
<p>
<%= f.submit "Search" %>
</p>
<% end %>
The search page loads just fine, but hitting the "submit" button gives me the following error:
Searchlogic::Search::UnknownConditionError in SourcesController#index
The author is not a valid condition. You may only use conditions that map to a named scope
Here is the code from SourcesController:
class SourcesController < ApplicationController
def index
query = if params[:search] then
params[:search][:hash]
end
#search = Source.search(query)
#sources = #search.all
end
And here are the parameters:
Parameters: {"commit"=>"Search", "search"=>{"hash"=>{"text_name_like"=>"canterbury", "date_begin_greater_than_or_equal"=>"", "author"=>{"name_like"=>""}, "editor_like"=>"", "link_not_blank"=>"0", "trans_none_not_null"=>"0", "trans_other_not_null"=>"0", "trans_english_not_null"=>"0", "trans_french_not_null"=>"0", "region_like"=>"", "live_not_null"=>"", "app_facsimile_not_null"=>"0", "date_end_less_than_or_equal"=>""}, "order"=>""}}
Does anyone have any ideas about what is happening here? Do you need to see more of the error message?
Many thanks!
I too have come across this limitation on searchlogic. there is a 'hack' of a solution that i got. its simple really.
search for items you looking for on the join model. this will give you the ids of the models that have that search.
this is a bit quirky but its the closest solution i could offer you.
hope this helps.
:)

Resources