ActiveRecord sort children within parent - ruby-on-rails-3

Is there a way to pre-sort the children of a parent through ActiveRecord (Rails 3.2.13)?
So if you have a setup like this
class Parent < ActiveRecord::Base
has_many :children
[...]
class Children < ActiveRecord::Base
belongs_to :parent
Something like this:
p = Parent.where(:name => 'Diana').includes(:children, :order => 'd_o_b DESC')
That way when I call p.children I am getting an array of objects ordered by birth, and not by their database ID.
Or do I just need to sort my array afterward?

In your Parent model, change the has_many to:
has_many :children, :order => 'd_o_b DESC'
Then anytime you access the children association for a parent record (e.g., #parent.children), they'll be in descending order of date of birth.

Related

Rails query through multiple models

It's been 2 days by now that I'm struggling to write query. So, I'm trying to query within these 3 related models:
class Company...
has_many :programs
end
class Program...
belongs_to :company
has_many :transactions
end
class Transaction...
belongs_to :program
end
As output, I need a list of the amount of all Transactions each Company made and on what date.
Your query should be something like this.
Company.includes(:programs => :transactions).map do |company|
{:company_name => company.name,
:transactions => company.programs.map{|program| program.transactions.map{|t| {:amount => t.amount, :date => t.created_at.strftime("%d-%m-%Y")}}.flatten
}
end
You could change the format of the date by referring to strftime method of Time class from here - http://ruby-doc.org/core-2.2.0/Time.html#method-i-strftime.

Sort by a column in JOIN table of many-to-many relationship (rails)

I have a many to many relationship between category and post.
The join table is category_post_relationships.
class Post < ActiveRecord::Base
has_many :categories, through: :category_link_relationships
has_many :category_post_relationships , :dependent => :destroy
end
class Category < ActiveRecord::Base
has_many :posts, through: :category_link_relationships
has_many :category_post_relationships , :dependent => :destroy
end
class CategoryPostRelationship < ActiveRecord::Base
belongs_to :post
belongs_to :category
end
If I have a category, the category can query for all posts by category.posts. And I want to sort these posts by the created_at of the join table category_link_relationships. There can be only one record for each category and post. I want to sort the links by the column created_at of related relationship records.
For example, post 1 was created and associated to category A.
Then, the post 1 was associated to category B.
Post 2 was then created and associated to category B.
category B now has post 1 and post 2. I want to sort post 1 and post 2 by the created_at of the relationships, not the created_at of posts.
Thanks.
class Post < ActiveRecord::Base
has_many :category_link_relationships , :dependent => :destroy
has_many :categories, through: :category_link_relationships
end
class Category < ActiveRecord::Base
has_many :category_link_relationships , :dependent => :destroy
has_many :posts, through: :category_link_relationships
end
and now you can find posts in next way:
#category.posts.joins(:category_link_relationships).order('category_link_relati‌​onships.created_at')
If you will always order this way, you can use a default scope on the join table.
class CategoryLinkRelationship < ApplicationRecord
belongs_to :post
belongs_to :category
default_scope { order(created_at: :asc)}
end
This will be automatically picked up by ActiveRecord. Be careful, if there is also a default scope on Category it will override the sort.

rails select distinct nested associations and fetch those associations

i have the models User, Company, Product, View
class Company < ActiveRecord::Base
has_many :users
end
class Product < ActiveRecord::Base
has_many :views_by_user, -> { where viewable_type: User },
as: :viewable, class_name: "View"
end
class User < ActiveRecord::Base
has_many :viewed, as: :viewer, class_name: "View"
belongs_to :company
end
class View < ActiveRecord::Base
belongs_to :viewable, polymorphic: true
belongs_to :viewer, polymorphic: true
end
What i did with the above is, when a user views product, i save the data in the views
Now i want the list of distinct companies that have looked at my product(via user) and total count for my serializer. what i have done is,
distinct_users = #product.views_by_user
.includes(viewer: [:company])
.joins("left outer join users on views.viewer_id = users.id")
.select("distinct users.company_id, views.*")
but with this, i would have to do something like
distinct_users.will_paginate(...).map(&:viewer).map(&:company)
is there a better way to do it? also if i use distinct_users.count it throws me an error
PG::UndefinedFunction: ERROR: function count(integer, views) does not exist
LINE 1: SELECT COUNT(distinct users.company_id,...
Start from Company if this is the type of record you actually want. You can use merge to combine the conditions on a relation with those from another. Try this:
Company.joins(:users => :viewed).merge(View.where(viewable: #product))
HTH

Rails: How to set up an IF condition with a JOIN in a has_many :through relationship

I have an application where users can customize a calendar and fill it with a given pool of events. A user can also overwrite a title for his own calendar by an alias. So I have the following has_many :through relation:
class Calendar < ActiveRecord::Base
has_many :event_aliases
has_many :events, :through => :event_aliases
end
class Event < ActiveRecord::Base
attr_accessible :title
has_many :event_aliases
has_many :calendars, :through => :event_aliases
end
class EventAliases < ActiveRecord::Base
attr_accessible :course_id, :calendar_id, :custom_name
belongs_to :event
belongs_to :calendar
end
No I want to deliver the calendar with the aliases. If an event has an alias (custom_name), it should be displayed. Otherwise the default event name (title) should be displayed.
Is there a way to easily set up a query that returns all events for the current calendar whether with a custom_name (if exists) or with the default title?
My current solution is to hardcode an if condition into the query which I would like to avoid.
title_column = "case when custom_name IS NOT NULL then custom_name else title end as title"
# assume we are given a calendar_id
Calendar.find(calendar_id).event_aliases.joins(:event).select(title_column, :event_id).each do |event_alias|
# do further stuff here
end
I also could fetch all event_aliases and run through each of them to get the default title if necessary.
# assume we are given a calendar_id
Calendar.find(calendar_id).event_aliases.each do |event_alias|
title = event_alias.custom_name
if title.nil?
title = Event.find(event_alias.event_id).title
# do further stuff here
end
But this one results in too many queries to me.
So is there any smarter way of accomplishing what I want? Maybe using named scopes or another fancy rails technique?
UPDATE
I ended up with making a "custom" select via the has_many :through relationship. So the only thing changes is the Calendar model:
class Calendar < ActiveRecord::Base
has_many :event_aliases
has_many :events, :through => :event_aliases,
:select => "event_aliases.custom_name as custom_name, events.*"
end
So accessing the custom_name / the title now happens a little like #Doon suggested:
Calendar.find(1).courses.each do |course|
title = course.custom_name || course.title
end
This creates only 2 queries instead of 3:
Calendar Load (0.6ms) SELECT `calendars`.* FROM `calendars` WHERE `calendars`.`id` = 1 LIMIT 1
Event Load (0.7ms) SELECT event_aliases.custom_name as custom_name, events.* FROM `events` INNER JOIN `event_aliases` ON `events`.`id` = `event_aliases`.`event_id` WHERE `event_aliases`.`calendar_id` = 1
what about using includes to grab the events at the same time as you pull the aliases.
Calendar.find(1).event_aliases.includes(:event).each do |e|
puts e.custom_name.blank? ? e.event.title : e.custom_name
end
the SQL Rails generates will look something like this:
Calendar Load (0.2ms) SELECT "calendars".* FROM "calendars" WHERE "calendars"."id" = ? LIMIT 1
EventAlias Load (0.2ms) SELECT "event_aliases".* FROM "event_aliases" WHERE "event_aliases"."calendar_id" = 1
Event Load (0.2ms) SELECT "events".* FROM "events" WHERE "events"."id" IN (1, 2)
also if you want to clean it up a bit you can add a virtual field to the EventAlias
class EventAlias < ActiveRecord::Base
def name
custom_name || self.event.title
end
end
As long as you use the includes, the queries will be be the same.

How to filter by association count?

Let's say I have models that look like this:
class Foo < ActiveRecord::Base
has_many :bars, :through => :cakes
has_many :cakes
end
class Bar < ActiveRecord::Base
has_many :foos, :through => :cakes
has_many :cakes
end
class Cake < ActiveRecord::Base
belongs_to :foo
belongs_to :bar
end
How would I get all foos which had 10 or more bars (and therefore 10 or more cakes)?
Foo.all(:joins => :cakes,
:group => "cakes.foo_id",
:having => "count(cakes.bar_id) >= 10")
okay i tried the answer above, but had a problem.
for our purposes Father has_many :sons, ok?
i wanted to find Fathers that had zero sons.
the above did not work, because it produced an inner join... thereby filtering out all fathers without sons.
the following did work for me:
Father.includes(:sons).group('fathers.id').having( 'count(sons.id)=0' )
and it also happens to work for any other filter you'd require
Father.includes(:sons).group('fathers.id').having( 'count(sons.id)=3' )