I have what I assume is a pretty simple query, which is essentially collecting our Customer object and counting the orders associated with that customer. But every time I try to load more than a weeks worth of Customer data, I get 504 timeouts, I assume from the system taking to long to collect everything. We use Spree as our eComm framework, which handles the Product & Order.
What am I missing that's making this collection of objects so labor intensive on our servers? It's a good 45 seconds before our server kicks us off with the 504.
customer.rb (model):
has_many :orders, foreign_key: :customer_id, class_name: "Spree::Order"
def sorted_orders
orders.sort_by(&:completed_at)
end
def non_sub_orders
orders.where(purchased_via_subscription: false)
end
def standard_items
orders.joins(:products).merge(Spree::Product.where(is_default: true))
end
def spec_items
orders.joins(:products).merge(Spree::Product.where(is_default: false))
end
customers_controller.rb:
#customers = Customer.joins(:orders).where(spree_orders: {purchased_via_subscription: false, completed_at: #from...#to}).uniq
customers/index.html.erb (view):
<form class="filter_form col-10">
Dates: <%= text_field(:date, :from, type: 'date', value: params[:date].try(:[], :from), style: "") %>
to <%= text_field(:date, :to, type: 'date', value: params[:date].try(:[], :to), style: "") %>
<input type="submit" value="Filter" />
</form>
<% #customers.each_with_index do |c, index| %>
<tbody id="tbodyid">
<tr>
<td><%= index + 1 %></td>
<td><%= c.email %></td>
<td><%= c.sorted_orders.first.completed_at.strftime("%D") %></td>
<td><%= c.sorted_orders.last.completed_at.strftime("%D") %></td>
<td><%= c.spec_items.count %></td>
<td><%= c.standard_items.count %></td>
<td><%= c.non_sub_orders.count %></td>
<td><%= number_to_currency(c.total_revenue/c.non_sub_orders.count) %></td>
<td><%= number_to_currency(c.total_revenue) %></td>
</tr>
</tbody>
<% end %>
this is not only N + 1 queries problem but also custom queries (which #includes will not fix).
solution (maybe): create custom associations for custom queries and preload them.
# customer.rb
has_many :orders, foreign_key: :customer_id, class_name: "Spree::Order"
has_many :sorted_orders, -> { sort_by(&:completed_at) }, class_name: "Spree::Order"
has_many :non_sub_orders, -> { where(purchased_via_subscription: false) }, class_name: "Spree::Order"
has_many :standard_items, -> { joins(:products).merge(Spree::Product.where(is_default: true)) }, class_name: "Spree::Order"
has_many :spec_items, -> { joins(:products).merge(Spree::Product.where(is_default: false)) }, class_name: "Spree::Order"
# customers_controller.rb
#customers = Customer.joins(:orders)
.where(spree_orders: {purchased_via_subscription: false, completed_at: #from...#to})
.includes(:sorted_orders, :non_sub_orders, :standard_items, :spec_items)
.uniq
I am trying to sort results based on an associated field. I found posts indicating that this could be done with includes.
I have an observation model that has
before_validation :parse_supervisor, :parse_employee
has_many :attachments, :dependent => :destroy
accepts_nested_attributes_for :attachments
belongs_to :user, foreign_key: "employee_id", class_name: "User"
belongs_to :user, foreign_key: "supervisor_id", class_name: "User"
belongs_to :department
I have a user.rb that includes the following. User.rb is also used with adauth for active directory integration but I don't believe that is causing the problems.
class User < ActiveRecord::Base
has_many :subordinates, class_name: "User", foreign_key: "supervisor_id"
belongs_to :supervisor, class_name: "User"
has_many :observations
Part of my observations\index.rb is as follows. The user.
<% #observations.each do |observation| %>
<tr>
<td><%= observation.employee_id %></td>
<td><%= observation.user.first_name %></td>
<td><%= observation.user.last_name %></td>
<td><%= observation.user.id %></td>
<% d = observation.date %>
<td style="text-align: center;"><%= d.strftime("%m/%d/%y") %></td>
<td><%= observation.status %></td>
The observation.user.first_name and last_name works for display.
My observations controller has
def index
#observations = Observation.all
#users = User.all
get_ldap('*', '*')
#observations = Observation.includes([:users]).order('[user].last_name ASC')
I put user in brackets [] because it appears that user is a reserved word in sql.
The error I am getting is
ActiveRecord::StatementInvalid in Observations#index
Showing C:/Users/cmendla/RubymineProjects/employee_observations/app/views/observations/index.html.erb where line #23 raised:
TinyTds::Error: The multi-part identifier "user.last_name" could not be bound.: EXEC sp_executesql N'SELECT [eo].[observations].* FROM [eo].[observations] ORDER BY [user].last_name ASC'
Rails.root: C:/Users/cmendla/RubymineProjects/employee_observations
Application Trace | Framework Trace | Full Trace
app/views/observations/index.html.erb:23:in `_app_views_observations_index_html_erb__594730326_70277244'
Request
Parameters:
None
My overall goal here is to be able to sort records from the observations table based on the last (and first) names of the user model . Note - I am using MS Sql Server for the database.
I think error is here change it like:
#observations = Observation.includes(:user).order('users.last_name ASC')
I have an app which allows users to add recipes and then select their favourite recipes to view in their member area, I can select a favourite and i am getting the user_id, recipe_id and giving it a favourite_id.
What I would like to do is output the actual recipe to my view (as a favourite), like dish name, country_of_origin etc. I can do this with an actual recipe but not the favourite.Do i need to use has_many_through for this?
My models look like this
User
has_many :recipes
has_many :favourites
recipe
belongs_to :user
has_many :ingredients
has_many :preperations
has_many :favourites
favourites
belongs_to :user
belongs_to :recipe
attr_accessible :user_id, :recipe_id
My controller
#favourites = current_user.favourites
my link_to post
<%= link_to "Add to favorites", {:controller => 'favourites', :action => 'create', :recipe_id => r.id}, {:method => :post } %>
i can list the current users recipes, this is my contoller for this
#recipes = current_user.recipes if current_user.recipes
and then output them to the view like so
<% #recipes.each do |r| %>
<tr>
<td><%= r.dish_name %></td>
<td><%= r.country_of_origin %></td>
<td><%= r.difficulty %></td>
<td><%= r.preperation_time %></td>
<td><%= ingredient_names(r.ingredients) %></td>
<td><%= preperation_steps(r.preperations) %></td>
<td><%= image_tag r.avatar.url(:thumb)%></td>
<tr>
So if i do this in the view
<li><%= #favourites %></li>
I get this outputted
<Favourite id: 16, user_id: 8, recipe_id: 21, created_at: "2012-11-07 20:24:39", updated_at: "2012-11-07 20:24:39">]
If i try
<%= #favourites.dish_name %>
then i get the error undefined method dish_name
How do i get the params of the recipe model to show in the view. I do apologise just cant work it out, which should be really easy i guess?
Any help appreciated
You get undefined dish_name because your Favourite model does not have such attribute.
#favourites = current_user.favourites returns an array of Favourite objects.
In your view you would do something like
<% #favourites.each do |f| %>
<%= f.recipe %>
<% end %>
That will display all the recipes your current_user favorites. You could narrow it down to display a specific recipe or what not.
The favorites is just holding the relationship.
You can access the recipe by
#favourites.recipe
which will then be a recipe object and contain your attributes like dish_name etc.
#favourites is an array, yes? Then you must loop through #favourites and work with each element individually as so:
<% #favourites.each do |favorite| %>
<%= favorite.recipe.dish_name %>
<% end %>
I have a simple app where you can upload recipes
I have just integrated the gem terrarum to give me all the countries of the world within my country model. I have a relationship where a recipe has_one country and a country belongs to recipe. I am using nested forms from ryan bates and have had no problem getting information to show from my ingredients model and preparation model. But i cannot get the country name to save in the table or show in the view (though this is caused by not saving to model)
Code is as follows
Form
<%= f.label :country_id, "Country Of Origin" %>
<%= f.collection_select(:country_id, Country.all, :id, :name, :prompt => 'Please select country') %>
View
<% #recipes.each do |r| %>
<tr>
<td><%= r.dish_name %></td>
<td><%= r.country.name %></td>
<td><%= r.difficulty %></td>
<td><%= r.preperation_time %></td>
<td><%= ingredient_names(r.ingredients) %></td>
<td><%= preperation_steps(r.preperations) %></td>
<td><%= image_tag r.avatar.url(:thumb)%></td>
</tr>
Helper
def preperation_steps(preperations)
if preperations
preperation_array = preperations.map {|pre| pre.prep_steps}
preperation_array.join("\n")
end
end
def country_name(country)
if country
country_array = country.map {|c| c.country_name}
country_array.join("\n")
end
end
end
I have included my preparation helper as this works, so surely my country_name helper mirrors this? or do i not need to put a helper in for this?
recipe controller
def new
#recipes = current_user.recipes if current_user.recipes #show recipes if the user has any recipes
#favourites = current_user.favourites
end
recipe model
belongs_to :user
belongs_to :country
has_many :ingredients
has_many :preperations
has_many :favourites
attr_accessible :dish_name, :difficulty, :preperation_time, :ingredients_attributes, :preperations_attributes, :country_id, :avatar:preperations_attributes, :country_id, :avatar
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
accepts_nested_attributes_for :ingredients, :preperations
scope :top_countries, order("country_of_origin DESC")
if anyone can help it would be much appreciated
Thanks
In this code I see two mistakes:
Recipe should belong_to :country. Don't use has_one here. The foreign key country_id should be in the recipes table.
#recipe.build_country is not necessary. You already have a list of countries. You should only use build_country if you are planning to add a new country to the list of countries, which you are not in this case.
Also, you don't need fields_for. You can just do:
<%= f.label :country_id, "Country Of Origin" %>
<%= f.collection_select(:country_id, Country.all, :id, :name, :prompt => 'Please select country') %>
I am working with a legacy database and am also reading through the Rails guides on associations.
I have two models. A diary model and an animal model.
diary.rb
class Diary < ActiveRecord::Base
establish_connection :premvet
attr_accessible :DiaryNo ,:DiaryName, :DiaryDate, :SlotHour, :TotalSlots, :BlockSlots, :BlockBooked, :FreeSlot, :BookedSlot
self.table_name = 'diary'
self.primary_key = 'DiaryNo'
has_many :animals, :foreign_key => 'DiaryQueue'
end
animal.rb
class Animal < ActiveRecord::Base
establish_connection :premvet
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'
belongs_to :diary, :foreign_key => 'DiaryNo'
end
animals index view
<% #animals_todaysappointments.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.NextApptDate.strftime("%d %b. %Y - %H:%M") %></td>
<td><%= animal.DiaryQueue %>
<td><%= animal.diary.DiaryName %></td>
<td><%= link_to 'Show', animal %></td>
</tr>
<% end %>
As you can see it's showing the DiaryQueue from the animal table using:
<td><%= animal.DiaryQueue %>
and I am trying to show the DiaryName from the diary table with:
<td><%= animal.diary.DiaryName %></td>
This fails with an exception error:
undefined method `DiaryName' for nil:NilClass
The #animals_todaysappointments method looks like this:
def appointments
#animals_todaysappointments = Animal.where('DATE(NextApptDate) = ?', Date.today).page(params[:page]).per_page(15).order('NextApptDate ASC')
respond_to do |format|
format.html # index.html.erb
end
end
If I add:
delegate :DiaryName, :to => :diary
to the animal model then I get the following error:
Animal#DiaryName delegated to diary.DiaryName, but diary is nil: #
Each animal record has a DiaryQueue value between 0 and 10 and the diary table has a number of rows, an example row is:
DiaryNo DiaryName DiaryDate SlotHour TotalSlots BlockBooked FreeSlot BookedSlot
--------------------------------------------------------------------------------------
1 Morning 2012-07-16 9 18 0 0 18
There are rows with the same DiaryNo, but they also have the same DiaryName. It's only the DiaryDate, SlotHour, TotalSlots, BlockBooked, FreeSlot and BookedSlot that alter in those rows.
To try and prevent the error I added the following to the view:
<% unless animal.DiaryName.nil? %>
<td><%= animal.DiaryName %></td>
<% else %>
<td><%= animal.DiaryQueue %>
<% end %>
but I get an exception:
Animal#DiaryName delegated to diary.DiaryName, but diary is nil: #
What am I doing wrong?
Given your answers above, you might need to change the belongs_to method in Animal to the following:
belongs_to :diary, :foreign_key => "DiaryQueue", :primary_key => "DiaryNo"
In your current implementation your models are going to try to match Animal#DiaryNo against Diary#DiaryNo, when what you've said you wanted is to match Animal#DiaryQueue against Diary#DiaryNo.