Rails 3. How can I only display one nested model form? - ruby-on-rails

I have these two models: Company and CompanyContact.
So I have the usual...
companies_controller.rb
def edit
#company = Company.find(params[:id])
student = #company.students.build
company_contact = #company.company_contacts.build
end
company.rb
has_many :company_contacts, :dependent => :destroy
accepts_nested_attributes_for :company_contacts, :reject_if => :reject_company_contacts, :allow_destroy => true
company_contact.rb
belongs_to :company
form.html.erb
<%= f.fields_for :company_contacts do |builder| %>
<%= render "company_contact_fields", :f => builder %>
<% end %>
_company_contacts.html.erb
<p style="margin:5px 0;">
<%= f.label :first_name %><br />
<%= f.text_field :first_name, :class => 'text_field' %>
</p>
<p style="margin:5px 0;">
<%= f.label :email %><br />
<%= f.text_field :email, :class => 'text_field' %>
</p>
In the edit form, if I already have a company contact for a company, it gives me that existing record plus another empty company contact form ready to be filled out. Which is ok because that it's supposed to happen.
What I need to do is only have ONE company contact, so if there is already a company contact, I don't want to display another form to add an extra company contact. I do not want to setup a has_one relationship because my client might want to add extra company contacts in the future and also when I tried a has_one relationship I got bunch of errors.
Ok so to only have ONE company contact per company, I tried a counter solution, you know in the loop set counter = 0 and then check if counter > 0 but that "solution" didn't work. What would you suggest?

If I got you correct, then
#company.company_contacts.build unless #company.company_contacts.present?
is what you are looking for.
This way, if a company already has a contact, then no more contacts are built. Likewise, if a company has no contact, this will build up contacts which shall then be used by f.fields_for :company_contacts to render in the form.

Related

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.

Correct usage of simple_form collection_check_boxes

I have set up 2 models as following :
class Sector < ActiveRecord::Base
attr_accessible :summary, :title, :sector_ids
belongs_to :platform
end
and
class Platform < ActiveRecord::Base
attr_accessible :name, :url
has_many :sectors
end
and a form which tries to use example from here as follows :
<%= simple_form_for #platform, :html => { :class => 'form-vertical'} do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="row-fluid">
<div class="span12">
<div class="span6">
<%= field_set_tag "General" do %>
<%= f.input :name %>
<%= f.input :url %>
<%= f.collection_check_boxes :sector_ids, Sector.all, :title, :title %>
<% end %>
</div>
</div>
</div>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
nevertheless, after I try to submit the form I get the following error :
Can't mass-assign protected attributes: sector_ids
What am I missing here? I successfully migrated the database after adding appropriate associations, but it seems like Rails doesnt really know what to do now wit the sector ids that are selected.
Solution :
class Sector < ActiveRecord::Base
attr_accessible :summary, :title
belongs_to :platforms
end
and
class Platform < ActiveRecord::Base
attr_accessible :name, :url, :platform_attributes, :sector_ids
has_many :sectors
end
and in the view :
<%= f.association :sectors, :as => :check_boxes %>
Ofcourse, do not forget to run "rake db"migrate", in case you havent done it yet. I was also required to restart the server in order for the changes to apply.
I hope this helps someone.
Try adding { :sector_ids => [] } to your Platform permits list.
https://github.com/rails/strong_parameters#nested-parameters
First I think you should remove :sectors_ids from the attr_accessible of Sector and try.
If the above doesn't works (which probably may not work), see the documentation of simple_form, specially read https://github.com/plataformatec/simple_form#associations and https://github.com/plataformatec/simple_form#collection-check-boxes; the last one makes what you are trying to do, but, in the associations they use has_and_belongs_to_many associations.
Finally, check this out https://github.com/plataformatec/simple_form/issues/341
Update
The solution is to make a has_and_belongs_to_many relationship, there is no way out, because, you want to associate some sectors to one platform, but, you want to associate the same sectors to other platforms, otherwise you won't need checkbox, only nested forms to keep adding new records. You have to make this change in your database design and structure.

Rails 3 has_many :through form with dropdown menus?

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!

Nested model form not submitting everything

I heard about this community while listening to Hypercritical and I am excited to join in with my first question. I am working on my first rails App and I have run into an issue that I cannot seem to crack. I have been watching Railscast, Lynda.com, and Googling for days but I still cannot comprehend how to create a form that that will update my has_many :through associations at once. Allow me to try an explain what I am doing.
My Goal:
The firm I work for provides many "Service Offerings" and I want to be able to create a new service offering on one page and have it create the contacts and other information that is associated with it. The additional information such as "contacts" will live in their own tables because they may need to be referenced by many "Service Offerings."
Problem:
When I submit the form the "Service Offering" fields submit and are entered into the database, but the fields for the "Business Developer" do not. Obviously, I would like everything to be entered into its appropriate table and the for the IDs to be linked in the join table. I would really appreciate any insight that you could provide.
What I Have So Far: What you see below is Service Offerings and Business Developers. Eventually I will be adding Contacts, Photos, and Files but I thought I would start simply and work my way up.
Models:
class ServiceOffering < ActiveRecord::Base
attr_accessible :name, :description
has_many :business_developer_service_offerings
has_many :business_developers, :through => :business_developer_service_offerings
accepts_nested_attributes_for :business_developer_service_offerings
end
class BusinessDeveloper < ActiveRecord::Base
attr_accessible :first_name, :last_name
has_many :business_developer_service_offerings
has_many :service_offerings, :through => :business_developer_service_offerings
end
class BusinessDeveloperServiceOffering < ActiveRecord::Base
belongs_to :business_developer
belongs_to :service_offering
end
Controller:
def new
#service_offering = ServiceOffering.new
#service_offering.business_developers.build
end
def create
#service_offering = ServiceOffering.new(params[:service_offering])
if #service_offering.save
redirect_to(:action => 'list')
else
render('new')
end
end
View:
<%= form_for((#service_offering), :url => {:action => 'create'}) do |f|%>
<p>
<%= f.label :name%>
<%= f.text_field :name %>
<%= f.label :description%>
<%= f.text_field :description %>
</p>
<%= f.fields_for :business_developer do |builder| %>
<p>
<%= builder.label :first_name%>
<%= builder.text_field :first_name %>
<%= builder.label :last_name%>
<%= builder.text_field :last_name %>
</p>
<%end%>
<%= f.submit "Submit" %>
<%end%>
I figured it out. It turns out a few things were wrong and needed to be changed in addition to the two suggestions #Delba made.
The Form:
I took a look at RailsCasts #196 again and noticed that my form looked different than the one used there, so I tried to match it up:
<%= form_for #service_offering do |f|%>
<p>
<%= f.label :name%>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_field :description %>
</p>
<%= f.fields_for :business_developers do |builder| %>
<p>
<%= builder.label :first_name %>
<%= builder.text_field :first_name %>
<%= builder.label :last_name %>
<%= builder.text_field :last_name %>
</p>
<%end%>
<%= f.submit "Submit" %>
<%end%>
Initially, this presented an error:
undefined method `service_offerings_path'
Routes:
This lead me to learn about RESTful Routes because I was using the old routing style:
match ':controller(/:action(/:id(.:format)))'
So I updated my routes to the new RESTful Routes style:
get "service_offerings/list"
resource :service_offerings
resource :business_developers
attr_accessible:
That got the form visible but it was still not working. So I did some searching around on this site and found this post that talked about adding "something_attributes" to your parent objects model under attr_accessible. So I did:
class ServiceOffering < ActiveRecord::Base
has_many :business_developer_service_offerings
has_many :business_developers, :through => :business_developer_service_offerings
accepts_nested_attributes_for :business_developers
attr_accessible :name, :description, :business_developers_attributes
end
That change along with #Delba's suggestion shown in the above model and controller listed below solved it.
def new
#service_offering = ServiceOffering.new
#business_developer = #service_offering.business_developers.build
end
You just forgot to assign #business_developper.
def new
#service_offering = ServiceOffering.new
#business_developper = #service_offering.business_developpers.build
end
-
#business_developer = #service_offering.business_developers.build
initializes an instance of biz_dev which is then available in the view.
fields_for :biz_dev isn't really tied to this instance but to the many-to-many relationship btw serv_off and biz_dev.
In this way, you can add multiple input for additional biz_dev if you initialize another biz_dev instance in your controller. For instance:
5.times { #service_offering.biz_dev.build }
will add additional fields in your form without you having to declare them in your view.
I hope it helped.

One-To-Many Model Update RoR

I've got a one-to-many relationship from Treatment to Cost. The reason for this is because for invoicing and tax purposes we need to keep a record of changes in price for a treatment. So this is implemented by saying that the cost is equal to the most recent cost entry associated with that treatment.
This feature needs to be completely transparent to the user, they shouldn't know anything about the historical costs, just that there's a current one.
So when they hit edit, and do an update, if the cost of the treatment were to change, I then want to update the cost table also. The problem I have is with the models being represented in a form.
<% form_for([:admin, #treatment]) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_field :description %>
</p>
<p>
<%= f.label :duration %><br />
<%= f.text_field :duration %>
</p>
<p>
<%= f.submit 'Save' %>
</p>
<% end %>
So Treatment has_many :cost in the model, and in this form I want to have the cost field with the latest cost in it. For a start, how do I do that? Additionally, how do I update the cost model if the cost has changed? I'm assuming it's done in the controller, but if I have a text_field for the cost, how do I disassociate it with the #treatment model?
Cheers
I had a similar issue, so I had my has_many association but then I had a has_one association that pointed to the most recent version.
NOTE: I am using MSSQL, so your syntax might be a little different.
class Treatment < ActiveRecord::Base
has_many :costs
has_one :current_cost, :class_name => "Cost",
:conditions => "costs.id = (SELECT MAX(c.id)
FROM costs c
WHERE c.treatment_id = costs.treatment_id)"
end
I started with this:
class Treatment < ActiveRecord::Base
has_many :costs
def current_cost
self.costs.last(:order => "id")
end
end
But after it got large enough, I needed to minimize database dips and this was becoming a bottleneck.
If you had the ability of setting an enabled field in the costs model and using a validation to ensure that only one cost is enabled per treatment. Then I would recommend:
class Treatment < ActiveRecord::Base
has_many :costs
has_one :current_cost, :class_name => "Cost", :conditions => ["costs.enabled = ?", true]
end
I hope this helps!

Resources