2

I'm not sure if this is just a lacking of the Rails language, or if I am searching all the wrong things here on Stack Overflow, but I cannot find out how to add an attribute to each record in an array.

Here is an example of what I'm trying to do:

@news_stories.each do |individual_news_story|
  @user_for_record = User.where(:id => individual_news_story[:user_id]).pluck('name', 'profile_image_url');
  individual_news_story.attributes(:author_name) = @user_for_record[0][0]
  individual_news_story.attributes(:author_avatar) = @user_for_record[0][1]
end

Any ideas?

CFitz
  • 178
  • 4
  • 16

2 Answers2

3

If the NewsStory model (or whatever its name is) has a belongs_to relationship to User, then you don't have to do any of this. You can access the attributes of the associated User directly:

@news_stories.each do |news_story|
  news_story.user.name  # gives you the name of the associated user
  news_story.user.profile_image_url  # same for the avatar
end

To avoid an N+1 query, you can preload the associated user record for every news story at once by using includes in the NewsStory query:

NewsStory.includes(:user)... # rest of the query

If you do this, you won't need the @user_for_record query — Rails will do the heavy lifting for you, and you could even see a performance improvement, thanks to not issuing a separate pluck query for every single news story in the collection.

If you need to have those extra attributes there regardless:

You can select them as extra attributes in your NewsStory query:

NewsStory.
  includes(:user).
  joins(:user).
  select([
    NewsStory.arel_table[Arel.star],
    User.arel_table[:name].as("author_name"),
    User.arel_table[:profile_image_url].as("author_avatar"),
  ]).
  where(...) # rest of the query
Mate Solymosi
  • 5,699
  • 23
  • 30
  • Thank you @Máté, I am now using the benefits of the assocation rather than doing the hard query. However I am still having trouble assigning these values as additional attributes/properties to the record/object. I should have stated this in the original question, but these properties I am attempting to add are not inherent in the model. I am merely adding them to form a desired API response. Do you know if this is possible or must they be attributes in the model from the start? – CFitz Jul 13 '17 at 23:45
  • Updated my answer. Though if you want to control how your API response looks, you should use serializers instead of contorting your ActiveRecord queries. – Mate Solymosi Jul 13 '17 at 23:50
  • Thank you @Máté, really appreciate both the updated answer and the serializer recommendation. Going to look into this. – CFitz Jul 13 '17 at 23:53
0

It looks like you're trying to cache the name and avatar of the user on the NewsStory model, in which case, what you want is this:

@news_stories.each do |individual_news_story|
  user_for_record = User.find(individual_news_story.user_id)

  individual_news_story.author_name = user_for_record.name
  individual_news_story.author_avatar = user_for_record.profile_image_url
end

A couple of notes.

  1. I've used find instead of where. find returns a single record identified by it's primary key (id); where returns an array of records. There are definitely more efficient ways to do this -- eager-loading, for one -- but since you're just starting out, I think it's more important to learn the basics before you dig into the advanced stuff to make things more performant.

  2. I've gotten rid of the pluck call, because here again, you're just learning and pluck is a performance optimization useful when you're working with large amounts of data, and if that's what you're doing then activerecord has a batch api you should look into.

  3. I've changed @user_for_record to user_for_record. The @ denote instance variables in ruby. Instance variables are shared and accessible from any instance method in an instance of a class. In this case, all you need is a local variable.

Alex Sharp
  • 533
  • 5
  • 12