0

My topic model is

class Topic < ActiveRecord::Base
  has_many :posts
  accepts_nested_attributes_for :posts
  validates_presence_of :topic
  validates :topic, length: { minimum: 3 }
end

My post model is

class Post < ActiveRecord::Base
  belongs_to :topic, counter_cache: true
  validates_length_of :title ,:minimum => 3
  validates_length_of :description,:minimum => 5
end

My Topic controller is

class TopicsController < ApplicationController
  before_action :set_topic, only: [:show, :edit, :destroy, :update]
  def index
    @topics = Topic.all
  end
end

My Topic index is

<h1>Topics</h1>
<table class="table">
  <% @topics.each do |t| %>
    <%= link_to t.topic,topic_posts_path(topic_id: t.id) %><%= "(# . 
t.posts_count})" %><br>
    <% @post= t.posts.limit(2) %>
    <% @post.each do |p| %>
      <li>
        <%=link_to p.title,topic_post_path(topic_id: p.topic_id,id: p.id) %><br>
      </li>
    <% end %>
    <%= link_to "more..", topic_posts_path(t.id)%><br>
   <% end %><br>
  <%= link_to "Add Topic",new_topic_path %>
</table>

I want to display the first two posts associated with the topic in the topic's index. I used limit for that, but can I do this using ruby without passing any query to db?

3 Answers3

4

Create new association which takes only 2 records of associated model posts.

topic.rb

has_many :recent_posts, -> { limit(2) }, class_name: 'Post'

topics_controller.rb

class TopicsController < ApplicationController
  before_action :set_topic, only: [:show, :edit, :destroy, :update]
  def index
    @topics = Topic.includes(:recent_posts)
  end
end

In your view

<h1>Topics</h1>
<table class="table">
  <% @topics.each do |t| %>
    <%= link_to t.topic,topic_posts_path(topic_id: t.id) %><%= "(# . 
t.posts_count})" %><br>
    <% t.recent_posts.each do |p| %>
      <li>
        <%=link_to p.title,topic_post_path(topic_id: p.topic_id,id: p.id) %><br>
      </li>
    <% end %>
    <%= link_to "more..", topic_posts_path(t.id)%><br>
   <% end %><br>
  <%= link_to "Add Topic",new_topic_path %>
</table>
Vishal
  • 7,113
  • 6
  • 31
  • 61
-2

You can use @post.last(2) which is a ruby method.

Rashid D R
  • 162
  • 6
  • 1
    Sorry , but last `.last(2)` will internally fire `limit 2`, query which is a sql query. – Anand Aug 14 '18 at 07:18
-3

but can I do this using ruby without passing any query to db?

yes you can. Controller

  def index
    @topics = Topic.includes(:posts)
  end

View

<h1>Topics</h1>
<table class="table">
<% @topics.each do |t| %>
  <%= link_to t.topic,topic_posts_path(topic_id: t.id) %><%= "(# . 
t.posts_count})" %><br>
  <%@posts= t.posts%>
  <% @posts.each_with_index do |p, index| %>
    <%if index <= 1%>
      <li>
        <%=link_to p.title,topic_post_path(topic_id: p.topic_id,id: p.id) %><br>
      </li>
    <%end%>
  <%end%>
  <%= link_to "more..", topic_posts_path(t.id)%><br>
  <br>
<%= link_to "Add Topic",new_topic_path %>
</table>
Anand
  • 6,457
  • 3
  • 12
  • 26
  • Without an explicit `includes` as shown in the correct answer by @Vishal this will produce `N+1`. – Aleksei Matiushkin Aug 14 '18 at 07:05
  • @mudasobwa Did i misunderstand the question? `can I do this using ruby without passing any query to db?` – Anand Aug 14 '18 at 07:10
  • The code you have provided passes an additional query to DB here: `@posts= t.posts`. `Topic.all` does not load associations from the database by default. – Aleksei Matiushkin Aug 14 '18 at 07:11
  • @mudasobwa In that way in controller it should be `@topics = Topic.includes(:posts)`, But so far as user's question concerned which is without using db query and using simple ruby, my answer should not be down voted. However i appreciate the answer provided in standard way. – Anand Aug 14 '18 at 07:15
  • 1
    Your answer surely should be downvoted because it does not decrease a number of database queries by any mean. Open a console and check. Once again: `@posts = t.posts` **executes a query against database**. – Aleksei Matiushkin Aug 14 '18 at 07:19
  • @NARENPUSHPARAJU Why do you need to us `group_by` in simple ruby you can use each_with_index to limit first 2 post. – Anand Aug 14 '18 at 07:52
  • @Gabbar Yeah your's answer is correct. My guide asked me to use group_by thats's why i'm so curious about that – NAREN PUSHPARAJU Aug 14 '18 at 07:57
  • @NARENPUSHPARAJU I am curious, how is this an accepted answer even though you really need an answer using `group_by`. Needless to mention it doesn't reduce the database queries. So, there are better answers here. – Jagdeep Singh Aug 14 '18 at 08:03
  • @JagdeepSingh He gave me the answer to use by each_with index which doesn't pass any query to my db which i wanted. But yeah you are correct his answer doesn't give the answer i expected. Unaccepted . – NAREN PUSHPARAJU Aug 14 '18 at 08:08
  • @JagdeepSingh I used includes(:posts) in my topic so N+1 is solved – NAREN PUSHPARAJU Aug 14 '18 at 08:13
  • @JagdeepSingh After using includes(:post) it should reduce N+1 query, which i mentioned in answer, and if you are talking about coding standard, vishal has given good answer in that case, i don't know why my answer should be down-voted, although i have answer as per user requirement. please cross check it. – Anand Aug 14 '18 at 08:17
  • @NARENPUSHPARAJU Feel free to accept/upvote answer if you this answer fullfill your requirement. thanks anyway. – Anand Aug 14 '18 at 08:18
  • @Gabbar it was downvoted (at least by me) when there were N+1 queries. Can't revert it now due to time constraint. – Jagdeep Singh Aug 14 '18 at 08:19
  • @JagdeepSingh By upvote you can revert it :D. believe me i can't write that dirty answer, i just followed user requirement . anyway leave it thanks :) – Anand Aug 14 '18 at 08:23
  • @Gabbar Okay . Your answer worked superbly. But it doesn't solved what my guide said. Anyway Thank you for your answer. I will discuss your answer with my guide – NAREN PUSHPARAJU Aug 14 '18 at 08:23
  • @NARENPUSHPARAJU Glad to know that, by accepting or upvoting answer you can say me thanks. – Anand Aug 14 '18 at 08:24
  • @JagdeepSingh His answer doesn't have N+1 query issue – NAREN PUSHPARAJU Aug 14 '18 at 08:26
  • @NARENPUSHPARAJU check my last comment. That accepts it. – Jagdeep Singh Aug 14 '18 at 08:27
  • @Gabbar Accepted. But did you know how to use by group_by – NAREN PUSHPARAJU Aug 14 '18 at 08:27
  • @NARENPUSHPARAJU Yes, i do, but so far as i think you don't have clarity of your requirement, there is no point to use group_by here, but i can give you some references for that 1 => https://apidock.com/ruby/Enumerable/group_by 2=> https://stackoverflow.com/questions/18920877/ruby-array-of-hash-group-by-and-modify-in-one-line 3=> http://markusjais.com/the-group_by-method-from-rubys-enumerable-mixin-and-compared-with-scal/ – Anand Aug 14 '18 at 08:30
  • @Gabbar Thank you i will refer those. – NAREN PUSHPARAJU Aug 14 '18 at 08:33