PostgreSQL Rails has_many :through / collection_singular_ids / :order problem - ruby-on-rails-3

In the process of migrating to heroku, I have a weird error only when I use PostgreSQL (works fine in Mysql)
When I execute #user.county_ids I get the following error:
ActiveRecord::StatementInvalid: PGError: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
LINE 1: ...id" WHERE ("activity_areas".user_id = 1) ORDER BY counties.n...
The generated sql request is :
SELECT DISTINCT "activity_areas".county_id FROM "activity_areas" INNER JOIN "counties" ON "counties"."id" = "activity_areas"."county_id" WHERE ("activity_areas".user_id = 1) ORDER BY counties.name ASC
and finally the models:
class User < ActiveRecord::Base
has_many :activity_areas
has_many :counties, :through => :activity_areas
end
class ActivityArea < ActiveRecord::Base
belongs_to :user
belongs_to :county
default_scope joins(:county).order("counties.name ASC")
end
class County < ActiveRecord::Base
has_many :activity_areas
has_many :users, :through => :activity_areas
default_scope :order => 'name ASC'
end
Any idea on how to fix this?
Thanks,

When it comes to PostgreSQL, ensure that the elements in order by clause are also present in the select clause. MySQL is kinda lenient on this rule :)
Try changing the default scope in activity area model to
default_scope select('counties.name').joins(:county).order("counties.name ASC")
This should generate a SQL like
SELECT DISTINCT "activity_areas".county_id, counties.name FROM "activity_areas"...

Related

How do I chain multiple models to reduce the number of SQL queries

I'm trying to check whether a student has attempted an assigned test or not. I want to chain the relevant models to bring down the number of queries to just 1. The following are my models:
class Test < ActiveRecord::Base
has_many :assigns
has_many :attempts
belongs_to :topic
end
class Topic < ActiveRecord::Base
has_many :tests
has_many :attempts
has_many :assigns, through: :test
end
class Assign < ActiveRecord::Base
belongs_to :test
belongs_to :student
has_many :attempts
end
class Attempt < ActiveRecord::Base
belongs_to :test
belongs_to :topic
belongs_to :assign
belongs_to :student
end
I want to check if a particular student (id: 100) has attempted an assigned test or not, and also retrieve other details such as the topic name of the test. So far I have something like this:
ta = Assign.includes(:test => {:topic => :attempts})
This allows me to retrieve details such as the test_name, topic_name, when it was assigned etc. in a single query. How do I also include the Attempt records of student_id: 100 in the same query? With what I have now, when I retrieve the student's attempt details a brand new query is being generated.
What I want is something like the follwoing without having to touch the database again:
ta.test.attempts.where(student_id: 100)
How do I do all this with just one query?
Okay, since you want all kinds of information from all the joined tables, so you will have to join them up from the beginning.
Attempt.joins(:topic, :test, :assign)
Then you can filter it with the student_id
.where("attempts.student_id" => 100)
Finally, the fields you want
.select("attempts.id as attempt_id, tests.name as test_name, topics.name as topic_name, assigns.created_at as assigned_at")
In summary
Attempt
.joins(:topic, :test, :assign)
.where("attempts.student_id" => 100)
.select("attempts.id as attempt_id, tests.name as test_name, topics.name as topic_name, assigns.created_at as assigned_at")

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

How to write this ActiveRecord Query using Join instead of subquery in Rails 4

Consider the following:
class User < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :user #this user is the event owner
has_many :members
end
class Members < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
Now, I need to list all the members for which current_user is the owner. so I have come up with this:
#members = Member.where event_id: current_user.events
which produces the following query:
SELECT "members".* FROM "members" WHERE "members"."event_id" IN (SELECT "events"."id" FROM "events" WHERE "events"."user_id" = 1)
This works as expected but uses subqueries instead of JOIN. Does anyone know a better way to write this same query?
Add a has_many :through association to your User model:
class User < ActiveRecord::Base
has_many :events
has_many :members, :through => :events
end
Now you can query for all a user's members through the members association:
user.members
The SQL generated will look something like:
SELECT "members".* FROM "members" INNER JOIN "events" ON "members"."id" = "events"."member_id" WHERE "events"."user_id" = 1
Transformed to JOIN syntax (with table aliases to make it shorter and easier to read):
SELECT m.*
FROM events e
JOIN members m ON m.event_id = e.id
WHERE e.user_id = $1
I guess this will work.
Member.joins(:event).where("events.user_id = ?" , current_user.id)
You could do something like :
Member.joins(:event).where(events: {user_id: current_user.id})

Rails 3 has_one with join table

I have the following:
has_many :sports, :through => :user_sports
has_one :primary_sport, class_name: "UserSport", conditions: ["user_sports.primary = ?", true]
has_many :user_sports
When I run this in console:
athlete = Athlete.all.last
athlete.primary_sport
The record that is returned is the record from the join table instead of the record joining the sports table. Any way to return the actual sport from the join?
You might probably do something like this:
class UserSport < ActiveRecord::Base
has_many :athletes
has_many :sports
end
athlete = Athlete.all.last
athlete.primary_sport.sport
Didn't try it by myself, just check and see :)

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' )