append to an active record relations object

10,472

Solution 1

You could do something like this:

def index
  posts_ids = []
  Category.most_popular.limit(20).each do |cat|
    post_ids << Post.enabled.recent.by_category(cat).map(&:id)
  end
  @posts = Post.find( post_ids ).page(1).per(30)
end

Solution 2

Let me define your problem to ensure that I understand it correctly. In your view, you want the latest post of each category first. And then you want all posts ordered by their recentness.

I would create two instance variables in the controller to be later used in the view.

def index
  enabled_posts = Post.enabled.recent

  @category_posts = enabled_posts.joins(:categories).group("categories.id")

  exclude_post_ids = @category_posts.pluck("posts.id")

  @posts = enabled_posts.where("id NOT IN (?)", exclude_post_ids)
end

The above should be convenient if you're using two different sections to display @category_posts and the remaining posts. But if you're using a single section and you want all the posts ordered in a single variable, then simply change your controller code to the following:

def index
  enabled_posts = Post.enabled.recent

  category_posts = enabled_posts.joins(:categories).group("categories.id")

  exclude_post_ids = @category_posts.pluck("posts.id")

  remaining_posts = enabled_posts.where("id NOT IN (?)", exclude_post_ids)

  @posts = category_posts + remaining_posts
end
Share:
10,472
inquisitive
Author by

inquisitive

A quick learner. An Inquisitive. Worked a bit on Android, RoR and the MEEN Stack. Currently working on Golang+React

Updated on June 04, 2022

Comments

  • inquisitive
    inquisitive almost 2 years

    I have a Post model. Having a scope which finds the enabled posts.A post can belong to a category. I also have a Category model, with categories ranked. Category has many posts. I want to display first distinct 20 posts which belong to distinct categories(which are the top 20 categories) and then display the rest of the post in descending order of their published_time.

    This is what I have

    Post model:

    class Post < ActiveRecord::Base
    belongs_to :categories
    scope :by_category, ->(category) { joins(categories).where(categories: { id: category.id }) }
    scope :enabled, where(disabled: false)
    scope :recent, order('published_at DESC')
    

    Category model

    class Category < ActiveRecord::Base
    has_many :feeds
    scope :most_popular, order('rank ASC')
    

    Home Controller

    def index
      Category.most_popular.limit(20).each do |cat|
        @posts= Post.enabled.recent.by_category(cat).page(1).per(30)
      end
    

    in view file I am rendering the attributes of the post that i receive using @posts. But as obvious, it returns only the post of the last category found in the loop. Basically it doesn't append. I tried using << to append.. as in -

    @posts <<  Post.enabled.recent.by_category(cat).page(1).per(30)
    

    But it gives no method << for nil:nil class

    I tried making @posts as an array, but then it does not take page and per of kaminari into play.

    I tried making @posts as ActiveRecord::Relation object using new, it gave argument error.

    I tried making @posts as object of Post but then it says undefined method << for Post, since ofcourse, << is not a method for my model class. I also followed some SO posts but it didn't seem to fit in my step.

    Basically, my insight into achieving this was appending records into the model object and then displaying the object. I even doubt, if my approach is good enough. There could be more efficient way of doing this, which I may be missing out in RoR.