Building has many through relationship rails from existing records - ruby-on-rails

I'm a bit stuck on creating a form for a has many through relationship. At the moment my models are that Songs can have many Setlists and vice versa, through Allocations.
I'm currently working on an edit page, in which a user can add songs to a setlist. The view currently looks like this:
<h1>Edit a Setlist</h1>
<div class="row">
<div class="span8">
<%=form_for(#setlist) do|f|%>
<%=f.label :date, "Set a date" %>
<span><%=f.date_select :date%><span>
<div>
<div id="library">
<%= render 'library' %>
</div>
<%= render 'songs_in_set' %>
</div>
<%=f.submit "Save", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
The library partial referred to above:
<table class= "table table-striped table-bordered">
<thead>
<th>Title</th>
<th>Artist</th>
<th>Add to Set</th>
</thead>
<tbody>
<% #songs.each do |song| %>
<tr>
<td><%= song.title %></td>
<td><%= song.artist %></td>
<td><%= link_to "ADD", '#' %></td>
</tr>
<% end %>
</tbody>
</table>
I want to convert that link in the library partial to a form for creating a new allocation, thus adding a song to the set list.
The relevant bits from my controllers:
The setlist controller:
def edit
#songs = Song.all(order: 'title')
#setlist = Setlist.find(params[:id])
#allocations = #setlist.allocations
end
def update
#setlist = Setlist.find(params[:id])
if #setlist.update_attributes(params[:setlist])
flash[:success] = "SAVED!"
redirect_to setlist_path(#setlist)
else
render 'edit'
end
end
and the allocations controller:
def new
#allocation = Allocation.new
end
def create
#allocation = Allocation.new(params[:allocation])
if #allocation.save
flash[:success] = "Songs added"
redirect_to edit_setlist_path(#allocation.setlist_id)
else
flash[:fail] = "Error"
redirect_to edit_setlist_path(#allocation.setlist_id)
end
end
I know that I've got to do something along the lines of setlist.allocations.build but I'm having trouble getting the right parameters (getting each individual song id and the setlist id). I've tried putting a form for helper within the songs.each do loop but that didn't seem to work. I'm a bit lost so any pointers in the right direction would be appreciated. Thanks in advance

Try adding this to Setlist:
accepts_nested_attributes_for :allocations, :dependent => :destroy
Then you can nest association fieldsets within your setlist form. In the library partial:
<td>
<%= f.fields_for :allocations do |ff| %>
<%= f.hidden_field :song_id, song.id %>
...
<% end %>
</td>
This should work if you want to create allocations for songs which exist already, but you might need something different for a scenario when you are also creating songs in the same form. I had a problem like that not long ago, and wasn't able to find a clean solution, but let me know and I can share that as well...

Related

how can i get project_id by remarks in ruby on rails

I have manager remark model that takes input as a remark and decision value and saves it with the project site ID. I have a project site model that takes input as name, date, and file and stores it. Many remarks have a many to one relation with project site ID, and the project site belongs to the manager remark. I want to access the decision attribute boolean value in project site index form, but I am unable to access that boolean value in the index page of the project site. Here is my code of project site and manager remarks model, view and controller-
project site index.html.erb
<table>
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Attendance</th>
<th>Status</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #project_sites.each do |project_site| %>
<tr>
<td><%= project_site.name.titleize %></td>
<td><%= project_site.date %></td>
<td><%= link_to ' View attendance', project_site.file, :class => "fi-page-export-csv" %></td>
<td><%= "here i want to access manager remark decision value" %></td>
<td><%= link_to 'Remark ', project_site %><span>(<%= project_site.manager_remarks.size %>)</span></td>
<td><%= link_to 'Edit', edit_project_site_path(project_site) %></td>
<td><%= link_to 'Destroy', project_site, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
project site controller
def index
#project_sites = ProjectSite.all.order("created_at DESC")
#manager_remark = ManagerRemark.joins(:project_site).where(:project_sites => { :user_id => #user.id })
end
# GET /project_sites/1
# GET /project_sites/1.json
def show
#manager_remark = ManagerRemark.new
#manager_remark.project_site_id = #project_site.id
end
# GET /project_sites/new
def new
#project_site = ProjectSite.new
end
def project_site_params
params.require(:project_site).permit(:name, :date, :file)
end
manager_remark controller
class ManagerRemarksController < ApplicationController
def create
#manager_remark = ManagerRemark.new(remark_params)
#manager_remark.project_site_id = params[:project_site_id]
#manager_remark.save
redirect_to project_site_path(#manager_remark.project_site)
end
def remark_params
params.require(:manager_remark).permit(:remark, :decision)
end
end
manager_remark view form
<%= form_for [ #project_site, #manager_remark ] do |f| %>
<div class="row">
<div class="medium-6 columns">
<%= f.radio_button :decision, true %>
<%= f.label :approve %>
<%= f.radio_button :decision, false %>
<%= f.label :reject %>
</div>
<br>
<br>
<div class="medium-6 cloumns">
<%= f.label :remark %><br/>
<%= f.text_area :remark %>
</div>
</div>
<div>
<%= f.submit 'Submit', :class => 'button primary' %>
</div>
<% end %>
routes.rb
Rails.application.routes.draw do
root to: 'home#index'
devise_for :users
resources :project_sites do
resources :manager_remarks
end
get '/project_manager_level_two' => 'project_manager_level_two#index'
get '/project_managers' => 'project_managers#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
If I understand correctly, you have a ProjectSite that contains a ManagerRemark with a decision, right? If that's the case, the simple answer is:
<%= project_site.ManagerRemark.decision %>
If you are saying that each ProjectSite has many ManagerRemarks, you'll want to place the above inside a loop, like so:
<% project_site.manager_remarks.each do |manager_remark| %>
<%= manager_remark.decision %><br/>
<% end %>
This assumes that your models are correctly configured to recognize these relationships. The above may also be optimized by adding an include clause to your fetch inside the controller and there's no need to fetch the ManagerRemark objects separately. Therefore, you'd probably want something like:
def index
#project_sites = ProjectSite.all.includes( :manager_remark ).order("created_at DESC")
end

Error when trying to link to patient profile

I'm getting an error when trying to link_to a patient profile when a provider views his patients list. I have no problem displaying all the names of the patients that belong to the provider but when trying to link to the patient profile I get an undefined method 'id'.
So the way it works is, patients can search for providers and add them to the List model. On the provider side, I just list out all the patients that added that specific provider. Here is my erb code below,
<div class="body">
<div class="body">
<% if #active_patients.count > 0 %>
<table>
<thead>
<tr>
<th>Patient Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% #active_patients.each do |list| %>
<tr>
<td>
<%= list.patient.role.user.first_name %> <%= list.patient.role.user.last_name %>
</td>
<td>
<%= link_to patient_path(id: #patient.id), class: "btn" do %>View<% end %> . #### THIS IS THE LINE
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<div class="no-records">
<%= image_tag "icon-no-records", class: "image" %>
<div class="text">You have no patients.</div>
</div><!--no-records-->
<% end %>
</div><!--body-->
</div>
Here is my List model,
class List < ApplicationRecord
belongs_to :membershipable, :polymorphic => true
belongs_to :provider
def patient
membershipable_type=='Patient' ? membershipable : nil
end
def provider_user
patient.try(:user)
end
end
Also here's the error message ->
Let Rails do the work of building the path. Each ActiveRecord model has a to_param method which decides how the instance will be encoded in an URL. By default it returns the model id but it could also be a slug based on the title or another property of the model.
Calling your helper like patient_path(patient) should do the trick.
Additionally, in your current code, you're referring to the previously unused #patient variable, even though it looks like you want to refer to list.patient instead.
Here:
<% #active_patients.each do |list| %>
<tr>
<td>
<%= list.patient.role.user.first_name %> <%= list.patient.role.user.last_name %>
</td>
<td>
<%= link_to patient_path(id: #patient.id), class: "btn" do %>View<% end %> . #### THIS IS THE LINE
</td>
</tr>
<% end %>
you have the variable list available to you. It appears that you get the patient by doing list.patient, as you do here:
<%= list.patient.role.user.first_name %> <%= list.patient.role.user.last_name %>
But, then you try to use a variable called #patient, here:
<%= link_to patient_path(id: #patient.id), class: "btn" do %>View<% end %> .
when you don't have the variable #patient. So, you get your nil error.
Instead, it seems you should do:
<%= link_to patient_path(id: list.patient.id), class: "btn" do %>View<% end %> .
Or, as milgner points out, you could simply do:
<%= link_to patient_path(list.patient), class: "btn" do %>View<% end %> .
Also, you might want to look into the Law of Demeter, which you violate (IMO) when you do:
list.patient.role.user.first_name

How can I move calculations in View to Model?

I´m very new to ROR and I have stumbled up on a problem. I believe it´s a newbie problem, but I´m not sure if it´s. I´m building this app were users should be able to enter the amount of paper use in their business and from there the app calculates their eco footprint so to say.
at login the user enters their staff number. The staff number is used to divide with paper_weight etc. ( see in view example).
I know it basically works the way it is now, but I´m aware of this is not the best practice.
I also wanted to move the div containing the running totals to application.html.erb but I can´t do that since my calculations are in the View but not in the Model.
here is a link to the github repo https://github.com/DadiHall/SprettaEMS1.5
Are there any other ways to do this? Can someone please advise me?
my index.html.erb view
<div class="row">
<div class="col-md-10 ">
<h1>Pappírsnotkun</h1>
<table class=" well table table-hover">
<thead>
<tr>
<th>Dags</th>
<th>Tegund</th>
<th>Þyngd</th>
<th>Kostnaður</th>
<th>Þar af umhvm. pappír</th>
<th>Pappírs magn m.v. Stöðugildi</th>
<th>Hl.f. Umhvm. Pappírs</th>
<th>Fjöldi trjáa m.v. magn pappírs</th>
</tr>
</thead>
<tbody>
<% paper_total_cost = 0 %>
<% paper_total_trees = 0 %>
<% paper_total_staff_ratio = 0 %>
<% #papers.each do |paper| %>
<tr>
<td><%= paper.date.strftime("%Y/%m/%d") %></td>
<td><%= paper.paper_type %></td>
<td><%= paper.paper_weight %> -kg.</td>
<td><%= paper.paper_cost %> -kr.</td>
<td><%= paper.env_paper_weight %> -kg.</td>
<td><%= (paper.paper_weight.to_i / current_user.staff) %> kg/stöðugildi </td>
<td><%= (( paper.env_paper_weight / paper.paper_weight)* 100) %>%</td>
<td><%= (( paper.paper_weight.to_f/1000)*15) %></td>
<td><%= link_to 'Sýna', paper, class: 'btn btn-xs btn-info' %></td>
<td><%= link_to 'Uppfæra', edit_paper_path(paper), class: 'btn btn-xs btn-warning' %></td>
<td><%= link_to 'Eyða', paper, method: :delete, class: 'btn btn-xs btn-danger', data: { confirm: 'Are you sure?' } %></td>
<% paper_total_cost = paper_total_cost + (paper.paper_cost.to_i) %>
<% paper_total_trees = paper_total_trees + (( paper.paper_weight.to_f/1000)*15) %>
<% paper_total_staff_ratio = paper_total_staff_ratio + (paper.paper_weight.to_i / current_user.staff) %>
</tr>
<% end %>
</tbody>
</table>
</div>
<div class="row">
<div class=" col-md-2 panel panel-default pull-right" >
<div class="panel panel-heading ">
<h5 class="user-name">User: <% if current_user && current_user %>
<%= current_user.name %>,
Staff: <%= current_user.staff %>
<% end %>
</h5>
</div>
<div class="panel-body">
<h6 class="pull-right">Pappír Kostnaður Samtals:<%= number_with_precision(paper_total_cost.to_f, precision: 2) %> kr </h6><br>
<h6 class="pull-right">Fjöldi Trjáa vegna Pappírs notkunar:<%= number_with_precision(paper_total_trees.to_f, precision: 2) %> Tré </h6><br>
<h6 class="pull-right">Magn Pappírs f. hvert stöðugildi:<%= number_with_precision(paper_total_staff_ratio.to_f, precision: 2) %> Kg </h6>
</div>
</div>
</div>
<%= link_to 'New Paper', new_paper_path %>
My papers_controller.rb
class PapersController < ApplicationController
before_action :set_paper, only: [:edit, :update, :show, :destroy]
def index
#papers = Paper.all
end
def create
#paper = Paper.new(paper_params)
if #paper.save
flash[:success] = "Messages sent."
redirect_to paper_path(#paper)
else
flash[:danger] = "Error occured, message has not been sent."
redirect_to new_paper_path
end
end
def new
#paper = Paper.new
end
def edit
end
def show
end
def update
if #paper.update(paper_params)
flash[:success] = "Line was successfully updated"
redirect_to papers_path(#paper)
else
render 'edit'
end
end
def destroy
#paper.destroy
flash[:danger] = "Line was successfully destroyed"
redirect_to papers_path
end
private
def set_paper
#paper = Paper.find(params[:id])
end
def paper_params
params.require(:paper).permit(:paper_type, :date, :paper_weight, :paper_cost, :env_paper_weight)
end
end
my paper.rb Model
class Paper < ActiveRecord::Base
has_many :users
end
You can move the calculations into the model by defining functions in the model.
The first calculation uses current_user, which will not be in the model by default, so that'll have to be a parameter. The other two are just using paper's attributes; pardon my poor naming, I'm not clear what the second two metrics are.
class Paper
has_many :users
def paper_weight_per_capita(current_user)
paper_weight.to_i / current_user.staff
end
def paper_weight_metric1
( env_paper_weight / paper_weight)* 100)
end
def paper_weight_metric2
(( paper_weight.to_f/1000)*15)
end
end
And in your view
<%= paper.paper_weight_per_capita(current_user) %>
<%= paper.paper_weight_metric1 %>
If you want the running total in your layout then you'll need to load the paper objects on every page. You can do this with an application_controller before_action, though some folks don't like this approach and here, but I think it is a reasonable way for you now.
class ApplicationController < ActionController::Base
before_action :load_running_totals
def load_running_totals
# method 1. Similar to how the calculation is done in the view.
# #paper_weight_per_capita = Papers.all.map(&:paper_weight_per_capita, current_user).reduce(&:+)
# method 2. does the summation in the database without having to load all the records into ruby. (weight needs to be a number in the db.)
#paper_weight_per_capita = Papers.sum(:weight) / current_user.staff
end
end
Now in any view (including your layout):
<%= #paper_weight_per_capita %>

Rails 4 Restaurant order system ActiveRecord::RecordNotFound in ItemsController#create

I am creating a simple restaurant ordering system with a one to many relationship between a menu and its items. One menu has many items. For the sake of simplicity it is not a many to many.
I am able to create menus fine, but would like to be able to add and show the menu items for that menu in the menus show action.
The menu show action displays okay but when I try to add a new menu item I get the following error:
ActiveRecord::RecordNotFound in ItemsController#create
Couldn't find Menu with 'id'=
raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
And here is the queries from the terminal:
Started POST "/items" for ::1 at 2015-01-11 16:09:44 +0000
Processing by ItemsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"***", "item"=>{"name"=>"Test", "price"=>"23", "course"=>"Main", "vegetarian"=>"1", "allergy"=>""}, "commit"=>"Add item"}
Menu Load (0.3ms) SELECT `menus`.* FROM `menus` WHERE `menus`.`id` = NULL LIMIT 1
Completed 404 Not Found in 8ms
EDIT: I followed doon's advice and looked into nested resources and this is indeed a better way of doing it. The updated code is below:
routes.rb
resources :menus do
resources :items
end
menus_controller.rb
def show
#menu = Menu.find(params[:id])
#items = #menu.items
end
items_controller.rb
def create
#menu = Menu.find(params[:menu_id])
#item = #menu.items.create!(item_params)
if #item.save
flash[:success] = "Item added!"
redirect_to #menu
else
flash[:danger] = "Errors found!"
redirect_to #menu
end
end
private
def item_params
params.require(:item).permit(:name, :price, :course, :vegetarian, :allergy, :menu_id)
end
And the Menus
show.html.erb
<%= link_to "<< Back", menus_path, data: { confirm: back_message } %>
<h1><%= #menu.name %> menu</h1>
<center><button id="toggleButton" class="btn btn-sm btn-info">Show/Hide Add Item Form</button></center>
<br>
<div class="row">
<div class="col-xs-offset-3 col-xs-6 toggleDiv hideDiv">
<%= form_for [#menu, Item.new] do |f| %>
<table class="table table-condensed table-no-border">
<tr>
<th scope="row" class="col-xs-2">Name:</th>
<td class="col-xs-10"><%= f.text_field :name %></td>
</tr>
<tr>
<th scope="row">Price:</th>
<td><%= f.text_field :price %></td>
</tr>
<tr>
<th scope="row">Course:</th>
<td><%= f.select(:course, options_for_select([['Starter', 'Starter'], ['Main', 'Main'], ['Dessert', 'Dessert'], ['Drink', 'Drink']]), prompt: "Please select...", class: 'form-control') %></td>
</tr>
<tr>
<th scope="row">Vegetarian:</th>
<td><%= f.check_box :vegetarian %></td>
</tr>
<tr>
<th scope="row">Allergy:</th>
<td><%= f.text_field :allergy %></td>
</tr>
<tr><td colspan="2"><%= f.submit "Add item", class: "btn btn-sm btn-success col-xs-offset-4 col-xs-4" %></td></tr>
</table>
<% end %>
</div>
</div>
<table class="table table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Course</th>
<th>Vegetarian</th>
<th>Allergy</th>
</tr>
</thead>
<tbody>
<% #items.each do |item| %>
<tr>
<td><%= item.name %></td>
<td><%= number_to_currency(item.price, unit: "£") %></td>
<td><%= item.course %></td>
<td><%= item.vegetarian %></td>
<td><%= item.allergy %></td>
</tr>
<% end %>
</tbody>
</table>
What I would probably do in a case like this (especially if your item doesn't exist outside of a menu) is to use Nested Resources:
http://guides.rubyonrails.org/routing.html#nested-resources
http://railscasts.com/episodes/139-nested-resources (a bit dated, but still a decent grounding)
Below are some changes to get you pointed in this direction.
config/routes.rb
resources :menu do
resources :items
end
now our urls will look like
/menu/:menu_id/items/
So we need to adjust the items_controller to get the menu by looking at :menu_id, and we don't need the hidden field anymore. I put it in a before_action as every method in the controller will build through the association.
items_controller.rb
class ItemsController < ApplicationController
before_action :find_menu
...
def create
#item = #menu.items.new(item_params)
if #item.save
redirect_to #menu, notice: 'item added'
else
redirect_to #menu, warning: 'item failed'
end
end
...
private
def find_menu
#menu = Menu.find(params[:menu_id])
end
end
if you want to show it on the menu, we need a new Item to display.
menus_controller.rb
def show
#menu = Menu.find(params[:id])
#item = #menu.items.new
end
Then we need to make use of the nested resource in the menus/show view. By passing in the array of the menu and the item, rails will generate the correct path.
menus/show.html.erb
<%= form_for [#menu,#item] do |f| %>
Are you posting to "/post" or "post/[:id]"?
#menu = Menu.find(params[:id])
Will not find anything if you do not pass it an id. The id can come from the URL params, but this means you should be sending your request to "/post/[:menuid].
You can use the gem https://github.com/charliesome/better_errors to debug your program at the controller level and execute rails console there. just put in "fail" in your controller and then you will have a command line interface where you can inspect your params value and ensure it has the id included and play with the console there.
I have now solved it. Thanks to better_errors I was able to better understand what was going on.
I implemented the show action in menus_controller.rb from aaron.
def show
#item = Item.new
#menu = Menu.includes(:items).find(params[:id])
end
I needed a way to pass the menu id into the menu_id field, so I added a hidden field in the table or show.html.erb passing the current #menu.id into the :menu_id field.
<%= f.hidden_field :menu_id, :value => #menu.id %>
I have read online that passing values through hidden fields is not a great idea though.
Then in the create action of the Items Controller I was able to make a new record as usual redirecting to the existing show view using the menu id from the item object.
def create
#item = Item.new(item_params)
if #item.save
redirect_to menu_path(#item.menu_id)
else
redirect_to menu_path(#menu.menu_id)
end
end
It feels like a bit of a hack so open to suggestions on how to improve.

According to a lynda.com screencast, rails is supposed to be generating a button using the form_for (view) method, but it's not

EDIT: The first question is my premise itself. Is rails/html SUPPOSED to generate a "Create Subject" button without me explicitly asking it to?
So here is the controller that is working with the view
class SubjectsController < ApplicationController
def index
list
render('list')
end
def list
#subjects = Subject.order("subjects.position ASC")
end
def show
#subject = Subject.find(params[:id])
end
def new
#subject = Subject.new(:name => 'default')
end
def create
#instantiate a new object using form params
#subject = Subject.new(params[:subject])
#save the subject
if #subject.save
#if save succeeds redirect to list action
else
#if save fails, redisplay form
render('new')
end
end
end
And here is the misbehaving view (html.erb) file which isn't generating my button
<%= link_to("<< Back to List", {:action => 'list'}, :class => 'back-link') %>
<div class="subject new">
<h2>Create Subject</h2>
<%= form_for(:subject, :url => {:action => 'create'}) do |f| %>
<table summary="Subject form fields">
<tr>
<th>Name</th>
<td><%= f.text_field(:name) %></td>
</tr>
<tr>
<th>Position</th>
<td><%= f.text_field(:position) %></td>
</tr>
<tr>
<th>Visible</th>
<td><%= f.text_field(:visible) %></td>
</tr>
</table>
<% end %>
</div>
Currently, the output on the browser is:
'<< Back to List' (link)
<h2>Create Subject</h2>
Name [blank-form]
Position [blank-form]
Visible [blank-form]
[missing button location]
There is supposed to (according to lynda.com) be a button which says "Create Subject" in the missing button location, but it's not there.
Nothing in your code is supposed to generate a button.
You'll need to add:
<%= f.submit 'Create Subject' %>
inside the form. Maybe between </table> and <% end %>

Resources