I am wondering about the difference between ActiveRecord::Associations::CollectionProxy
and ActiveRecord::AssociationRelation
.
class Vehicle < ActiveRecord::Base
has_many :wheels
end
class Wheel < ActiveRecord::Base
belongs_to :vehicle
end
So if I do:
v = Vehicle.new
v.wheels # => #<ActiveRecord::Associations::CollectionProxy []>
v.wheels.all # => #<ActiveRecord::AssociationRelation []>
I have no idea what is the difference between them and why this is implemented this way?
ActiveRecord::Relation
are simple query objects before being turned into a query and executed, CollectionProxy
on the other hand are a little bit more sophisticated.
First of all you get association extentions, you probably saw something that looks like this, assume a book store model that has many books
class Store < ActiveRecord::Base
has_many :books do
def used
where(is_used: true)
end
end
end
This way you get to call used books in a store using a syntax that looks like this
Store.first.books.used
But this is the most basic uses, you could use the attributes that are exposed to you in the collection proxy, which are owner
, reflection
and target
The owner
provides a a reference to the parent object holding the association
The reflection
object is an instance of ActiveRecord::Reflection::AssocciationReflection
and contains all the configuration options for the association.
The target
is the association collection objects (or single object when has_one
and belongs_to
).
Using those methods you could do some conditons in your association extention, for example if we have a blog, we give access to all deleted posts to users who are admins (lame example I know)
Class Publisher < ActiveRecord::Base
has_many :posts do
def deleted
if owner.admin?
Post.where(deleted: true)
else
where(deleted: true)
end
end
end
end
You also get access to two more methods which are reset
and reload
, the first one (reset
) clears the cached association objects, the second one (reload
) is more common and is used to reset
and then loads the associated objects from the database.
I hope this explains how having a CollectionProxy
class would be so useful
Ok. The difference is pretty simple.
Explanation based on your example:
the association proxy in v.wheels
has:
v
as @owner;:has_many
macro.From docs:
Association proxies in Active Record are middlemen between the @owner and the @target. The @target object is not loaded until needed.
v = Vehicle.new
v.wheels # we are not sending any methods to @target object (collection of wheels)
# => #<ActiveRecord::Associations::CollectionProxy []>
Which means, as soon as you call any method on the @target object (that holds collection of wheels
in our case) the @target is loaded, and it becomes ActiveRecord_AssociationRelation
.
v.wheels.all # sending the `all` method to @target (wheels)
# => #<ActiveRecord::AssociationRelation []>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With