Sorting an index through an associated table? - ruby-on-rails

I have an index that displays all of the current "dprojects" created. I'm working on being able to click the table headers to sort the data by that parameter. I have this all working except for one column. This column displays our customers by the number we've assigned them. For example, "Customer A" is "008". The "dprojects" table has a column for customer ID, and the "schools" table has columns for both the ID and the actual name of the customer.
Because it is a "dprojects" index, the table is sorting by the ID associated with the customer, and not the customer's name. How can I get it to sort alphabetically by name? Here's my code:
view: (dname is the customer name within the school model)
<table>
<thead>
<tr style="border-bottom: 2px solid black;">
<th> <%= sortable "scode", "School" %></th>
</tr>
</thead>
<tbody>
<% #dprojects.where.not(status: "Completed").each do |dproject| %>
<tr>
<td width="20%" class="dotted"><%= dproject.school.dname[0,25] + "..." %></td>
</tr>
<% end %>
</tbody>
</table>
model:
class Dproject < ActiveRecord::Base
belongs_to :school, foreign_key: 'scode'
end
controller:
def index
#dprojects = Dproject.order(params[:sort])
respond_with(#dprojects)
end
helper:
module DprojectsHelper
def sortable(column, title = nil)
title ||= column.titleize
link_to title, :sort => column
end
end
I thought I would be able to modify the view with the line:
<th> <%= sortable "dproject.school.dname", "School" %></th>
but that doesn't work. What am I missing? Thanks for any assistance!

I suppose it is because there is no such column (dproject.school.dname) in #dprojects. You need to join the tables in order to access also the columns of the model School.
I was tested the sorting on a model Book Author, where Book belongs_to :author, pretty similar to your. For the sorting to work I did like what follows:
In controller there is a join table, note the aliases (AS):
sort_by = params[:sort] || 'author_name'
#books = Book.joins(:author).select('books.name AS book_name, authors.name AS author_name').order(sort_by)
I used sort_by to avoid ambiguous column name, as for me there is name column in both models.
Then in view:
<p>id | <%= sortable 'book_name', 'Book' %> | <%= sortable 'author_name', 'Author' %></p>
<% #books.each do |book| %>
<p><%= book.book_name %> | <%= book.author_name %> </p>
<% end %>

Related

What is the correct associations for join tables in Ruby on Rails?

So I have a recipes app that I'm working on and I can't understand why I'm not getting something to display. I'm using Recipe as a join table and for simplicity I only included it along with three other tables. For purposes of the app the only thing I'm wanting to display is the name associated on each model.
I have the following models:
Vegetable
Protein
Starch
Recipe
Recipe is acting as the join table and has:
belongs_to :vegetable
belongs_to :protein
belongs_to :starch
accepts_nested_attributes_for :vegetable, :protein, :starch
Each of the other model has:
has_one :recipe
So my thought for the view was a simple iteration
<table>
<thead>
<tr>
<th> Vegetable Name </th>
<th> Protein Name </th>
</tr>
</thead>
<% #recipes.each do |recipes| %>
<tr>
<td> <%= recipe.vegetable.name %> <td>
<td> <%= recipe.protein.name %> <td>
</tr>
Yet nothing is coming up beyond the headers. I've done various things with the iteration like changing recipes to recipe, passing recipe as an argument at the end of each column. I've made sure that my Recipe controller has the following:
def recipe_params
params.require(:recipe).permit(:vegetable_id, :protein_id, :starch_id)
end
Am I wrong in my associations and that's why it's not presenting anything?
I think it is a typo. Use this:
<% #recipes.each do |**recipe**| %>
<tr>
<td><%= recipe.vegetable.name %><td>
<td><%= recipe.protein.name %><td>
</tr>
<%end%>

Rails displaying table data dynamically

Models
class Smoothval < ActiveRecord::Base
has_many :smoothings
has_many :steps
end
class Step < ActiveRecord::Base
belongs_to :usdzar
belongs_to :smoothing
belongs_to :smoothval
default_scope lambda {
order(id: :desc)
}
end
class Usdzar < ActiveRecord::Base
has_many :smoothings
has_many :steps
end
The index view that I am trying to achieve:
<table><thead>
<tr>
<th>Smoothing1</th>
<th>Smoothing2</th>
</tr></thead>
<tbody>
<tr>
<td>"finalsmprice","stepnum"</td>
<td>"finalsmprice","stepnum"</td>
</tr>
</tbody>
</table
So all the fsmprices and the stepnums for every smoothval from the steps table
My index.html.erb looks like this:
<h1>Listing Steps</h1>
<table class="table table-striped table-bordered table-condensed">
<thead>
<% Step.all.includes(:smoothval).group_by(&:smoothval_id).each do |smoothval_id, steps| %>
<tr>
<th>
Smoothing: <%= smoothval_id %>
</th>
</tr>
</thead>
<tbody>
<% steps.each do |step| %>
<tr>
<td>
(<%= step.fsmprice %>): <%= step.stepnum %>
</td>
<% end %>
</tr> </tbody>
<% end %>
</table>
The table is very long and obviously displays all headings and data after one another. I have tried to create a postgres pivot table but that failed using tablefunc so back trying to do it in rails
In my controller I can get the smoothvals (the table headings) into an array as follows, the array looks like [1, 5]:
sv = Smoothval.where('id IN (SELECT DISTINCT(steps.smoothval_id) FROM steps)')
#assigned = sv.pluck(:id)
How can I get the above table layout
Thanks
This was too difficult and came accross a post that said its best to create a pivot table
HOWEVER thats not so easy in Postgres, the issue with the tablefunc extension is that it doesnt dynamically assign the columns, so they need to be statically assigned. So I went this route for now and tested the function https://github.com/hnsl/colpivot
I stuck with the basics and did this but will need to think more about refactoring the solution.
SELECT c_crosstab('Select * from steps', 'ct_view', 'id', 'smoothval_id', 'stepnum', 'first');
SELECT *
FROM crosstab(
'SELECT usdzar_id, smoothval_id, stepnum
FROM steps
ORDER BY 1,2 ASC') -- needs to be "ORDER BY 1,2" here
AS ct ("SV" int, "Smooth1" int, "Smooth2" int, "Smooth3" int);
Hope this helps somoene else

Rails Way of Grouping By an Association

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!

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.

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