user2490003
user2490003

Reputation: 11890

Overriding a has_many association getter

A user can have several cars -

Every time I call @user.cars it returns the list of cars in default search order.

If I wanted the association sorted on some arbitrary field, I could do

class User < ActiveRecord::Base
  has_many :cars, -> { order :num_wheels }
end

But let's say my ordering logic is complex and I want to just override the association getter to implement my own logic

I try something something like -

class User < ActiveRecord::Base
  has_many :cars

  def cars
    # Pretend this is complex logic
    cars.order(:num_wheels)
  end
end

However that obviously fails because you can't reference the original cars from inside the overridden cars method without it looping infinitely.

Is there a way to reference the "original" getter from inside my overridden getter?

Thanks!

Upvotes: 9

Views: 6437

Answers (3)

lei liu
lei liu

Reputation: 2775

Use super:

class User < ActiveRecord::Base
  has_many :cars

  def cars
    # Pretend this is complex logic
    super.order(:num_wheels)
  end
end

when you use a macro like has_many, Rails dynamically creates a module(which could be accessed by User.generated_association_methods).In your case, define the accessors and readers(such as "cars" in your case, which could be accessed by User.generated_association_methods.instance_methods). This module becomes the ancestor of your User class, so you can access the reader method(cars) by "super" in your own cars method.

Upvotes: 24

thanhnha1103
thanhnha1103

Reputation: 1055

Try this:

class User < ActiveRecord::Base
  has_many :cars
  def cars
    Car.where(user_id: id).order(:num_wheels)
  end
end

Upvotes: -6

Joe Marion
Joe Marion

Reputation: 406

With my understanding I believe what has_many is essentially doing is:

Class User < ActiveRecord::Base
  has_many :cars

  # is essentially
  def cars
    Car.where(user_id: self.id)
  end
end

So when a user wants to list all the cars it would still be User.cars. When using ActiveRecord the has_many is assuming both the method name of cars and the foreign keys associated.

Upvotes: 3

Related Questions