heading_to_tahiti
heading_to_tahiti

Reputation: 795

Rails ActiveRecord how to order by a custom named association

Ok so have created 2 models User and Following. Where User has a username attribute and Following has 2 attributes which are User associations: user_id, following_user_id. I have set up these associations in the respective models and all works good.

class User < ActiveRecord::Base
  has_many :followings, dependent: :destroy
  has_many :followers, :class_name => 'Following', :foreign_key => 'following_user_id', dependent: :destroy
end

class Following < ActiveRecord::Base
  belongs_to :user
  belongs_to :following_user, :class_name => 'User', :foreign_key => 'following_user_id'
end

Now I need to order the results when doing an ActiveRecord query by the username. I can achieve this easily for the straight-up User association (user_id) with the following code which will return to me a list of Followings ordered by the username of the association belonging to user_id:

Following.where(:user_id => 47).includes(:user).order("users.username ASC")

The problem is I cannot achieve the same result for ordering by the other association (following_user_id). I have added the association to the .includes call but i get an error because active record is looking for the association on a table titled following_users

Following.where(:user_id => 47).includes(:user => :followers).order("following_users.username ASC")

I have tried changing the association name in the .order call to names I set up in the user model as followers, followings but none work, it still is looking for a table with those titles. I have also tried user.username, but this will order based off the other association such as in the first example.

How can I order ActiveRecord results by following_user.username?

Upvotes: 0

Views: 1090

Answers (1)

SHS
SHS

Reputation: 7744

That is because there is no following_users table in your SQL query.

You will need to manually join it like so:

Following.
joins("
  INNER JOIN users AS following_users ON
  following_users.id = followings.following_user_id
").
where(user_id: 47). # use "followings.user_id" if necessary
includes(user: :followers).
order("following_users.username ASC")

To fetch Following rows that don't have a following_user_id, simply use an OUTER JOIN.

Alternatively, you can do this in Ruby rather than SQL, if you can afford the speed and memory cost:

Following.
where(user_id: 47). # use "followings.user_id" if necessary
includes(:following_user, {user: :followers}).
sort_by{ |f| f.following_user.try(:username).to_s }

Just FYI: That try is in case of a missing following_user and the to_s is to ensure that strings are compared for sorting. Otherwise, nil when compared with a String will crash.

Upvotes: 3

Related Questions