I have an AR query using 'will_paginate' that looks like this:
paginate :all,
:page => criteria[:page],
:per_page => criteria[:per_page],
:include => { :user, :person },
:conditions => [conditions , criteria[:from_date], criteria[:to_date], criteria[:patient_id],criteria[:user_id]].concat(criteria[:actions]).concat(criteria[:types]).concat(criteria[:users]).concat(criteria[:statuses]).concat(criteria[:priorities]).compact,
:order => criteria[:order]
I get an error in the order clause:
Unknown column 'user.person.last_name' in 'order clause'
I am trying to order by a person's last name. As you can see I have included user and person in a nested include. User belongs to person with this statement:
belongs_to :person, :class_name => 'Party', :foreign_key => 'person_id', :with_disabled => true
Person is a subclass of Party:
class Person < Party
Party has a last_name field
The order by should be table_name.column, something like people.last_name
Related
I have (in Rails 3.2.13):
class User < ActiveRecord::Base
has_many :app_event_login_logouts
end
class AppEventLoginLogout < ActiveRecord::Base
belongs_to :user
end
and would like to get back something like:
AppEventLoginLogoug.select("id, type, users.email").joins(:user)
basically id, type from app_event_login_logouts and email from users but this doesn't seem to be working. What would be the correct syntax?
Try out following code:
ret = User.joins(:app_event_login_logouts).select('app_event_login_logouts.id, app_event_login_logouts.type, users.email')
ret.first.id # will return app_event_login_logouts.id
ret.first.email # will return users.email
...
AppEventLoginLogoug.find(:all,
{:include => [:users],
:select => ['id', 'type', 'users.email']})
I also checked the apidoc and found something like that:
result= AppEventLoginLogoug.find(:all,
:conditions => ['condition_here'],
:joins => [:users],
:select => 'whatever_to_select'
:order => 'your.order')
A CertProgramItem has_many :cert_schedules.
A CertSchedule belongs_to :reg_fee_item, :foreign_key => 'reg_fee_item_id', :class_name => 'Item'
Starting with the CertProgramItem, I want to get all CertSchedules and their related tables in one query (to avoid the n+1 problem). My first query was:
cpi_arr = CertProgramItem.find(:all, :include => :cert_schedules, :order => :id)
However, this didn't fetch the members of the Item class which belong to the collection of CertSchedules.
I have modified the query:
cpi_arr = CertProgramItem.find(:all, :include => {:cert_schedules => :items}, :order => :id)
and
cpi_arr = CertProgramItem.find(:all, :include => {:cert_schedules => :reg_fee_items}, :order => :id)
but I get errors like ActiveRecord::ConfigurationError: Association named 'items' was not found; perhaps you misspelled it?" or ActiveRecord::ConfigurationError: Association named 'reg_fee_items' was not found; perhaps you misspelled it? for the 2nd.
Is there a way to get this nested, foreign-key association in one query?
Here's some more detailed information on the CertSchedule assocciations:
class CertSchedule < ActiveRecord::Base
belongs_to :cert_program_item
belongs_to :reg_fee_item, :foreign_key => 'reg_fee_item_id', :class_name => 'Item'
belongs_to :start_term, :class_name => 'SchoolTerm', :foreign_key => 'start_term_id'
My latest version of the query looks like this:
cpi_arr = CertProgramItem.find(:all, :include => [:cert_tier, {:cert_schedules => [:reg_fee_item,:start_term] }])
This query is now successfully returning what I expected. Lessons learned:
Use the foreign key name from the model, not the actual table name.
Multiple items in an association need to be surrounded with square brackets [].
I don't know why I can't figure this out, I think it should be fairly simple. I have two models (see below). I'm trying to come up with a named scope for SupplierCategory that would find all SupplierCategory(s) (including :suppliers) who's associated Supplier(s) are not empty.
I tried a straight up join, named_scope :with_suppliers, :joins => :suppliers which gives me only categories with suppliers, but it gives me each category listed separately, so if a category has 2 suppliers, i get the category twice in the returned array:
Currently I'm using:
named_scope :with_suppliers, :include => :suppliers
and then in my view I'm using:
<%= render :partial => 'category', :collection => #categories.find_all{|c| !c.suppliers.empty? } %>
Not exactly eloquent but illustrates what I'm trying to achieve.
Class Definitions
class SupplierCategory < AR
has_many :suppliers, :order => "name"
end
class Supplier < AR
belongs_to :supplier
end
Here is one more approach:
named_scope :with_suppliers, :include => :suppliers,
:conditions => "suppliers.id IS NOT NULL"
This works because Rails uses OUTER JOIN for include clause. When no matching rows are found the query returns NULL values for supplier columns. Hence NOT NULL check returns the matching rows.
Rails 4
scope :with_suppliers, { includes(:steps).where("steps.id IS NOT NULL") }
Or using a static method:
def self.with_suppliers
includes(:steps).where("steps.id IS NOT NULL")
end
Note:
This solution eager loads suppliers.
categories = SupplierCategory.with_suppliers
categories.first.suppliers #loaded from memory
class SupplierCategory < AR
has_many :supliers
def self.with_supliers
self.all.reject{ |c| c.supliers.empty? }
end
end
SupplierCategory.with_supliers
#=> Array of SuplierCategories with supliers
Another way more flexible using named_scope
class SupplierCategory < AR
has_many :supliers
named_scope :with_supliers, :joins => :supliers, :select => 'distinct(suplier_categories.id), suplier_categories.*', :having => "count(supliers.id) > 0"
end
SupplierCategory.with_supliers(:all, :limit => 4)
#=> first 4 SupplierCategories with suppliers
Simpler version:
named_scope :with_suppliers, :joins => :suppliers, :group => :id
If you want to use it frequently, consider using counter_cache.
I believe it would be something like
#model SupplierCategory
named_scope :with_suppliers,
:joins => :suppliers,
:select => "distinct(supplier_categories), supplier_categories.*",
:conditions => "suppliers.supplier_categories_id = supplier_categories.id"
Let me know if it works for you.
Edit:
Using fl00r's idea:
named_scope :with_suppliers,
:joins => :suppliers,
:select => "distinct(supplier_categories), supplier_categories.*",
:having => "count(supliers.id) > 0"
I believe this is the faster way.
I have a sentence and correction model
class Sentence < ActiveRecord::Base
has_one :correction
class Correction < ActiveRecord::Base
belongs_to :sentence
and I'm trying find all sentences which don't have a correction. To do this I'm simply looking for corrections which don't exist i.e. whose id = nil. But it is failing and i can't figure out why
Sentence.find :all, :include => :correction, :conditions => {:correction => {:id => nil}}
from (irb):4>> Sentence.find :all, :include => :correction, :conditions => {:correction => {:id => nil}}
ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'correction.sentence_id' in 'where clause': SELECT * FROM sentences WHERE (correction.sentence_id IS NULL)
Perhaps its the syntax or maybe just the overall approach. Can anyone help?
You can use this:
Sentence.all(:include => :correction,
:conditions => "corrections.sentence_id IS NULL")
Is there a shortcut for giving a limit and order when accessing a has_many relation in an ActiveRecord model?
For example, here's what I'd like to express:
#user.posts(:limit => 5, :order => "title")
As opposed to the longer version:
Post.find(:all, :limit => 5, :order => "title", :conditions => ['user_id = ?', #user.id])
I know you can specify it directly in the has_many relationship, but is there a way to do it on the fly, such as showing 10 posts on one page, but only 3 on another?
I have something similar in a blog model:
has_many :posts, :class_name => "BlogPost", :foreign_key => "owner_id",
:order => "items.published_at desc", :include => [:creator] do
def recent(limit=3)
find(:all, :limit => limit, :order => "items.published_at desc")
end
end
Usage:
Blog.posts.recent
or
Blog.posts.recent(5)
You can do it using a named scope on the post model:
class Post < ActiveRecord::Base
named_scope :limited, lambda {|*num| {:limit => num.empty? ? DEFAULT_LIMIT : num.first}}
end
This is essentially similar to utility_scopes, as reported by #Milan, except you do it piece meal, only where you need to do it.
You can use Ryan Daigle's utility_scopes. After installation (it's a gem) you will get some new useful scopes such as:
#user.posts.ordered('title ASC').limited(5)
You can even set default order and limit for any model.