Summing child objects rails 4.0 - ruby-on-rails

I have a model Task Orders that has_many Invoices. One of my Task Order attributes is "total invoiced". One of my Invoice attributes is "amount". I want a relationship where Total Invoiced = sum of "Amount". I want this to show up in my task_order/index page. Here is my task order index controller:
def index
#task_orders = TaskOrder.all
#invoices = #task_order.invoices
#task_order.invoicedAmount = #task_order.invoices.sum(:amount)
end
I am getting the error undefined method `invoices' for nil:NilClass
I do want to mention that my code in task_order/show works:
def show
#invoices = #task_order.invoices
#task_order.invoicedAmount = #invoices.sum(:amount)
end
As a follow up question, I am much more familiar with SQL queries than I am using Active Record queries. Can someone point me to a guide on how to render the results of a pure SQL query?
Thank you!!

Your index method is not going to work, because you're getting #invoices from #task_order.invoices, but you declare #task_orders instead. Note the singular vs. plural difference.

Related

Count total bookings by category - Rails

My BookingGroup has_many Booking. Booking contains column category where the data can be "adult" or "child_infant" or child_normal.
Now I want to count all total %child% and display it in my index view table
I was'nt sure whether this could be done in one line or I have to use a scope, this is where I stucked.
BookingGroup model
def search_by_category
bookings.visible.map(&:category).inject(:+)
end
Assuming category is a string column, you should be able to count it like that :
bookings.visible.where("category LIKE ?", "child%").count
bookings.visible.where(category: ["child_infant", "child_normal"]).count
We can use LIKE just as in SQL with active record
In your BookingGroup model
def search_by_category
bookings.visible.where('category LIKE ?', '%child%').size
end
But, if you do so for many booking_groups, your code will have N+1 queries issue. You can use eager load in your controller
#booking_groups = BookingGroup.joins(:bookings).select('booking_groups.*', 'count(*) as total_bookings').where('bookings.category LIKE ?', '%child%').group(:id)
Then you can
#booking_groups.first.total_bookings

Get all included Active Record objects of collection

Is there any way of getting all objects, included in Active Relation query?
Something like this:
def index
#items = Item.all.includes(:comments)
#comments = #items.comments // No such method for AR Collection :(
end
The obvious way to get all the items in this case is:
#comments = #items.map(&:comments).flatten.uniq
Thanks to using .includes(:comments) there should be no N+1 queries, but I worry about the performance of this code. Are there any built-in or more effective ways of getting all included records of collection?
You can request comments like this:
#comments = Comment.where(item_id: Item.pluck(:id))

Querying or iterating join tables in ActiveRecord/Rails

I have two tables:
Venues
has_many :venue_intel_maps
VenueIntelMap
belongs_to :venue
In the venue_intel_map table there's a column called :tag_label that I want to grab for each particular venue that has a venue_intel_map
In my controller below this returns an array of each venue where then each venue has a venue_id
def show
#venues = Venue.where(parent_venue_id: current_admin.parent_venue_id)
end
So in my views, I can do this for particular venue.
- #venues.each do |venue|
=venue.name
=venue.address
=venue.location
But because I want to get to venue_intel_maps table so I can call a column called :tag_label I wanted to avoided a nested each statement that might look something like this
- #venues.each do |venues|
- venues.venue_intel_maps.each do |intel_maps|
= intel_maps.tag_label
Is there anyway I can clean this up or any suggestions? I was trying to play around with .joins or .selects in active record but didn't know how to successfuly do that so I can somehow put the logic in my Venues model.
EDIT
Would it also be better somehow to define in the controller my venue doing this? In the views I'm going to be pulling things from my venue_intel_maps table so would this serve any advantage?
#venues = Venue.joins(:venue_intel_maps).where(parent_venue_id: current_admin.parent_venue_id)
depending on your Rails version, try:
#venues.venue_intel_maps.pluck(:tag_label)
#venues.venue_intel_maps.map(&:tag_label)
see: http://rubyinrails.com/2014/06/rails-pluck-vs-select-map-collect/
You could preload (Rails 3+) the venue_intel_maps in the controller
def show
#venues = Venue.where(
parent_venue_id: current_admin.parent_venue_id
).preload(:venue_intel_maps)
end
Then in the view, use the iteration you suggested
- #venues.each do |venue|
= venue.name
= venue.address
= venue.location
= venue.venue_intel_maps.each do |intel_maps|
= intel_maps.tag_label

Sorting users by score - rails

In my rails app, each user has a karma/score that i'm calculating through the user model as follows:
after_invitation_accepted :increment_score_of_inviter
def increment_score_of_inviter
invitation_by.increment!(:score, 10)
end
def comment_vote_count
Vote.find(comment_ids).count * 2
end
def calculated_vote_count
base_score + comment_vote_count
end
def recalculate_score!
update_attribute(:score, calculated_vote_count)
end
I'm trying to create paginated list of all the users, sorted by their scores. With thousands of users, how do I do this efficiently?
I was think of using:
User.all.sort_by(&:calculated_vote_count)
But, this would be pretty heavy.
Well...using User.all upon a table full of records will be a memory hog for your application. Instead you should try to accomplish what you want on the DB layer.
At this point I'm assuming base_score is one of the table columns (is base_score same as score?), so you'd have to do something like the following (using LEFT JOIN):
User.select("users.*, (COUNT(votes.id) * 2 + users.base_score) AS calculated_vote_count").joins("LEFT JOIN votes ON votes.user_id = user.id").order("calculated_vote_count DESC")
And then you can paginate the results the way you like.
I didn't test it, but it should work. Let me know if doesn't.
It's pretty straight forward:
User.order('score DESC').all
Obviously you'd have pagination, User.order('score DESC').page(params[:page]).per(20) with Kaminari.

Help converting Rails 2 Database logic to Rails 3.1/ PostgreSQL

How do I select a single random record for each user, but order the Array by the latest record pr. user.
If Foo uploads a new painting, I would like to select a single random record from foo. This way a user that uploads 10 paintings won't monopolize all the space on the front page, but still get a slot on the top of the page.
This is how I did it with Rails 2.x running on MySQL.
#paintings = Painting.all.reverse
first_paintings = []
#paintings.group_by(&:user_id).each do |user_id, paintings|
first_paintings << paintings[rand(paintings.size-1)]
end
#paintings = (first_paintings + (Painting.all - first_paintings).reverse).paginate(:per_page => 9, :page => params[:page])
The example above generates a lot of SQL query's and is properly badly optimized. How would you pull this off with Rails 3.1 running on PostgreSQL? I have 7000 records..
#paintings = Painting.all.reverse = #paintings = Painting.order("id desc")
If you really want to reverse the order of the the paintings result set I would set up a scope then just use that
Something like
class Painting < ActiveRecord::Base
scope :reversed, order("id desc")
end
Then you can use Painting.reversed anywhere you need it
You have definitely set up a belongs_to association in your Painting model, so I would do:
# painting.rb
default_scope order('id DESC')
# paintings_controller.rb
first_paintings = User.includes(:paintings).collect do |user|
user.paintings.sample
end
#paintings = (first_paintings + Painting.where('id NOT IN (?)', first_paintings)).paginate(:per_page => 9, :page => params[:page])
I think this solution results in the fewest SQL queries, and is very readable. Not tested, but I hope you got the idea.
You could use the dynamic finders:
Painting.order("id desc").find_by_user_id!(user.id)
This is assuming your Paintings table contains a user_id column or some other way to associate users to paintings which it appears you have covered since you're calling user_id in your initial code. This isn't random but using find_all_by_user_id would allow you to call .reverse on the array if you still wanted and find a random painting.

Resources