What is the equivalent of the has_many 'conditions' option in Rails 4?

Ruby on-Rails-4

Ruby on-Rails-4 Problem Overview


Can someone tell me what is the equivalent way to do the following line in Rails 4?

has_many :friends, :through => :friendships, :conditions => "status = 'accepted'", :order => :first_name

I tried the following:

has_many :friends, -> { where status: 'accepted' }, :through => :friendships , :order => :first_name

But I get the following error:

Invalid mix of scope block and deprecated finder options on ActiveRecord association: User.has_many :friends

Ruby on-Rails-4 Solutions


Solution 1 - Ruby on-Rails-4

Needs to be the second arg:

class Customer < ActiveRecord::Base
  has_many :orders, -> { where processed: true }
end

http://edgeguides.rubyonrails.org/association_basics.html#scopes-for-has-many

RESPONSE TO UPDATE:

Put the order inside the block:

has_many :friends, -> { where(friendship: {status: 'accepted'}).order('first_name DESC') }, :through => :friendships

Solution 2 - Ruby on-Rails-4

While other answers on here are technically correct, they violate encapsulation. The User model should not know that the Friendship model has a column called status, and that it can have a specific value like accepted.

If you decide to make a change, to take advantage of Enums in Rails 4, for example, you would have to change both User and Friendship models. This could lead to bugs that maintaining encapsulation avoids.

I would expose a scope in the Friendship model:

scope :accepted, -> { where(status: :accepted) }

I would then use this scope in the User model, hiding any implementation details from User.

has_many :friendships, -> { Friendship.accepted }
has_many :friends, through: :friendships

# Or...

has_many :friends, -> { Friendship.accepted }, through: :friendships

You can go further and rename the scope to accepted_friendships to be clearer.

has_many :accepted_friendships, -> { Friendship.accepted }
has_many :friends, through: :accepted_friendships

Now you have successfully encapsulated implementation details in their respective models. Should anything change you only have one place to change it, reducing maintenance and increasing robustness.

Solution 3 - Ruby on-Rails-4

A Rails 3.2 version of Mohamad's answer would be the following:

class Friend < ActiveRecord::Base
  has_many :friendships, :order => :first_name

  has_many :friends, :through => :friendships,
           :conditions => proc { Friendship.accepted.where_ast }

  has_many :pending_friends, :through => :friendships,
           class_name => Friend,
           :conditions => proc { Friendship.pending.where_ast }
end

class Friendship < ActiveRecord::Base
  scope :status, ->(status) { where(:status => status) }
  scope :accepted, -> { status('accepted') }
  scope :pending, -> { where(arel_table[:status].not_eq('accepted')) } 
end

NOTES:

  • where_ast is important as it returns the AREL nodes that are required for the condition to work
  • within the proc passed to :conditions, self is not always a model instance (e.g. when the association is merged with another query)
  • Using raw SQL within your scopes and associations will likely cause issues at some point to do with namespacing of table names... use AREL.

Solution 4 - Ruby on-Rails-4

In order to work on Rails 4.1 (my case), i had to put:

has_many :friends, -> { where(friendships: { status: 'accepted' }) }, through: :friendships

Note the S on friendships. It refers directly to the database name.

Solution 5 - Ruby on-Rails-4

has_many :friends, -> { where(status: 'accepted').order('first_name')}, through: :friendships

or

has_many :friends, -> { where(status: 'accepted').order(:first_name)}, through: :friendships

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmedBouzidView Question on Stackoverflow
Solution 1 - Ruby on-Rails-4KaleidoscopeView Answer on Stackoverflow
Solution 2 - Ruby on-Rails-4MohamadView Answer on Stackoverflow
Solution 3 - Ruby on-Rails-4br3ntView Answer on Stackoverflow
Solution 4 - Ruby on-Rails-4Oswaldo FerreiraView Answer on Stackoverflow
Solution 5 - Ruby on-Rails-4Amrit DhunganaView Answer on Stackoverflow