Rails Way of Grouping By an Association - ruby-on-rails

I know I am making this more difficult than it needs to be. There has to be a rails way of accomplishing this task. To demonstrate: here are two models:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :blogs
def to_s
name
end
end
#app/models/blog.rb
class Blog < ActiveRecord::Base
belongs_to :user
delegate :name, to: :user, prefix: true, allow_nil: true
def to_s
title
end
end
So what I want to do is group all of the blogs by the associated user. Then I want to list those blogs per each user.
Key Detail: not all blogs have an associated user. Some have user_id = nil. Here is a listing of the blogs to demonstrate (last two blogs have user_id = nil):
So I got what I wanted to work. But the solution is not easy to read, and I know there must be some way to accomplish this using Rails' query interface. I couldn't figure it out though, so I hacked together my own solution below:
#app/controllers/admin_controller.rb
class AdminController < ApplicationController
def index
#group_blogs_by_user = {}
User.all.pluck(:name).each{|user| #group_blogs_by_user[user] = []}
#group_blogs_by_user[nil] = [] #provide nil category when no user_id was specified for a blog
Blog.all.each {|blog| #group_blogs_by_user[blog.user_name].push(blog)}
#group_blogs_by_user.reject!{ |_ , v|v.empty?} #do not show users that have no blogs
end
end
And here is the view to display it:
#app/views/admin/index.html.erb
<h1>Showing Count of Blogs per User</h1>
<table>
<thead>
<th>User</th>
<th>Blogs Count</th>
</thead>
<tbody>
<% #group_blogs_by_user.each do |user, blogs_of_this_user| %>
<tr>
<td><%= user || "No User Specified"%></td>
<td><%= blogs_of_this_user.size %></td>
</tr>
<% end %>
</tbody>
</table>
<hr>
<h1>Showing Breakdown of Blogs per User</h1>
<% #group_blogs_by_user.each do |user, blogs_of_this_user| %>
<h3><%= (user || "No User Specified") + " (#{blogs_of_this_user.size} blogs)" %></h3>
<table class="table">
<thead>
<th>Blog ID</th>
<th>Created At</th>
</thead>
<tbody>
<% blogs_of_this_user.each do |blog| %>
<tr>
<td> <%= link_to(blog.id, blog)%></td>
<td> <%= blog.created_at.strftime("%d-%m-%Y")%></td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
And here is what it renders, which is what I want:
I run into this situation all the time where I want to group a table by some association, and I find myself continually hacking together a solution. How can I do this the rails way with Rails' Query Interface?

To get all the blogs from a user and print the username, blog_id and blog count
<% #users.each do |user| %>
<%= user.name %>
<%= user.blogs.count %>
<% user.blogs.each do |blog|%>
<%= blog.id %>
<% end %>
<% end %>
To get the amount of blogs with no user
<%= Blog.where(user: nil).count %>
I hope I got your question right and this helps!

Related

How can I display values from relationship on column array?

I have 2 tables (users and meetings).
I'm trying to displaying the name of the user on table index view
users
|id| |name|
1 DEMO 1
2 DEMO 2
3 DEMO 3
meetings
|id| |user_id|
1 ["1", "2"]
2 ["2"]
3 ["2", "3"]
The Controller /app/controllers/meetings_controller.erb
def index
#meetings = Meeting.all
end
Models
#meeting.rb
class Meeting < ApplicationRecord
belongs_to :users
end
#user.rb
class User < ApplicationRecord
has_many :meetings
end
The View /app/views/meetings/index.html.erb
<table>
<thead>
<tr>
<td>id</td>
<td>User Names</td>
</tr>
</thead>
<tbody>
<% #meetings.each do |meeting| %>
<tr>
<td><%= meeting.id %></td>
<td><%= meeting.user_id %></td>
</tr>
<% end %>
</tbody>
</table>
I'm trying to display the user_id on array relationship and i tried this code:
I got the following error using the following code
undefined method `each' for "[\"1\", \"2\"]":String
<% meeting.user_id do |array|%>
<%= array.user.name %>
<% end %>
I got the following error using the following code
undefined method `each' for "[\"1\", \"2\"]":String
<% meeting.user_id do |array|%>
<%= array %>
<% end %>
I cannot display the value relationship because of column array.
Can you please help me with this issue?
Thanks in advance.
While there is nothing wrong with your approach, one comes to understand that the path of least resistance (= least pain) is to follow "The Rails Way".
So instead of answering your question, let me suggest that the relationship between your models should be:
# user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :meetings
end
# meeting.rb
class Meeting < ActiveRecord::Base
has_and_belongs_to_many :users
end
# you will also need to create a join table with a migration:
def change
create_join_table :meetings, :users
end
Then the view will include:
<% #meetings.each do |meeting| %>
<tr>
<td><%= meeting.id %></td>
<td><%= meeting.users.map(&:name).join(', ') %></td>
</tr>
<% end %>
I assume that you have a has_many relation between Meeting and User. That means that meeting.users will return the list of the users for the current meeting.
The following will return a comma-separated string with the names.
<table>
<thead>
<tr>
<td>id</td>
<td>User Names</td>
</tr>
</thead>
<tbody>
<% #meetings.each do |meeting| %>
<tr>
<td><%= meeting.id %></td>
<td><%= meeting.users.map(&:name).join(', ') %></td>
</tr>
<% end %>
</tbody>
</table>

Displaying has and belongs to many associations in index

Hey I'm new to Rails and all this so bear with me, thanks!
I have two models:
class User < ApplicationRecord
has_and_belongs_to_many :sports
end
class Sport < ApplicationRecord
has_and_belongs_to_many :users
end
My users have a few different sports that they can choose each. I'm simply trying to display all users in a table, along with which sports they do. However.. the only way I've managed to get anything without an error is by using current_user as shown below. I've been looking how to do this for hours... I know it's going to be stupidly simple but I just can't figure it out or even know how to go in the right direction.
# users_controller.rb
def index
#users = User.all
#sports = current_user.sports
end
# users/index.html.erb
<% #users.each do |user| %>
<tr>
<td><%= link_to user.name, user %></td>
<td><%= link_to user.email, user %></td>
<% #sports.each do |s| %>
<td><%= s.name %></td>
<% end %>
</tr>
<% end %>
That's my current code but obviously this shows only the signed in users associations and repeats it for the other users like this:
<table>
<tr>
<th>Name</th>
<th>Sport 1:</th>
<th>2:</th>
</tr>
<tr>
<td>User 1 (current_user)</td>
<td>Football</td>
<td>Running</td>
</tr>
<tr>
<td>User 2</td>
<td>Football (User 1's Sports)</td>
<td>Running </td>
</tr>
</table>
Thanks in advance.
You can try using the following and deleting #sports = current_user.sports:
<% user.sports.each do |s| %>
<td><%= s.name %></td>
<% end %>
using user.sports while looping through each of the user will lead to N+1 queries on your database. You can change your controller method to something like
def index
#users = User.all.eager_load(:sports)
end
and then in html
<% user.sports.each do |s| %>
<td><%= s.name %></td>
<% end %>
This will load users along with left_outer_join on sports table and this will save to lot of extra queries on your database.
For Info you can refer this good blog.
Thanks

How to add attributes to a active record result in Rails 4?

Lets say that I have a model with name of User. How can I add a virtual attributes to the final result of generated query ?
class User < ActiveRecord::Base
ATTRIBUTES = %w[name email balance]
scope :main_selection, -> { select('name,email,total_bought, total_deposit') }
def balance
(total_deposit - total_bought).round
end
end
and inside my controller I have
#user = User.main_selection
#attributes = User::ATTRIBUTES
Inside the View I would like to show it in a table
<table>
<thead>
<tr>
<% #attributes.each do |a| %>
<th><%= a %><th>
<% end %>
</tr>
</thead>
<tbody>
<% #user.each do |u| %>
<tr>
<% #attributes.each do |a| %>
<td><%= u[a] %><td>
<% end %>
</tr>
<% end %>
</tbody>
<table>
The only problem is that I need to add the balance attribute into the generated result, so that the loop with u[a] could work.
I need to mention that the balance can be called by #user.first.balance, but inside the loop does not work and it shows a nil value instead.
Try u.send(a) instead of u[a]
u[a] will only work on attributes. In your example, balance is not an attribute, it's a method.

Ruby on Rails: Push Posts in to a hash and then show them

I'm creating a twitter-copy and right now I'm trying to get all the posts from all the users you follow and then show them on the home page. I've done this in PHP before, but I'm new at RoR so I might be trying to do this the wrong way.
a User has many Subscriptions
and a Subscription belongs to User
a User has many Posts
and a Post belongs to User
This is what i've got so far:
session_controller.rb
def get_posts
#sub = #current_user.subscriptions.first
Post.where("user_id = ?", #sub.following_id).find_each do |tweet|
render partial: 'shared/tweet', locals: {tweet: tweet}
end
end
I know .first gets only the first subscription, but I wanted to try to get just something out.
home.html.erb
<table>
<tr>
<th>Username</th>
<th>Tweet</th>
</tr>
<%= yield %>
</table>
_tweet.html.erb
<div class="tweet">
<td>Username here somehow</td>
<td><%= tweet.content %></td>
</div>
But right now nothing is coming up in the table.. So, what am I doing wrong? (am I doing anything right at all?)
Try this:
session_controller.rb
def get_posts
#sub = #current_user.subscriptions.first
#tweets = Post.where("user_id = ?", #sub.following_id)
end
home.html.erb
<table>
<thead>
<tr>
<th>Username</th>
<th>Tweet</th>
</tr>
</thead>
<tbody>
<% #tweets.each do |tweet| %>
<%= render 'shared/tweet', tweet: tweet %>
<% end %>
</tbody>
</table>
_tweet.html.erb
<tr class="tweet">
<td><%= tweet.user.name %></td> # Not sure
<td><%= tweet.content %></td>
</tr>
EDIT:
To get all the tweets for all the subscritions:
following_ids = #current_user.subscriptions.map(&:following_id)
#tweets = Post.where(user_id: following_ids)

Rails 3: How to display data of multiple intra-dependent models in a table in the view?

I have a betting application, where users can bet on sports matches (e.g. basketball). I have something like this:
Group has_many :users
Group belongs_to :league
League has_many :matches
Match has_many :bets
User has many :bets
Bet belongs_to :group
Bet belongs_to :match
Bet belongs_to :user
As you can see, users bet in groups. A group bets on one league (e.g. soccer season 2011/2012) and a league consists of many matches, that can be betted on.
I want to render the view for an action in the Group Controller (so Group is the base model). It should display a table with the matches on the horizontal table header and the users on the vertical table header. A table cell should show the bet of a user for the given match, like so:
| Team 1 vs Team 2 | Team 3 vs Team 4
=======+==================+=================
User 1 | 1 : 2 | 0 : 0
-------+------------------+-----------------
User 2 | 2 : 0 | 1 : 0
Now the question is: how to best setup access to my data?
I could set up the #group attribute in the controller and then access data in the view like so:
<table>
<thead>
<tr>
<th> </th>
<% for match in #group.league.matches %>
<th><%= "#{match.team1.name} vs. #{match.team2.name}" %></th>
<% end %>
</tr>
</thead>
<tbody>
<% for user in #group.users %>
<tr>
<th><%= user.name %></th>
<% for match in #group.league.matches %>
<td><%= ?????? %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
My problem is denoted with "??????": How to access the correct bet?
match.bets.find_by_group_id_and_user_id( ... ) # this will generate a new SELECT on DB
match.bets.to_a.find{|bet| bet.user_id == user.id && bet.group_id == #group.id}.home_score # seems cluttered and should not belong in the view
Your input on this is greatly appreciated. Thanks!
This should work, if I'm understanding correctly:
<% for user in #group.users %>
<tr>
<th><%= user.name %></th>
<% for match in #group.league.matches %>
<% bet = Bet.find_by_user_id_and_match_id(user.id, match.id) %>
<td><%= bet.whatever.. %></td>
<% end %>
</tr>
<% end %>
In response to your comment, you could put this stuff in a hash table in the controller like so:
#users = #group.users
#user_bets = #users.inject({}) do |hsh, user|
hsh[user] = {}
#group.league.matches.each do |match|
hsh[user][match] = .. # look up the Bet and assign it here
end
hsh
end

Resources