Neil
Neil

Reputation: 5178

Rails iterate over only the persisted records associated to an object

I have something like this in my show action:

def show
  @blog = Blog.find(params[:id])
  @new_comment = @blog.comments.build
end

Now in the view I do two things:

  1. I render the _form.html.erb for the Comment so that the user can add a comment to this blog
  2. I want to be able to iterate over the existing comments for this blog in order to display them.

I am having problems with number 2. The issue is that as I iterate over the associated comments with this:

<% @blog.comments.each do |comment| %>

It is grabbing that @new_comment while iterating over the associated blogs. How can I exclude that built-but-not-yet-persisted @new_comment?

It would be nice if something like @blog.comments.each_with_id worked, or even @blog.comments.persisted.each. Ultimately I only want to iterate over the persisted comments associated to this blog.

I would prefer not to have to nest a conditional that asks if the comment is persisted?. I'm hoping there is an iterator out there for my current situation that just grabs the persisted records.

Upvotes: 2

Views: 1018

Answers (3)

PhilVarg
PhilVarg

Reputation: 4820

in your view loop, you can skip if comment.new_record?

<% @blog.comments.each do |comment| %>
  <% next if comment.new_record? %>

EDIT per your comment:

if you dont want filter out during the iteration, you can reject new records before iterating. however, i wouldn't recommend this approach, as youre creating an entirely new array of records for little reason. Your performance shoundnt really take a hit assuming blogs dont have thousands of comments, but its still not a great practice.

<% @blog.comments.reject(&:new_record?).each do |comment| %>

if you truly want to separate the logic from the view and controller, you can make another variable entirely dedicated to blog comments, prior to building a new one, so that its not included during the iteration.

# controller
def show
  @blog = Blog.find(params[:id])
  @current_comments = @blog.comments
  @new_comment = @blog.comments.build
end

#view
<% @current_comments.each do |comment| %>

for what its worth, i'd still recommend the first apprach

Upvotes: 2

Laura Paakkinen
Laura Paakkinen

Reputation: 1691

How about if you create the new comment without using the association to the blog instance? Then it won't show up when you iterate through @blog.comments.

def show
  @blog = Blog.find(params[:id])
  @new_comment = Comment.new(blog: @blog)
end

Upvotes: 3

Marc Lainez
Marc Lainez

Reputation: 3080

You could add a class method to your Comment model such as this:

def self.persisted
  reject { |comment| comment.new_record? }
end

Then call

@blog.comments.persisted

The downside is that it's not after this you don't have an ActiveRecord::Relation anymore and might break your scopes chaining. Make sure you're using it last in your ActiveRecord queries.

Upvotes: 1

Related Questions