Eager loading in Rails 3? - mysql

I am attempting to learn how to increase the performance of Rails applications and the first step I am looking at is eager loading.
I've configured the bullet gem which shows where I can make use of eager loading, however, I'm not too sure how to make use of the help!
An example log is:
2012-04-26 15:59:34
0.0.0.0:3000http://0.0.0.0:3000/animals
N+1 Query detected
Animal => [:client]
Add to your finder: :include => [:client]
N+1 Query method call stack
N+1 Query method call stack
/Users/dannymcclelland/Projects/premvet/app/views/animals/index.html.erb:26:in `block in _app_views_animals_index_html_erb__2796162405947806753_70316525286320'
/Users/dannymcclelland/Projects/premvet/app/views/animals/index.html.erb:22:in `_app_views_animals_index_html_erb__2796162405947806753_70316525286320'
/Users/dannymcclelland/Projects/premvet/app/controllers/animals_controller.rb:7:in `index'2012-04-26 15:59:34[WARN] user: dannymcclelland
0.0.0.0:3000http://0.0.0.0:3000/animals
Unused Eager Loading detected
Animal => [:id, :AnimalName, :Species]
Remove from your finder: :include => [:id, :AnimalName, :Species]2012-04-26 16:00:56
The lines that jump out are:
N+1 Query detected
Animal => [:client]
Add to your finder: :include => [:client]
and
Unused Eager Loading detected
Animal => [:id, :AnimalName, :Species]
Remove from your finder: :include => [:id, :AnimalName, :Species]
What I am unsure of, is what is the definition of the 'finder'. Is it the block in the view or is it in the controller.
Taking the first section of log that jumps out at me the controller is as follows:
def index
#animals = Animal.page(params[:page]).per_page(15)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #animals }
end
end
the model:
class Animal < ActiveRecord::Base
self.table_name = 'animal'
self.primary_key = 'PVID'
attr_accessible :AddedBy, :Age, :AnimalBFAmount, :AnimalBalance, :AnimalName, :Archive, :BillType, :Breed, :ChronicStatus, :Class, :Classification, :ClientKey, :Colour, :Date1, :DateOfBirth, :DateofBirth, :Dead, :DiaryQueue, :DiscField, :DrugsAtCost, :DrugsNoVAT, :ESDAmount, :ESDType, :FNote, :FirstRegisteredDate, :Height, :IDNumber, :Insured, :InsuredWith, :IsClient, :IsClientDate, :IsMaster, :LastBilledAmount, :LastBilledDate, :LastConsDate, :LastContributionDate, :LastPaidDate, :LastWeightDate, :Locked, :LoyaltyMultiplier, :LoyaltyPoints, :MR_Flag_0, :MR_Flag_1, :MR_Flag_10, :MR_Flag_11, :MR_Flag_12, :MR_Flag_13, :MR_Flag_14, :MR_Flag_15, :MR_Flag_2, :MR_Flag_3, :MR_Flag_4, :MR_Flag_5, :MR_Flag_6, :MR_Flag_7, :MR_Flag_7, :MR_Flag_8, :MR_Flag_9, :Mileage, :Neutered, :NextApptDate, :ORT, :OldSex, :Opt_Flag_0, :Opt_Flag_1, :Opt_Flag_2, :Opt_Flag_3, :Opt_Flag_4, :Opt_Flag_5, :Opt_Flag_6, :Opt_Flag_7, :PVID, :PreferredContact, :PreferredUser, :Ref1, :RefPrac, :ReferredBy, :SSDType, :SeenInPeriod, :SendBill, :Sex, :SiteAnimal, :Species, :Status, :SurcAmount, :SurcType, :SurgeryNumber, :TBU, :TOSAmount, :TOSDrugs, :TOSFees, :TOSType, :Weight
belongs_to :client, :foreign_key => 'ClientKey'
has_many :clinicals, :foreign_key => 'PVID'
has_many :labs, :foreign_key => 'PVID'
has_many :consults, :foreign_key => 'pvid'
has_many :sheets, :foreign_key => 'PVID'
default_scope :order => "Dead ASC, AnimalName ASC"
end
Note that I'm using a legacy database hence the strange column names etc.
The view contains the following:
<% #animals.includes(:client).each do |animal| %>
<tr>
<td><%= animal.id %></td>
<td><%= animal.AnimalName %></td>
<td><%= link_to animal.client.Surname, animal.client %></td>
<td><%= animal.Species %></td>
<td><%= animal.Breed %></td>
<td><%= animal.Dead %></td>
<td><%= link_to 'Show', animal %></td>
</tr>
<% end %>
So, should I be adding the other recommended columns to the view 'finder'? Or somewhere else?
Any help would be appreciated!

In your controller, you want to update your query to include the client:
#animals = Animal.includes(:client).page(params[:page]).per_page(15)

Related

undefined method `cliq_requests' for nil:NilClass

I keep getting this annoying error consistently and I cannot solve it. I recently posted a question on the same topic and got no productive help.
I want users to request to join a group. Cliqs = Groups. All of my console tests seem correct, but I cannot seem to find a solution to my problem. The association is showing up, but I can't seem to get the update/accept method to run.
This is driving me crazy! How do I fix this?
Here is my code:
My Models:
class User < ActiveRecord::Base
has_many :uploads
has_one :owned_cliq, foreign_key: 'owner_id', class_name: 'Cliq', dependent: :destroy
has_many :cliq_memberships, dependent: :destroy
has_many :cliqs, through: :cliq_memberships
has_many :cliq_requests, dependent: :destroy
...
end
class Cliq < ActiveRecord::Base
belongs_to :owner, class_name: 'User'
has_many :cliq_memberships, dependent: :destroy
has_many :members, through: :cliq_memberships, source: :user
has_many :cliq_requests, dependent: :destroy #cliq_request_sender
has_many :pending_members, through: :cliq_requests, source: :user, foreign_key: 'user_id'
end
class CliqRequest < ActiveRecord::Base
#from
belongs_to :user
#to
belongs_to :cliq
#validate :not_member
#validate :not_pending
def accept
cliq.members << pending_member
destroy
end
end
My controller:
class CliqRequestsController < ApplicationController
def index
#incoming
##cliq_requests_received = CliqRequest.where(cliq: cliq)
#outgoing
##cliq_requests_sent = current_user.cliq_requests
end
def show
end
def create
cliq = Cliq.find_by(params[:id])
#cliq_request = current_user.cliq_requests.new(cliq: cliq)
if #cliq_request.save
redirect_to current_user #change to cliqs/cliq path later
else
redirect_to cliq_path
end
end
def update
#cliq = Cliq.find_by(id: params[:cliq_id])
#cliq_request = #cliq.cliq_requests.find_by(id: params[:id])
#cliq_request.accept
end
def destroy
#cliq_request.destroy
end
end
My View:
<h1><%= #cliq.name %></h1>
<%= link_to 'Request to join Cliq', '/cliqs/:cliq_id/cliq_requests', :method => :post %>
<% #cliq_members.each do |cliq_member| %>
<ul><%= link_to cliq_member.username, user_path(cliq_member) %></ul>
<% end %>
<% if #current_user = #cliq.owner %>
<% #cliq.pending_members.each do |pending_member| %>
<ul><%= link_to pending_member.username, user_path %>
<%= link_to "Accept", "/cliqs/:cliq_id/cliq_requests/:id/", :method => :put %>
<%= link_to "Deny", "/cliqs/:cliq_id/cliq_requests/:id/", :method => :delete %>
</ul>
<% end %>
<% end %>
My Routes:
resources :cliqs do
resources :cliq_requests
end
These lines appear malformed:
<%= link_to 'Request to join Cliq', '/cliqs/:cliq_id/cliq_requests', :method => :post %>
<%= link_to "Accept", "/cliqs/:cliq_id/cliq_requests/:id/", :method => :put %>
<%= link_to "Deny", "/cliqs/:cliq_id/cliq_requests/:id/", :method => :delete %>
I recommend you use path helpers [e.g. cliq_cliq_request_path(cliq, cliq_request) if you are using resourceful routing]. You can use rake routes for help. If you are seeing things like :cliq_id and and :id in your development.log or test.log as part of the URLs that are hit, those should instead be numbers. You can also interpolate the strings yourself (e.g. "/cliqs/#{cliq_id}/cliq_requests/#{cliq_request.id}") but this is usually more typing and certainly more fragile over time.
One of your problems may be that you are looping through a list of pending member names, which doesn't have all the data you need to form the link correctly. So your update action may be working fine, but you may not be passing it the right data.
Also this line:
if #current_user = #cliq.owner
is an assignment, and so will always return true. Presumably you mean ==

Rails survey style application - Show all answers on option

I'm a new guy to ruby on rails and working on my first in-depth application. It has four tables: Questions, Options, Answers and Users. There's a list of questions and a user can vote for a unique option (stored in the Answers join table), I'm trying to get my head around table associations.
This is how I've setup my individual RB files:
class Question < ActiveRecord::Base
has_many :options
has_many :answers, :through => :options
end
class Option < ActiveRecord::Base
belongs_to :question
has_many :answers
end
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question
belongs_to :option
end
class User < ActiveRecord::Base
has_many :answers
has_many :questions, :through => :answers
end
My questions controller is setup like this to include the options table:
#questions = Question.includes(:options).all
and the table body in my index.html.erb file:
<tbody>
<% #questions.each do |question| %>
<tr class="<%= cycle('lineOdd', 'lineEven') %>">
<td><%= question.question_text %></td>
<td><%= link_to 'Show', question %></td>
<td><%= link_to 'Edit', edit_question_path(question) %></td>
<td><%= link_to 'Destroy', question, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% question.options.each do |option_text| %>
<tr class="backgroundColor1">
<td class="optionCell"> <%= option_text.option_text %> </td>
</tr>
<% end %>
<% end %>
</tbody>
In the Question class I've used 'has_many :answers, :through => :options' - is this the correct way to go about this and how would I output the total number of votes in a table row below the associated option.
Do I need to add to or change the question controller code?
This is my first post, sorry if I'm not informative enough!
Thanks
Lets start by fixing up the relations a bit:
class Question < ActiveRecord::Base
has_many :options
has_many :answers
has_many :users, through: :answers
end
There is nothing technically wrong with has_many :answers, :through => :options but since there is a direct relation through answers.question_id we don't need to go through the options table for the relation.
Displaying the count
If we simply did:
<td class="optionCell"><%= option.answers.count %></td>
This would create a nasty n+1 query to fetch the count of the answers for each option. So what we want to do is create a counter cache which stores a tally on the options table.
Lets start by creating a migration to add the column:
rails g migration AddAnswerCounterCacheToOptions answers_count:integer
rake db:migrate
Then we tell ActiveRecord to update the tally when we create associated records, this looks a bit strange since the counter_cache: true declaration is on the belongs_to side while the column is on the other but thats just how AR works.
class Option < ActiveRecord::Base
belongs_to :question
has_many :answers
end
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question
belongs_to :option, counter_cache: true
end
There is a little snag here. Since we may already have records we need to make sure they have correct counters. You can do this from the console but in the long run it is a good idea to create a rake task.
Option.find_each { |option| Option.reset_counters(option.id, :answers) }
This might take a bit of time since it needs to pull each Option and update the count.
Now we can display the tally like so:
<% question.options.each do |option| %>
<tr class="backgroundColor1">
<td class="optionCell"><%= option.option_text %></td>
<td class="optionCell"><%= option.answers.size %></td>
</tr>
<% end %>
.size is smart enough to use our counter cache column, but will fall back to querying the count which is a good thing for tests.

Assign foreign key in ruby rails

I am new to rails and I am having a hard time assigning the foreign key user_id to user_profile.
When a user creates an account it is saved to the database, then they are taken to create a profile. The user can create the profile and the profile is saved to the database. I used to have
<tr>
<th><%= f.label(:user_id, "User ID") %></th>
<td><%= f.text_field(:user_id) %></td>
</tr>
assign
on the user profile form, and it worked, user.user_profile showed there was a relationship. but I don't want a user to have to/be able to choose the user_id they are creating a profile for. I can't figure out how to assign the user_id to the user_profile. in the controller for user_profiles I have
def new
#user = User.find(session[:user_id])
#user_profile = UserProfile.new({:user_id => #user.id})
end
and for the new page for user_profiles I have
<%= form_for(:user_profiles, :url => {:action => 'create', :user_id => #user.id}) do |f| %>
<%= render(:partial => "form", :locals => {:f => f}) %>
<% end %>
any ideas? Thank You
Assuming you are on rails 4, you just need to add into User.rb
has_many :user_profiles, dependent: :destroy
and into user_profile.rb
belongs_to :user
validates :user_id, presence: true
user_profiles_controller.rb
def create
#user_profile = current_user.user_profiles.build(user_profile_params)
....
end
private
def user_profile_params
params.require(:user_profile).permit(:param1, :param2)
end
form view
<%= form_for(#user_profile) do |f| %>
<%= render(:partial => "form", :locals => {:f => f}) %>
<% end %>

Destroy action deleting from wrong table

I have a users model that can have many holidays through a rich join table.. My destroy statement on my view is deleting from the holidays table and NOT the user_holidays table as it should.. see below:
class HolidaysController < ApplicationController
def destroy
#user = User.find(params[:user_id])
#user_holiday = #user.holidays.find(params[:id])
#user_holiday.destroy
redirect_to #user
end
end
Heres the view button:
<% #user.holidays.each do |hld| %>
<td><%= hld.name %></td>
<td><%= hld.date %></td>
<td>
<%= button_to('Destroy', user_holiday_path(#user, hld), :method => 'delete', :class => 'btn btn-large btn-primary') %>
Models:
class User < ActiveRecord::Base
has_many :user_holidays
has_many :holidays, :through => :user_holidays
class UserHoliday < ActiveRecord::Base
attr_accessible :holiday_id, :user_id
belongs_to :user
belongs_to :holiday
class Holiday < ActiveRecord::Base
attr_accessible :name, :date
has_many :user_holidays
has_many :users, :through => :user_holidays
Any ideas? Thanks!!!!
You should use #user.user_holidays instead of #user.holidays
#user_holiday = #user.user_holidays.where(holiday_id: params[:id])
(my bad didn't read you question properly)
#user = User.find(params[:user_id])
#user.holidays.delete(#user.holidays.find(params[:id]))

Select data from two tables and listing

I have following structure:
class User < ActiveRecord::Base
has_many :Hobbies, :dependent => :destroy
accepts_nested_attributes_for :hobbies, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
class Hobby < ActiveRecord::Base
belongs_to :User
end
In my Users_controller.rb
def index
#data = User.all(:joins => :hobbies)
end
In index.html.erb
<% for item in #data %>
<tr>
<td><%= item.id %></td> #from table Users
<td><%= item.hobby_name %></td> #from table Hobbies
</tr>
<% end %>
And this gives me an error undefined method `hobby_name' for # User:0x103cf7480>
I thought that I have that associations right, but this error makes confused... Can you help me someone, please, where could be a problem?
You must specify the relation, your object doesn't have an attribute called hobby_name, it has an association to multiple hobbies, and each hobby has an attribute called hobby_name
So:
<% item.hobbies.each do |h| %>
<%= h.hobby_name %>
<% end %>