I’m very green when it comes to RoR and am having trouble trying to figure out whether my issue is caused by a problem in my model associations or whether I’m just not using the right syntax to access the data.
A user can have many budgets. Each budget is comprised of multiple detail lines. I thought there wasn’t a need for the user id in budget_details since it’s captured in budget and so can be inferred through the relationship between the three (maybe?!)
In the budget_details index I want to be able to include the users name; I’ve got it to work in the ‘show’ view but not the index.
I did use a scaffold to set these up, so I know there’s a lot of crud there, I was just trying to do an example before moving to a new project to do it for real.
The actual error is;
NoMethodError in Budget_details#index
Showing C:/Sites/example1/app/views/budget_details/index.html.erb where line #17 raised:
undefined method `name' for nil:NilClass
I can’t see why this fails but the show method works? Is is something to do with the scope? ie show is at the single instance level whereas index is at the ‘all’ level and so it can’t find the data in Users?
Any help much appreciated
Models:
User.rb
class User < ActiveRecord::Base
attr_accessible :email, :name
has_many :budgets
has_many :budget_details, :through => :budgets
Budget.rb
class Budget < ActiveRecord::Base
attr_accessible :budget_name, :user_id
belongs_to :user
has_many :budget_details
Budget_details.rb
class BudgetDetail < ActiveRecord::Base
attr_accessible :amount, :amount_type, :budget_id, :itemname
belongs_to :budget
Controller – budget_details_controller.rb
class BudgetDetailsController < ApplicationController
# GET /budget_details
# GET /budget_details.json
def index
@budget_details = BudgetDetail.all
@users = User.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @budget_details }
end
end
# GET /budget_details/1
# GET /budget_details/1.json
def show
@budget_detail = BudgetDetail.find(params[:id])
@user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: @budget_detail }
end
end
.....
show.html.erb
<%= notice %>
<p>
<b>Username:</b>
<%= @user.name %>
</p>
<p>
<b>Budget:</b>
<%= @budget_detail.budget_id %>
</p>
<p>
<b>Itemname:</b>
<%= @budget_detail.itemname %>
</p>
<p>
<b>Amount:</b>
<%= @budget_detail.amount %>
</p>
<p>
<b>Amount type:</b>
<%= @budget_detail.amount_type %>
</p>
<%= link_to 'Edit', edit_budget_detail_path(@budget_detail) %> |
<%= link_to 'Back', budget_details_path %>
index.html.erb
<h1>Listing budget_details</h1>
<table>
<tr>
<th>Username</th>
<th>Itemname</th>
<th>Budget</th>
<th>Amount</th>
<th>Amount type</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @budget_details.each do |budget_detail| %>
<tr>
<td><%= @user.name %></td>
<td><%= budget_detail.itemname %></td>
<td><%= budget_detail.budget_id %></td>
<td><%= budget_detail.amount %></td>
<td><%= budget_detail.amount_type %></td>
<td><%= link_to 'Show', budget_detail %></td>
<td><%= link_to 'Edit', edit_budget_detail_path(budget_detail) %></td>
<td><%= link_to 'Destroy', budget_detail, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<br />
In your
index.html.erbfile:You haven’t defined
@userin yourindexaction – hence the error.You can avoid this problem and altogether directly pulling
@userin the controller for either action, if just use the association:And to avoid the performance hit for doing so (N+1), you can eager-load them in your controller using
includes:However, this association doesn’t yet exist – you will need to add a ‘has-one-through’ relationship to your
BudgetDetailclass – the reverse of what you did for yourUserclass.To summarize:
You should add a
userassociation toBudgetDetailas ‘has-one-through’.Your controller actions should look like this:
And in your views don’t use
@user.namerather: