stackOverlord
stackOverlord

Reputation: 5412

Rails sort entries based on attributes of related model

I have a strange scenario: I would like to order all posts P by the time at which P's creator and the current user became friends.

The result would be a list of posts with newer friends' posts at the top and older friends' posts at the bottom. We can assume all users are friends of all other users, but each friendship has varying creation times.

I have Post, User, and Friendship models. Posts belong to users and users have many friendships.

class User < ActiveRecord::Base
  has_many :friendships
  has_many :friends, :through => :friendships
  has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
  has_many :inverse_friends, :through => :inverse_friendships, :source => :user
end

class Friendship < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, :class_name => "User"
end 

class Post < ActiveRecord::Base
  belongs_to :user
end

What's the best way to do this? Thanks!

Upvotes: 23

Views: 16466

Answers (3)

Matthew Bennett
Matthew Bennett

Reputation: 303

Just an addendum to this, as a thank you, Sean Hill's answer also works, slightly modified, to sort results using has_many through relationships with fields_for in Rails 4.

So if Messages belong_to both Conversations and Users and you first want a list of Conversations on the User page—perhaps before updating or modifying some message attribute—but sorted by an attribute on Conversations (e.g. "title"), not Messages, you can go from User through Messages to Conversations and Conversation attributes.

In the view:

<% fields_for :messages, @user.messages.joins(:conversation).order("title ASC") do %>

or in the User.rb model:

has_many :messages, -> {joins(:conversation).order("title ASC")}

Upvotes: 1

creativehandle
creativehandle

Reputation: 364

Turn Sean Hill's answer into a scope with:

class Post < ActiveRecord::Base
  belongs_to :user

  scope :ordered, -> {
    joins(:user => :friendships).order("friendships.friended_at")
  }
end

Upvotes: 6

Sean Hill
Sean Hill

Reputation: 15056

You can order on the associated table by doing a nested join, like so:

@posts = Post.joins(:user => :friendships).order("friendships.friended_at")

Upvotes: 34

Related Questions