ActiveRecord sort_by model's association - ruby-on-rails

I want to use a where clause to retrieve all the correct ItemAttributes, then sort by the column order in Static.
Relationships:
class ItemAttribute < ActiveRecord::Base
belongs_to :static, primary_key:"name", foreign_key:"name"
end
class Static < ActiveRecord::Base
has_many :item_attributes, :foreign_key => 'name', :primary_key => 'name'
end
Code that doesn't quite do it...
#items = ItemAttribute.where(level:5)
#sorted = #items.sort_by(&:static.order)

I like using scopes and keeping specifics of each class in it's class, for something like this I could add a scope to Static model,
class Static < ActiveRecord::Base
scope :sort_by_order, -> { order(order: :desc) } # or asc if you want
end
Then use that scope in the query
#sorted_items = ItemAttribute.joins(:static).where(level: 5).merge(Static.sort_by_order)

#sorted_items = ItemAttribute.include(:static).where(level:5).order('statics.order')

Related

How define named scoped with order on associations

I have two models.
class Shoe < ActiveRecord::Base
has_many :sizes, :dependent => :destroy
scope :brand_asc , order('brand ASC')
scope :brand_desc , order('brand DESC')
attr_accessible :brand ,:model
end
class Size < ActiveRecord::Base
belongs_to :shoe
scope :size_brand_asc , order("#{self.shoe.brand} ASC")
scope :size_brand_desc , order("#{self.shoe.brand} DESC")
end
It is very easy to call named scope on Shoe model like below.
#shoes = Shoe.brand_asc
But i want to sort sizes on the base of "brand" that is attribute of shoe model.so this
is not working for me like below.
#sizes = Size.size_brand_asc # giving error
How i can sort sizes on base of shoe brand
You can achieve this by doing like the following:
class Size < ActiveRecord::Base
belongs_to :shoe
scope :brand_ordered, lambda do |way = 'ASC'|
includes(:shoe).order("shoes.brand #{way}")
end
Usage:
#sizes = Size.brand_ordered('DESC')
# or
#sizes = Size.brand_ordered('ASC')
# equivalent:
#sizes = Size.brand_ordered
In the Shoe class:
class Shoe < ActiveRecord::Base
scope :brand_ordered, lambda do |way = 'ASC'|
order("#{self.table_name}.brand #{way}")
end
one line answer
scope :size_brand_asc, ->{joins(:shoe) & Shoe.brand_asc}

How to apply conditions when accessing records using a has_many through relationship in Rails?

I have the following models:
class Campaign < ActiveRecord::Base
has_many :campaign_keywords
has_many :leads, :through => :campaign_keywords
end
class CampaignKeyword < ActiveRecord::Base
belongs_to :campaign
has_many :leads
end
class Lead < ActiveRecord::Base
belongs_to :campaign_keyword
end
I am trying to build a function in the "Campaign" model that will only return leads which belong to a given campaign_keyword.
My attempt is:
def leads?(campaign_keyword_id = -1)
self.leads :conditions => ['campaign_keyword_id = #{campaign_keyword_id}']
end
but this does not work, the conditions are ignored.
Can you see a solution to this?
Create a named_scope for your Lead model, like so:
class Lead < ActiveRecord::Base
belongs_to :campaign_keyword
named_scope :with_keyword, lambda { |keyword| { :conditions => { :campaign_keyword => keyword } } }
end
Now, when you want to get leads for a particular campaign keyword, you would do so like this:
def leads_for_campaign(keyword)
self.leads.with_keyword(keyword)
end
This is much nicer and more re-usable, because the Lead model itself now knows how to find leads for a specific campaign.
For more of an idea of what you can do with named_scopes, check out http://apidock.com/rails/ActiveRecord/NamedScope/ClassMethods/named_scope
Try this:
def leads?(campaign_keyword_id = -1)
self.leads.all :conditions => ['campaign_keyword_id = #{campaign_keyword_id}']
end
I would rewrite your query as follows:
def leads?(campaign_keyword_id = -1)
self.leads.all :conditions => ['campaign_keyword_id = ?', campaign_keyword_id]
end
OR
self.leads.find_all_by_compaign_keyword_id(campaign_keyword_id)

Model Relationship Problem

I am trying to calculate the average (mean) rating for all entries within a category based on the following model associations ...
class Entry < ActiveRecord::Base
acts_as_rateable
belongs_to :category
...
end
class Category < ActiveRecord::Base
has_many :entry
...
end
class Rating < ActiveRecord::Base
belongs_to :rateable, :polymorphic => true
...
end
The rating model is handled by the acts as rateable plugin, so the rateable model looks like this ...
module Rateable #:nodoc:
...
module ClassMethods
def acts_as_rateable
has_many :ratings, :as => :rateable, :dependent => :destroy
...
end
end
...
end
How can I perform the average calculation? Can this be accomplished through the rails model associations or do I have to resort to a SQL query?
The average method is probably what you're looking for. Here's how to use it in your situation:
#category.entries.average('ratings.rating', :joins => :ratings)
Could you use a named_scope or custom method on the model. Either way it would still require some SQL since, if I understand the question, your are calculating a value.
In a traditional database application this would be a view on the data tables.
So in this context you might do something like... (note not tested or sure it is 100% complete)
class Category
has_many :entry do
def avg_rating()
#entries = find :all
#entres.each do |en|
#value += en.rating
end
return #value / entries.count
end
end
Edit - Check out EmFi's revised answer.
I make no promises but try this
class Category
def average_rating
Rating.average :rating,
:conditions => [ "type = ? AND entries.category_id = ?", "Entry", id ],
:join => "JOIN entries ON rateable_id = entries.id"
end
end

Rails: order using a has_many/belongs_to relationship

I was wondering if it was possible to use the find method to order the results based on a class's has_many relationship with another class. e.g.
# has the columns id, name
class Dog < ActiveRecord::Base
has_many :dog_tags
end
# has the columns id, color, dog_id
class DogTags < ActiveRecord::Base
belongs_to :dog
end
and I would like to do something like this:
#result = DogTag.find(:all, :order => dog.name)
thank you.
In Rails 4 it should be done this way:
#result = DogTag.joins(:dog).order('dogs.name')
or with scope:
class DogTags < ActiveRecord::Base
belongs_to :dog
scope :ordered_by_dog_name, -> { joins(:dog).order('dogs.name') }
end
#result = DogTags.ordered_by_dog_name
The second is easier to mock in tests as controller doesn't have to know about model details.
You need to join the related table to the request.
#result = DogTag.find(:all, :joins => :dog, :order => 'dogs.name')
Note that dogs is plural in the :order statement.

Is there a way of doing filtering joined associations using named scope?

I have the following associated models
class Enrollment < ActiveRecord::Base
has_many :addresses
end
class Address < ActiveRecord::Base
belongs_to :address_type
end
Currently I'm using the following (which I think is ugly) to filter out enrollment addresses of a certain address type.
class Enrollment < ActiveRecord::Base
def local_address
adds = []
addresses.each do |add|
adds << add if add.address_type.name == 'Local'
end
adds.last
end
end
Is there a way of using named scope of doing the same thing?
A generic solution:
class Address < ActiveRecord::Base
belongs_to :address_type
named_scope :local, { :conditions => { :address_type => { :name => "Local" }}}
end
This allows you to do the following:
Enrollment.find(12).addresses.local # Association extended with .local method
Address.local.all # Class methods extended with .local method
The named scope could help in all situations where you are only using "local" addresses.
With reference from the following stackoverflow post, I managed to solved my named scope query
Rails named_scopes with joins
Basically I need to do joins in the query
class Address < ActiveRecord::Base
belongs_to :address_type
named_scope :local, {
:joins => "INNER JOIN address_types ON address_types.id = addresses.address_type_id",
:conditions => "address_types.name = 'Local'"
}
end
So effectively I can rewrite my Enrollment's "local_address" method to
clss Enrollment < ActiveRecord::Base
def local_address
addresses.local.last
end
end

Resources