I have four models
class Votation
belongs_to :quorum
def calc
return self.quorum.calc
end
end
class Quorum
has_one :votation
...
end
class NewQuorum < Quorum
def calc
do stuff and call
self.votation.attribute_a
end
end
class OldQuorum < Quorum
def calc
do stuff and call
self.votation.attribute_b
end
end
I have loaded the votation into a #votation object and eager loaded the associated Quorum.
The problem is that when I call #votation.calc, ActiveRecord execute a SQL select to load the votation again when it has to execute self.votation.attribute_a for example.
SELECT "votations".* FROM "votations" WHERE "votations"."quorum_id" = $1
how can I avoid this select from being executed?
You can use the :inverse_of option when specifying the association to prevent another select statement from being executed.
So your models would look like:
class Votation
belongs_to :quorum, inverse_of: :votation
end
class Quorum
has_one :votation, inverse_of: :quorum
end
Reference: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/belongs_to
Related
I have a Lesson model which has many Completions like this:
class Lesson < ActiveRecord::Base
has_many :completions, as: :completable
belongs_to :course
end
And each Completion belongs to a User as well:
class Completion < ActiveRecord::Base
belongs_to :user
belongs_to :completable, polymorphic: true
end
From my application perspective I'm only interested in the amount of completions for a certain lesson, so I've included a counter cache. In regard to the individual Completions, I'm only interested if the Lesson is completed by the current user (I'm using Devise).
Is there some way to create a dynamic has_one relationship of some kind, that uses the information from the current_user to query the Completion table?
for instance:
has_one :completion do
def from_user current_user
Completion.where(completable: self, user: current_user)
end
end
Although this could work, I'm also having a polymorphic relationship. Rails is complaining that there's no foreign key called lesson_id. When I add a foreign_key: symbol, the do-end block stops working.
Any ideas?
Why not passing both block and options to has_many?
class Lesson < ActiveRecord::Base
has_many :completions, as: :completable do
def from_user user
if loaded?
find {|c| c.user_id = user.id}
else
find(user_id: user.id)
end
end
end
belongs_to :course
end
#lesson = Lesson.last
# Association not loaded - executing sql query
#lesson.completions.from_user(current_user)
#lesson.completions
# Association loaded - no sql query
#lesson.completions.from_user(current_user)
NOTE: You cannot treat it as an association, so it cannot be preloaded on its own.
I have the following models in my Ruby on Rails app :
class Invoice < ActiveRecord::Base
has_many :allocations
has_many :payments, :through => :allocations
end
class Allocation < ActiveRecord::Base
belongs_to :invoice
belongs_to :payment
end
class Payment < ActiveRecord::Base
has_many :allocations, :dependent => :destroy
has_many :invoices, :through => :allocations
end
My problem is that in the Allocation class I would like to use the total_amount of all associated invoices, ideally in a before_save callback.
This isn't possible right now, however, because at the time an allocation object gets saved it is only associated with one particular invoice object.
How can this be done with a minimum of database queries?
Invoice.where(asdfasdfasdf).map(&:allocations).flatten.map(&:total_amount).compact.inject(:+)
Because this is rails the database call is nothing. To sum up an array of numbers you can use this:
ary = [0,12,2,6,nil]
ary.compact.inject(:+)
#=> 20
You could clean this up a bit:
class Invoice
#...
def total_amount
allocations.map(&:total_amount).inject(:+) #throws error if there is 1 nil 'total_amount data' value
end
def self.sum_allocation_amounts(sql)
where(sql).map(&:total_amount).inject(:+)
end
end
Its not possible to call self.map(&:allocations) inside of an Invoice class method without errors so I'm passing in the some basic sql as a workaround. Ideally I'd make it possible to directly call this method on a daisy chain of activerecord where calls for Invoice but that's not working out for me right now ("undefined method map' for Class")
Invoice.sum_allocation_amounts("democol = 'demo params'")
I have a tree-like relationship model with a fixed depth, and each level has a code attribute - similar to this;
class Category < ActiveRecord::Base
has_many :sub_categories
default_scope order(:code)
end
class SubCategory < ActiveRecord::Base
belongs_to :category
has_many :items
def self.sorted
self.joins(:category).order('"categories".code ASC, "sub_categories".code')
end
end
class Item < ActiveRecord::Base
belongs_to :sub_category
def self.sorted
# what goes here?
end
end
Category.all gets all the the categories ordered by categories.code.
SubCategory.sorted gets all the sub_categories ordered by categories.code, sub_categories.code. I used this approach because default_scope : joins(:categories).order('categories.code, sub_categories.code') makes .find return read-only records.
I would like to call Items.sorted and get the all items ordered by categories.code, sub_categories.code, items.code but I can't figure out how. I imagine I need a second .joins, but I don't have a relationship name to supply.
Try this:
class Item < ActiveRecord::Base
belongs_to :sub_category
def self.sorted
# do not need self here as that is implied
joins(sub_category: :category).
order('"categories".code ASC, "sub_categories".code, "items".code')
end
end
See the docs for joining nested assoications here
This works, but it seems like there should be a better way;
def self.sorted
joins(:sub_category).
joins('INNER JOIN "categories" on "categories".id = "sub_categories".category_id').
order('"categories".code ASC, "sub_categories".code ASC, "items".number ASC')
end
This is my scenario:
class User < ActiveRecord::Base
has_many :things
# attr_accessible :average_rating
end
class Thing < ActiveRecord::Base
belongs_to :user
has_one :thing_rating
end
class ThingRating < ActiveRecord::Base
belongs_to :thing
attr_accessible :rating
end
I want to have an attribute in my User model which has the average calculation of his related ThingsRating.
What would be the best practice to manage this?
Thanks
May be you can use relation not sure but you can try this
class User < ActiveRecord::Base
has_many :things
has_many :thing_ratings, through: :things
# attr_accessible :average_rating
def avg_rating
#avg_rating ||= thing_ratings.average("thing_ratings.rating")
end
end
The easy way :
class User < ActiveRecord::Base
has_many :things
def avg_rating
#avg_rating ||= average(things.map(&:thing_rating))
end
private
def average(values)
values.inject(0.0) { |sum, el| sum + el } / arr.size
end
end
This is fine as a starter. But if you have a bit of trafic, you might find yourself with scaling problems.
You'll then have to refactor this to avoid making an SQL query to the things every time you call the method for a different user.
You could then have several possibilites :
Add a field in your User database, avg_rating, which would be updated by the ThingRating when it's created or updated.
Use a memcached or redis database to cache the value and invalidate the cache every time a ThingRating is updated or created.
These solutions aren't exhaustive of course. And you could find other ones which would better fit your needs.
i have a bunch of activerecord classes that look way to similar for my liking!
can I extend a base class to DRY the methods up, or is that going to confuse rails?
maybe i can share some stuff but not others?
if not - what's the best way to proceed?
many thanks ;)
class Stage < ActiveRecord::Base
acts_as_taggable
has_many :services, :as => :serviceable
belongs_to :event
belongs_to :user
after_save :tag!
def t(s)
self.tag_list.add s
self.event.tag_list.add s
end
# injected to after_save -> http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
def tag!
s = self
if s.id > 0 then s.t "id-greater-than-0" end
if s.id > 0 then s.t "some-stage-specific-stuff" end
self.tag_list
end
end
class Sound < ActiveRecord::Base
acts_as_taggable
has_many :services, :as => :serviceable
belongs_to :event
belongs_to :user
after_save :tag!
def t(s)
self.tag_list.add s
self.event.tag_list.add s
end
# injected to after_save -> http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
def tag!
s = self
if s.id > 0 then s.t "some-sound-specific-stuff" end
self.tag_list
end
end
You can use Single Table Inheritance for this problem. Essentially you will have two separate models, but they'll be saved in the same table. Along with that you can extract out the common functionality into a parent class, and leave the specifics for the child classes. You can read more about STI here: http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Single+table+inheritance