简体   繁体   中英

Select multiple values from associated model rails

Assuming I have this association

User have_many posts
Post belongs_to user

User   Post
----------------
id     id
name   title
       user_id

How to list only post title and username with includes/joins ?

(list of posts [title - username])

@posts = Post.includes(:user).select('........')

don't offer this

@posts = Post.all.each {|p| p.user.username}

__________________UP_____________________

It worked for joining 2 tables.

What if I want to use it for more complex example?

check out my prev question optimize sql query rails

@Humza's answer partly worked. it might be something like this

@posts = Post.joins(:user, :category).paginate(:page => params[:page]).order("created_at DESC")

but It doesn't display posts that don't have category

I also need to display gravatar but I think I can just use user.email as usr_email and use gravatar_for (post.usr_email) but I'll have to customize gravatar helper for this.

posts_controller.rb

def index
  @posts = Post.includes(:user).includes(:comments).paginate(:page => params[:page]).order("created_at DESC")
end

index.html.erb

 <%= render @posts %>

_post.html.erb

<%= gravatar_for post.user, size:20 %>
<%= link_to "#{post.title}", post_path(post) %>
<%= time_ago_in_words(post.created_at) %> 
<%= post.comments.count %>
<%= post.category.name if post.category %>

Take a look at pluck .

Post.joins(:user).pluck(:title, :name)

Note that it works in this case because there's no ambiguity regarding the name column, you might want to specify the table explicitly ( pluck(:title, "users.name") ).

includes is used in case of eager-loading. You need joins in this case.

posts = Post.joins(:user).select("posts.title AS title, users.name AS username")

You can access the values then in the following way:

post = posts.first
post.title # will give the title of the post
post.username # will give the name of the user that this post belongs to

If you can pluck multiple columns, then the following might be more useful to you:

posts = Post.joins(:user).pluck("posts.title", "users.name")

The result will be a 2D array, with each element being an array of the form [post_title, post_username]

You can call array methods on a scope so:

Post.includes(:user).map { |p| [p.title, p.user.name] }

will get the posts with included user and map each post to a tuple of the post title and the user name.

That may not entirely answer your question as I think you might want to restrict the results of the query to just the required fields in which case, I think you can add a .select('title', 'users.name') to the query. (Not in a position to test at the moment)

Post.joins(:user, :category)

but It doesn't display posts that don't have category

That's because joins uses INNER JOIN to join the tables together. If you want to everything from Post even though the particular record doesn't have its counterpart in the other table, you need to use LEFT JOIN . Unfortunately ActiveRecord doesn't have a nice way of generating it and you will need to do that manually:

Post.joins("LEFT OUTER JOIN categories ON categories.post_id = posts.id")...

See A Visual Explanation of SQL Joins for more information.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM