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
Related
I made a search (filtering) form to filter my objects according to given values. There is a Company model and search will be according to its attributes. This is my index.html.erb:
<% provide(:title, 'All companies') %>
<h1>All companies</h1>
<%= form_tag companies_path, :method => 'get' do %>
<%= hidden_field_tag :direction, params[:direction] %>
<%= hidden_field_tag :sort, params[:sort] %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<table class="pretty" border="1" cellpadding="10">
<tr>
<th><%= sortable "name" %></th>
<th><%= sortable "city" %></th>
<th><%= sortable "country" %></th>
<th><%= sortable "street_address" %></th>
<th><%= sortable "sector" %></th>
<th><%= sortable "telephone" %></th>
<th><%= sortable "fax" %></th>
<th>DELETE</th>
</tr>
<% for company in #companies %>
<tr class="<%= cycle('oddrow', 'evenrow') -%>">
<td><%= link_to company.name, company %></td>
<td><%= company.city %></td>
<td><%= company.country %></td>
<td><%= company.street_address %></td>
<td><%= company.sector %></td>
<td><%= company.telephone %></td>
<td><%= company.fax %></td>
<td><% if current_user.admin? %>
|| <%= link_to "delete", company, method: :delete,
data: { confirm: "You sure?" } %>
<% end %></td>
</tr>
<% end %>
</table>
<%= will_paginate #companies %>
This is my companies_controller.rb
helper_method :sort_column, :sort_direction
def index
#companies = Company.search(params[:search]).order(sort_column + ' ' + sort_direction).paginate(:per_page => 10, :page => params[:page])
end
This is my model company.rb
class Company < ActiveRecord::Base
attr_accessible :city, :country, :fax, :name, :reseller, :sector, :street_address, :telephone, :id
has_many :users , dependent: :destroy
def name_for_form
"#{name}"
end
def self.search(search)
if search
q = "%#{search}"
where('name LIKE ? OR city LIKE ? OR country LIKE ? OR street_address LIKE ? OR telephone LIKE ? OR fax LIKE ? OR sector LIKE ?',
q,q,q,q,q,q,q)
else
scoped
end
end
validates :city, presence: true
validates :country, presence: true
validates :fax, presence: true
validates :name, presence: true
validates :sector, presence: true
validates :street_address, presence: true
validates :telephone, presence: true
end
Lets assume I have 3 companies named kalahari, kalahari 2, and kalahari2. When I search kalahari, it founds only 1 company, kalahari. I mean it can't find kalahari in kalahari 2, or kalahari2. Only founds exact matches. When I search kala, it founds nothing. How can I fix that most simply? I am new to rails and don't want to mess a lot of things.
The simplest change that will get what you want is adding a wildcard to the end of your search query:
q = "%#{search}%"
The % matches anything when used with LIKE, so your code as currently written will match anything that ends with your input (so it would match a query of 'foo' to 'afoo', 'b_foo', and '1 3 5 x foo'), but without a matching wildcard on the end, it will not match things that contain the query but don't end with it (so 'foo' will not match 'foobar' or 'afoox').
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.
Again I'm faced with something that should not on the face of it be this difficult but has been driving me mad for an hour or so. I have multiple models which I want to pull into one 'index' view. I assume it's a join but I can't seem to find a way to do it.
What I have in my index view:
<% #tips.each do |tip| %>
<tr>
<td><%= tip.user_id %></td>
<td><%= tip.city_id # here I want to draw on the cities table to show city.name
%></td>
<td><%= tip.type_id # here I want to draw on the type table to show type.name
%></td>
<td><%= tip.place_id # here I want to draw on the place table to show place.name
%></td>
<td><%= tip.tip_desc %></td>
<td><%= link_to 'Show', tip %></td>
<td><%= link_to 'Edit', edit_tip_path(tip) %></td>
<td><%= link_to 'Destroy', tip, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
Here are the models:
class Tip < ActiveRecord::Base
belongs_to :user
belongs_to :city
belongs_to :place
end
class Place < ActiveRecord::Base
belongs_to :city
has_and_belongs_to_many :collections
has_many :tips
end
class City < ActiveRecord::Base
has_many :places
has_many :tips
end
Any help will be really appreciated!
Thanks in advance,
James
Suggestions from other guys should work. I'm not sure, but it seems like you don't have objects within specified IDs in your database. That's all :)
Example:
#tips.each do |tip|
tip.city_id # 1
City.find( tip.city_id ) # nil
end
I'm creating an app to log when files are moved to an archive drive, where they came from and were moved to and what user moved them.
This is my Archive model:
class Archive < ActiveRecord::Base
attr_accessible :archive_date, :start_dir, :end_dir, :user_id
#added by chris
belongs_to :user
end
This is my User model:
class User < ActiveRecord::Base
# new columns need to be added here to be writable through mass assignment
attr_accessible :username, :email, :password, :password_confirmation, :user_id
attr_accessor :password
before_save :prepare_password
before_save :assign_user_id
#added by chris
has_many :archives
def assign_user_id
self.user_id = User.maximum('user_id') + 1
end
end
This is my view:
<table id="archive_table">
<tr>
<th>Archive Date</th>
<th>Source Directory</th>
<th>Archive Directory</th>
<th>Archived By ID</th>
<th>Archived By Name</th>
</tr>
<% for archive in #archives %>
<tr>
<td><%= archive.archive_date %></td>
<td><%= archive.start_dir %></td>
<td><%= archive.end_dir %></td>
<td><%= archive.user_id %></td>
<td><%= archive.username %></td>
<td><%= link_to "Show", archive %></td>
<td><%= link_to "Edit", edit_archive_path(archive) %></td>
<td><%= link_to "Destroy", archive, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<p><%= link_to "New Archive", new_archive_path %></p>
When I start up my local rails server via rails s Try to load the page. This is what I get:
NoMethodError in Archives#index
Showing c:/appname/app/views/archives/index.html.erb where line #21 raised:
undefined method `username' for #<Archive:0x203d750>
Extracted source (around line #21):
18: <td><%= archive.start_dir %></td>
19: <td><%= archive.end_dir %></td>
20: <td><%= archive.user_id %></td>
21: <td><%= archive.username %></td>
22: <td><%= link_to "Show", archive %></td>
23: <td><%= link_to "Edit", edit_archive_path(archive) %></td>
24: <td><%= link_to "Destroy", archive, :confirm => 'Are you sure?', :method => :delete %></td>
Rails.root: c:
Application Trace | Framework Trace | Full Trace
app/views/archives/index.html.erb:21:in `block in _app_views_archives_index_html_erb__238412483_16592988'
app/views/archives/index.html.erb:15:in `each'
app/views/archives/index.html.erb:15:in `_app_views_archives_index_html_erb__238412483_16592988'
Assuming the archive record is correctly saved with the users User_ID how do i get my view to display the name of the user via the has_many belongs_to association?
Thank You for looking at my question.
Archives have users, users have usernames--I think you're just up one level to "high"; use:
<%= archive.user.username %>
Greetings
I have 3 db tables:
Types
id
name
Sources
id
name
type_id
Operations
id
source_id
comment
...
and models for each:
class Type < ActiveRecord::Base
has_many :sources, :dependent => :destroy
end
class Source < ActiveRecord::Base
belongs_to :type
has_many :operations, :dependent => :destroy
end
class Operation < ActiveRecord::Base
belongs_to :source
default_scope :order => 'created_at DESC'
end
In Operation#index controller i have code for getting data (generated by scaffolding)
#operations = Operation.all
And piece of view operations/index.html.erb also generated by scaffolding
<% #operations.each do |operation| %>
<tr>
<td><%= operation.source_id %></td>
<td><%= operation.comment %></td>
</tr>
<% end %>
Now I want to use source.name instead of *operation.source_id*
I tried to do:
-replace operation.source_id to operation.sources.name # doesn't work
-tried to using :joins, and can't get Sources table fields
irb(main):057:0> Operation.first( :joins => :source )
=> #<Operation id: 2088, source_id: 1, summ: 10.0, comment: "", created_at: "2011-01-01 07:39:45", updated_at: nil>
or
irb(main):063:0> Operation.first( :joins => 'INNER JOIN sources ON operations.source_id = sources.id' )
=> #<Operation id: 2088, source_id: 1, summ: 10.0, comment: "", created_at: "2011-01-01 07:39:45", updated_at: nil>
How I must properly use :joins for get additional field?
Or there is another approach to get combined tables data.
And why in the operations/show.html.erb i can use <%= #operation.source.name %> and successfully get source.name, but in the *operations/index.html.er*b can't
<% #operations.each do |operation| %>
<tr>
<td><%= operation.source.name %></td>
<td><%= operation.comment %></td>
</tr>
<% end %>
I'd also recommend changing your #index method to use an includes statement to avoid an N+1 situation (i.e. running a separate database query for each individual operation's source).
#operations = Operation.includes(:source).all
Solved:
opearions#index
#operations = Operation.all :joins => :source, :select => '*'
operations/index.html.erb
<% #operations.each do |operation| %>
<tr>
<td><%= operation.name %></td>
...