Displaying query results in rails - ruby-on-rails

So in my model a user has many roles through assignments and a role can have many users through assignments. I've managed to get the function for creating new assignments up and running but I want to display all the roles a user can perform on the user index page (which lists all the users).
My code for the users stub view thus far is:
<td><%= user.email %></td>
<td><%= user.phone %></td>
<td><%= user.roles.select("description")%></td>
The problem here is that instead of displaying the description for the role the output is:
Any pointers would be much appreciated. Thank you in advance!

Map the relation into an array of the strings, so that:
<td><%= user.roles.select("description")%></td>
becomes:
<td><%= user.roles.select("description").map &:description %></td>
or:
<td><%= user.roles.select("description").map(&:description).join(", ") %></td>
to join it into a single comma-separated string.

Here it is
user.roles.select("description").map(&:description).join(", ")

Related

Ruby - how to join output from a Rails command?

There are multiple calculations I'd like to be able to perform on my database, all centred around a user id, but I don't know how to recombine them after.
Let's say I have three tables for User, Purchases, and DiscountUsage. I want to find the last date a user made a purchase and the total number of discounts used. I would run two separate commands:
User.joins(:purchases).group("users.id").select("users.id", "MAX(purchases.purchase_date)")
User.joins(:discount_usages).group("users.id").select("users.id", "COUNT(*)")
I want my final output to be one table though, joined on users.id, but the output from select isn't the right data type to work on with Rails functions. How can I represent that the users.id values from both calls are the same and thus join them based on those columns?
I assume a User may not have any purchases and not all purchases use discount codes.
However, you want a full listing of each user with their last purchase date and total discount usages over all purchases. You may need to use a right join.
Something like:
query = User.select('users.id AS user_id', 'MAX(purchases.purchase_date) AS last_purchase_date', 'COUNT(discount_usages.id) AS total_discount_usages')
.joins('LEFT JOIN purchases ON purchases.user_id = users.id')
.joins('LEFT JOIN discount_usages ON discount_usages.user_id = purchases.user_id')
.group('users.id')
.to_sql
Then in order to grab the fields you could use:
rows = ApplicationRecord.connection.select_all(query).to_hash
https://guides.rubyonrails.org/active_record_querying.html#select-all
This will give you an array of hashes with keys: 'user_id', 'last_purchase_date', 'total_discount_usages'.
...
<% rows.each do |row| %>
<% row.symbolize_keys! %>
<tr>
<td><%= row[:user_id] %></td>
<td><%= row[:last_purchase_date] %></td>
<td><%= row[:total_discount_usages] %></td>
<tr>
...
You can select aggregates from joined tables and access them in the model by using aliases:
#users = User.joins(:purchases, :discount_usages)
.select(
"users.id",
"MAX(purchases.purchase_date) AS latest_purchase",
"COUNT(discount_usages.*) AS discount_usages_count"
)
.group("users.id")
If you want to hydrate the other attributes of the records select users.* instead of just users.id.
<table>
<thead>
<tr>
<th>Id</th>
<th>Latest Purchase</th>
<th>Discount usages</th>
</tr>
</thead>
<tbody>
<% #users.each do |user| %>
<tr>
<td><%= user.id %></td>
<td><%= user.latest_purchase %></td>
<td><%= user.discount_usages_count %></td>
</tr>
<% end %>
</tbody>
</table>
I want my final output to be one table though, joined on users.id, but
the output from select isn't the right data type to work on with Rails
functions.
Not sure quite what you mean here. This example will return a collection of user records just like a normal query.
If you want a "raw" result with just an array of arrays use .pluck instead of .select.

Rails 5 Multiple Sums and Join

This question is related to this previously asked question.
My DB columns for model Taxline: ID, RECEIPT, TAXID, BASE, AMOUNT
With entries:
1,1,001,30$,3$
2,1,001,50$,5$
3,2,001,20$,2$
And then a second table with columns: TICKETID, TICKETNUMBER
My controller
class TaxlinesController < ApplicationController
def index
#taxlines = Taxline.group(:RECEIPT).sum(:AMOUNT)
end
end
My view
<% #taxlines.each do |receipt, amount| %>
<td><%= receipt %></td>
<td><%= amount %></td>
<% end %>
This works great to show a ticket for each row with corresponding total amount.
Question 1. What is the proper way to also show in view sum of BASE? I tried .sum(:AMOUNT, :BASE) and .sum(:AMOUNT).sum(:BASE) but they both don't work.
Question 2. If now I call in view for instance <%= taxline.TAXID %> I get an error.
To solve this I tried to add in view <% #taxlines.each do |receipt, amount, taxid| %> and <td><%= taxid %></td>. And in controller #taxlines = Taxline.group(:RECEIPT).sum(:AMOUNT).select(:TAXID). But it shows a blank column.
Question 3. I want to show TICKETNAME value from TICKETS table.I have already set in Ticketline Model belongs_to :ticket. I assume that after solving question 1 I will be able to do ticketline.ticket.TICKETNAME.Right?
Question 1:
Try this
#taxlines = Taxline.group(:RECEIPT).select("SUM(AMOUNT) AS AMOUNT, SUM(BASE) AS BASE")
Question2:
To access TAXID you need to add this in group column.
#taxlines = Taxline.group(:RECEIPT, :TAXID).select("SUM(AMOUNT) AS AMOUNT, SUM(BASE) AS BASE, TAXID")
Question 3:
To access the other table variables, you need to add join statement to the query and then you should group.
#taxlines = Taxline.joins(:ticket).group(:RECEIPT, :TAXID).select("SUM(AMOUNT) AS AMOUNT, SUM(BASE) AS BASE, TAXID, TICKETNAME")
In the view side, try the below one.
<% #taxlines.each do |taxline| %>
<td><%= taxline.AMOUNT %></td>
<td><%= taxline.BASE %></td>
<td><%= taxline.TAXID %></td>
<td><%= taxline.TICKETNAME %></td>
<% end %>
P.S: Not tried this.

List data from two models

I have two models, Items and Calibrations. Items has many calibrations, meaning that every year the instruments have to be calibrated. Fields, date_calibration and date_expired, are located in the "calibrations" table. (Items: has_many :calibrations, calibration: belongs_to item)
I need list/show all the items that are expiring. I can list all the items without problem of course but, I don't know how to add date_expired to the list.
In the Items controller:
#items = Item.all.order("created_at DESC")
In the Index:
<% #items.each do |item| %>
<tr>
<td><%= item.cod %></td>
<td><%= item.number %></td>
<td><%= item.den_cont %></td>
<td><%= item.branch %></td>
<td><%= item.model %></td>
</tr>
<% end %>
I'm using Aptana and PostgreSQL version 9.1, Ruby 2.1 and Rails 4.1.
Can anyone of you suggest any solution or point me to the right direction?
UPDATE
What should I change to show the item using the sentence below..
Item_controller
Item.includes(:calibrations).where('calibrations.date_expired <= ?' , 2014/07/12)
Index
<% #items.each do |item| %>
Return undefined method each.
ALSO
Any idea on how to show a traffic light depending on how many days left to calibration_date ?? Tks again!
As long you have your relations properly defined in your models I do believe something similar to the following should do the trick!
Item.joins(:calibrations).where(date_expired <whatever condition>)
The equivalent SQL being:
SELECT Items.*
FROM Items
LEFT OUTER JOIN Calibrations
ON Calibrations.item_id = Items.item_id
WHERE date_expired <whatever condition>
With the equivalent SQL of the above statement being (using the includes method):
SELECT *
FROM Items
LEFT OUTER JOIN Calibrations
ON Calibrations.item_id = Items.item_id
WHERE date_expired <whatever condition>
Hope this helps!
Also, if you're not wanting to return any data related to the calibrations table (which it looks like this is the case), I would go with the joins method, however if you are, the includes method would be the way to go (http://tomdallimore.com/blog/includes-vs-joins-in-rails-when-and-where/)
Also, the following may be of interest: http://guides.rubyonrails.org/active_record_querying.html (particularly section 12)!
#items = Item.includes(:calibrations)
.where('calibrations.date_expired <= ?', some_date)
include will join the two tables and allow you to specify conditions on items based on columns from the calibrations table. I think that's what you wanted isn't it?

Make method work with activerecord grouped objects

I have a bunch of objects, and I grouped them by the day they happened.
scope :grouped_by_user_with_total_time, lambda {
group(:user_id, :day).select('user_id, SUM(time_worked) AS time_total, day, editable, approvable, accepted, comments')
}
I also have some methods that change editable, approvable, and accepted. But now since they are grouped, I get this error when trying to approve grouped objects.
Couldn't find TimeLog without an ID
My approve method:
def approve
#time_logs = []
t = TimeLog.find(params[:time_logs])
if t.instance_of?(Array)
#time_logs = t
else
#time_logs << t
end
end
What do I have to change so that the methods can work on all of the hourlogs that are grouped together?
<% #time_logss.each do |timelog| %>
<% if timelog.user_id != current_user.id %>
<tr>
<!--<td><%# check_box_tag 'accept' %></td>-->
<td><%= timelog.id_name %></td>
<td><%= timelog.day.strftime("%B %d, %Y") %></td>
<td><%= timelog.time_total %></td>
<td><%= timelog.state %></td>
<% if timelog.state == "Submitted" %>
<td><%= link_to "Approve", approve_time_sheets_path(time_sheets: timelog), method: :put %></td>
You are trying to combine an aggregate function with the need to access unique data. When you use .select(''), you are telling AR which fields to populate into the object (even attributes that do not exist directly in the database).
If you add in time_logs.*, you'll lose the aggregate on time_worked, since you'll then be forced to add id to the group clause.
What you need to do have 1 instance var with the time_logs and one with the aggregate data:
#time_logs = TimeLog.all
#time_totals = TimeLog.grouped_by_user_with_total_time
Then, in the view, you can pluck out the correct total for each time_log. Although, I'm not clear on how you will be able to relate the specific time_log with it's aggregate equivalent.

Passing a single record from an ActiveRecord model object into a param (or how to pass multiple fields in a param)?

Rails 2.3.5
I have a view displaying 'employee' records in a table where each table row haas a check_box_tag to select that (row) employee record (the table is inside a form_tag). The checkbox is passing an array of employee numbers to a method but I also need it to pass some of the other information from the record (first_name, last_name, etc) in the params.
Orignally this looked like (just passing an param with an array of employee numbers)
<% #employee_search.each do |e| %>
<td><%= check_box_tag 'selected_subordinates[]', e.employee_number %></td>
<td><%= e.employee_number %></td>
<td><%= e.first_name %></td>
<td><%= e.last_name %></td>
...
<% end %>
I'm not sure this was right, but I thought I should pass the entire record ('e') in the param:
<% #employee_search.each do |e %>
<td><%= check_box_tag 'selected_subordinates[]', e %></td>
<td><%= e.employee_number %></td>
<td><%= e.first_name %></td>
<td><%= e.last_name %></td>
...
<% end %>
The param array now looks like:
"selected_subordinates"=>["#<Employee:0xa946970>", "#<Employee:0xa946910>", "#<Employee:0xa9468b0>"]
I thought at this point I would be fine and just itterate through the objects in the param array referring to the record fields, but got an undefined method error:
params[:selected_subordinates].each do |s|
puts s.last_name
end
undefined method `last_name' for "#<Employee:0xa946970>":String
I started wondering if for some reason the entire model object was passed instead of just one record from the object. But, trying [0].last_name resulted in a different error.
params[:selected_subordinates].each do |s|
puts s.last_name
end
undefined method `last_name' for 35:Fixnum
Maybe I should have been using the fields I need to build an array for the param - so the param would be an array of arrays? I haven't had any luck so far trying to search for example of what to do when you need to setup a param array made of arrays, or pass a single model object record (and refer to it).
Thank You - Much Appreciated!
When you used e as the param, Rails was converting e to a String and passing that (you can't pass an object in an HTML form, right? Just values). When you saw "#<Employee:0xa946970>" in your params hash, it wasn't an Employee object, but instead a String with the contents of #<Employee:0xa946970> (which is what you get if you called .to_s on an Employee object).
Passing the ID gets you on the right track, but once you have the ID, you should look up the Employee with that ID from the database.
params[:selected_subordinates].each do |s|
employee = Employee.find(s)
puts employee.last_name
end
Of course, this loads them one at a time, so if you have a lot of checkboxes you could end up generating a large number of queries. You can also use the find method to find multiple objects based on an array of IDs:
employees = Employee.find(params[:selected_subordinates])
employees.each do |e|
puts e.last_name
end

Resources